/* ─── CSS Variables ─────────────────────────────────────────────────────────── */

:root {
  /* Default: dark theme (GitHub Dark-ish). Override via :root.light for light mode. */
  --bg-base:        #0d1117;
  --bg-surface:     #161b22;
  --bg-elevated:    #21262d;
  --bg-card:        #1c2128;
  --bg-hover:       #2d333b;
  --border:         #30363d;
  /* Border color reserved for glass-style capsules (conv-list-toggle,
     core pickers, search pill, wiki rail toggles, chips). Sits a notch
     lighter than --border in dark mode so the rim reads as "raised" even
     when --glass-glare-brightness is dialed low. Applied via the
     :root[data-style~="glass"] rule near the glass glare block. */
  --border-glass:   #4a5565;
  --text-primary:   #e6edf3;
  --text-secondary: #8b949e;
  /* Body reading-text base. Default 13px keeps today's look; the "Uniform
     14px text" toggle flips this to 14px on <html> inline (drives every
     font-size:var(--fs-body) site, including JS-inlined cssText). */
  --fs-body: 13px;
  /* Conversation list-row vertical padding (the row-height driver). Default 12px
     (the shorter height the user dialed in via the slider); the dev-GUI "Conv row
     height" slider still tunes it 8–20px. Selected cards use calc(this - 5px) and
     a +5px margin so total footprint is constant (no select jitter) at any value
     — see buildListItem in conversations.js. */
  --conv-row-pad-y: 12px;
  /* Facet avatar/tile grid — the global default for every facet grid (the
     Discover page AND the add-voice sheet). Value lifted from the Discover grid
     so its roomier, responsive sizing is the standard: auto-fill columns with a
     104px min so tiles keep their width and the count adapts to the container. */
  --facet-grid-cols: repeat(auto-fill, minmax(104px, 1fr));
  --facet-grid-gap: 16px 10px;
  /* In dark mode --text-muted aliases --text-secondary on purpose; the
     original #484f58 read as too dim against the dark surface. Light
     variants below keep their distinct muted shade. */
  --text-muted:     #8b949e;
  /* Accent blue is derived: --accent-blue-base is the raw hex (settable by
     the Accent color picker; dark/light mode each override below) and
     --accent-blue-sat-mult is a 0–1 multiplier driven by the "Blue
     saturation" slider in the Settings → Colors group. The derived
     --accent-blue value cascades to every consumer (.btn-primary, focus
     rings, sidebar accents, the iMessage-style Me bubble, etc.) so a
     single slider re-saturates the whole accent across the app. */
  --accent-blue-base: #1c95ff;
  --accent-blue-sat-mult: 1.0;
  --accent-blue: hsl(from var(--accent-blue-base) h calc(s * var(--accent-blue-sat-mult)) l);
  --accent-green:   #3fb950;
  --accent-orange:  #d29922;
  --accent-red:     #f85149;
  --accent-purple:  #bc8cff;
  --font-mono:      'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
  --font-ui:        Manrope, sans-serif;
  --fw-regular:     500;
  --fw-medium:      600;
  --fw-semibold:    700;
  --fw-bold:        800;
  /* Vertical-padding offset applied to nav rows (sidebar + facets nav).
     Driven by the dev GUI spacing slider. */
  --nav-spacing-offset: 0px;
  /* Conversation list font scale — separate desktop and mobile so the dev GUI
     can tune each independently. Inner rows use em so they multiply against
     this baseline. */
  --conv-list-scale-desktop: 1;
  --conv-list-scale-mobile:  1;
  --sidebar-width:  220px;
  --top-bar-height: 56px;
  /* Radius baseline doubled (formerly sm=8/md=12/lg=20/btn=10). The
     dev-GUI "Corner radius" slider scales these; 1.00× is the new
     default. Anyone with a stored hp:radius-scale from before the bump
     gets it halved once via the migration in index.html's pre-paint
     loader so the rendered look is preserved. */
  --radius-sm:      13px;
  --radius-md:      19px;
  --radius-lg:      32px;
  --radius-pill:    100px;
  --radius-btn:     16px;
  --transition:     150ms ease;
  /* ── Page-header tokens ─────────────────────────────────────────────────
     Single source of truth for the chrome at the top of every primary view
     (Conversations, Discover, Account, Help, and the wiki sub-modes Chat /
     Identity / Edit / Wiki/Sources). Edit these to retune all page headers
     in one place. The .page-header CSS class below references them. */
  --page-header-padding-y:    12px;
  --page-header-padding-x:    24px;
  --page-header-title-size:   1.1rem;
  /* Two height knobs:
       --page-header-height       — wiki center-panel inner headers
                                    (Identity / Chat / Wiki sub-views).
                                    Defaults to 56px so they stay flush
                                    with the wiki sidebar's New Facet wrap
                                    and the global top bar.
       --nav-page-header-height   — the 5 main-nav pages (Discover, Facets,
                                    Conversations, Account, Help). Tunable
                                    via the dev GUI slider so the chrome
                                    can be made shorter than the wiki rail. */
  --page-header-height:      56px;
  --nav-page-header-height:  46px;
  --page-header-mobile-height: 44px;
  /* Floor the page-header content row at the height of the Conversations
     search input (28px). Title-only views (Discover/Account/Help/etc.) get
     the extra space as balanced vertical padding (align-items:center) so
     every page header outer-renders at the same total height. */
  --page-header-content-min-height: 28px;
  /* ── Input-height token ─────────────────────────────────────────────────
     Single height for ALL typed-input affordances (text inputs, textareas,
     search bars, chat composers). 38px matches the chat-composer textarea
     min-height already used across conversations.js + wiki.js, and aligns
     with the Send button's padding (8px × 2 + ~22px content). Multiline
     fields use this as min-height and grow from there. The mobile rule
     (~line 1715) deliberately overrides to 44px for touch targets. */
  --input-height: 38px;

  /* Selected-facet border opacity multiplier. Range 0–200, default 40.
     Applied to the border color of an active facet row via color-mix:
       border-mix-percent = clamp(0%, intensity * 0.5%, 100%)
     so 0 → invisible border, 40 → 20% opacity (calibrated baseline —
     subtle rim that reads as selection without dominating), 100 → 50%
     opacity, 200 → 100% fully opaque. Width stays fixed at 1px — the
     slider only controls how "lit" the rim color reads. Decoupled from
     --facet-selected-bg-intensity so border and bg are dialed
     independently. See cloudflare/pages/components/wiki.js buildSubRow
     for the consumer. */
  --facet-selected-border-intensity: 0;

  /* Selected-facet background-tint intensity. Range 0–200, default 100.
     Multiplies the background color-mix percent applied to active rail
     rows: bg-mix-percent = clamp(0, intensity * 0.18%, 36%). 0 → fully
     transparent, 100 → 18% tint (baseline), 200 → 36% tint. Decoupled
     from --facet-selected-border-intensity so the user can dial border
     and bg independently. */
  --facet-selected-bg-intensity: 100;

  /* Glass-style edge-glare brightness multiplier. Range 0–200, default 20.
     Scales the ::after layer's opacity (the existing 0.9 in dark / 0.35 in
     light) by intensity/100, so 0 = invisible glare, 20 = the calibrated
     default (subtle rim), 100 = the unscaled base, 200 = doubled. Only
     takes effect under :root[data-style~="glass"] because that's the only
     place the ::after layer exists. See the "Glass — specular edge glare"
     block below for the consumer rules. */
  --glass-glare-brightness: 20;

  /* Nav logo (sidebar wordmark) size multiplier. Default 0.85 (= the dial's
     85% baseline). The dev-GUI "Nav logo size" slider (Settings ⚙ popover,
     50–150%) writes percent/100 into this var on documentElement;
     .sidebar-wordmark consumes it via transform:scale() from the leading edge. */
  --nav-logo-scale: 0.85;

  /* Incoming message-bubble border intensity. Range 0–200, default 100.
     Opacity multiplier (intensity * 1%) on the calibrated baseline color
       color-mix(in srgb, var(--accent-blue) 10%, var(--border))
     applied to all 4 edges of the 1px outline around incoming
     .conv-excerpt-bubble cells under the Liquid/Glass surface styles:
       0   → fully transparent border (top, bottom, left, right all gone)
       100 → 100% opaque baseline (matches the prior hardcode look)
       200 → clamped at 100% by the browser (no-op above 100, kept in the
             range for slider parity with the other intensity controls)
     Outgoing "Me" bubbles are unaffected (they're solid var(--accent-blue)).
     No effect under the Flat surface style — the inline identity-color
     left bar set by conversations.js is a separate attribute. */
  --incoming-bubble-border-intensity: 20;

  /* Identity-page card depth intensity. Range 0–200.
     Multiplier on the neutral drop shadow behind .identity-section-card and the
     .core-upload-cta that sits above the cards on the Core identity page. Purely
     CSS-internal now (no user control, no localStorage) — driven entirely by
     theme + hover. Dark mode is the default, so the base resting value is 0
     (no shadow at rest); light mode overrides to 200 in its :root.light block.
     Hover rules below ease this to a per-theme target. The value is
     @property-registered (see "Identity card depth" block) so it tweens as a
     number and the box-shadow follows frame-by-frame. */
  --identity-card-depth-intensity: 0;
}

:root.light {
  /* "Crisp" — default light variant. Near-black text, readable muted. */
  --bg-base:        #ffffff;
  --bg-surface:     #f5f7fa;
  --bg-elevated:    #ebeef3;
  --bg-card:        #ffffff;
  --bg-hover:       #e1e6ed;
  --border:         #b8bec8;
  --border-glass:   #a8b0c0;
  --text-primary:   #000000;
  --text-secondary: #1a2236;
  --text-muted:     #3a4255;
  --accent-blue-base: #1c95ff;
  --accent-green:   #15692e;
  --accent-orange:  #7d5400;
  --accent-red:     #b0151f;
  --accent-purple:  #6c3fc7;

  /* Identity card depth — light theme rests with a strong shadow (eases DOWN
     to 100 on hover; see the "Identity card depth" block). Dark mode rests at
     0 in :root above and eases IN to 40 on hover. */
  --identity-card-depth-intensity: 200;
}

/* Settings popover shell — anchored to the palette button. The .settings-popover
   modifier extends this base for the disclosure-grouped layout. */
.theme-variant-popover {
  position: absolute;
  top: calc(var(--top-bar-height) + 4px);
  right: 12px;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 6px 20px rgba(0,0,0,0.18);
  min-width: 200px;
}
.theme-variant-popover.hidden { display: none; }

/* Dev GUI popover — font + accent customization. Reuses .theme-variant-popover
   shell for positioning; controls below get their own styling. */
.dev-gui-section {
  padding: 8px 10px;
}
.dev-gui-section + .dev-gui-section {
  border-top: 1px solid var(--border);
}
.dev-gui-label {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  margin-bottom: 6px;
  font-family: var(--font-ui);
}
.dev-gui-select {
  width: 100%;
  padding: 6px 8px;
  font-size: 0.82rem;
  background: var(--bg-base);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-ui);
  cursor: pointer;
}
.dev-gui-select:focus {
  outline: none;
  border-color: var(--accent-blue);
}
.dev-gui-color-row {
  display: flex;
  gap: 8px;
  align-items: center;
}
.dev-gui-color {
  width: 36px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}
.dev-gui-range {
  width: 100%;
  cursor: pointer;
  accent-color: var(--accent-blue);
}
.dev-gui-reset {
  width: 100%;
  padding: 6px 10px;
  font-size: 0.78rem;
  background: var(--bg-base);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-ui);
  transition: background 120ms, color 120ms, border-color 120ms;
}
.dev-gui-reset:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--accent-blue);
}

/* Settings popover — disclosure-grouped layout with inner scroll wrapper.
   Overrides the parent .theme-variant-popover gap/padding so the scroll
   container + footer can sit edge-to-edge inside the popover shell. */
.settings-popover {
  gap: 0;
  padding: 0;
  overflow: hidden;
}
.settings-popover-scroll {
  max-height: min(82vh, 680px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.settings-popover-footer {
  border-top: 1px solid var(--border);
  padding: 10px 12px;
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}
.settings-popover-footer .dev-gui-reset {
  margin: 0;
  flex: 1;
}
.settings-group {
  border-bottom: 1px solid var(--border);
}
.settings-group:last-child { border-bottom: none; }
/* Hidden settings groups (e.g. dev-tuning controls suppressed on prod, leaving
   only the Theme light/dark toggle). Explicit so it beats the footer's
   display:flex; the .settings-group rule has no display of its own but this
   keeps both surfaces consistent. */
.settings-group[hidden], .settings-popover-footer[hidden] { display: none !important; }
.settings-group-summary {
  list-style: none;
  cursor: pointer;
  padding: 10px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.78rem;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-family: var(--font-ui);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.settings-group-summary::-webkit-details-marker { display: none; }
.settings-group-summary:hover { background: var(--bg-hover); }

/* Revert-sheet per-hunk disclosure rows — suppress the native triangle marker
   (we render our own rotating circular chevron). */
.revert-hunk-summary { list-style: none; }
.revert-hunk-summary::-webkit-details-marker { display: none; }
.revert-hunk-summary::marker { content: ''; }

/* Small ring spinner for the revert-sheet file headers while their AI summary
   loads. Reuses the global-search-spin keyframes. */
.revert-file-spinner {
  width: 13px;
  height: 13px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-blue);
  border-radius: 50%;
  animation: global-search-spin 0.7s linear infinite;
  flex-shrink: 0;
}
.settings-group-body {
  padding: 4px 0 8px;
}
.settings-group-chev {
  display: inline-block;
  font-size: 0.7rem;
  color: var(--text-muted);
  transition: transform 150ms;
  transform: rotate(180deg);
}
.settings-group[open] .settings-group-chev {
  transform: rotate(0deg);
}

/* ─── Reset & Base ───────────────────────────────────────────────────────────── */

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  font-size: 15px;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html, body {
  height: 100%;
  overflow: hidden;
  background: var(--bg-base);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-weight: var(--fw-medium);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Allow children to opt into safe-area insets via env(...) without
     re-declaring this on every block. */
}

/* On mobile, switch the root height from 100% (which resolves against the
   layout viewport including iOS Safari URL bar) to dynamic viewport height
   so the actual visible area is what we lay out against. dvh isn't
   universally supported on older browsers — they fall back to the 100%
   above. */
@supports (height: 100dvh) {
  html, body { height: 100dvh; }
}
body {
  font-size: var(--fs-body);
}

/* Form controls AND buttons don't inherit body weight — the UA default is 400
   (normal), which reads thin against the app's --fw-medium (600) body text.
   This was the "thin message input and many places like this", plus buttons
   that don't use the .btn class (e.g. .sidebar-feedback-btn "Give Feedback",
   .dev-gui-reset theme-settings toggles) — all unset, all fell to 400. Pin
   every text-entry surface and button to body weight so they match the UI.
   Class rules that set their own font-weight (.btn = --fw-medium, .btn-primary,
   etc.) still win on specificity, so this only lifts the unset ones —
   non-destructive. */
input, textarea, select, button {
  font-weight: var(--fw-medium);
}

a {
  color: var(--accent-blue);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* ─── Scrollbars ─────────────────────────────────────────────────────────────── */

::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--text-muted);
  border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--text-secondary);
}

/* ─── Utility ────────────────────────────────────────────────────────────────── */

.hidden {
  display: none !important;
}

/* ─── App Shell ──────────────────────────────────────────────────────────────── */

#app {
  display: flex;
  height: 100%;
  overflow: hidden;
}

/* ─── Sidebar ────────────────────────────────────────────────────────────────── */

#sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-width);
  height: 100vh;
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  z-index: 100;
  transition: width var(--transition);
}

/* Sidebar: team header */
/* Match the top-bar height so the sidebar→main divider is one straight
   horizontal line instead of a step. box-sizing keeps the 1px border inside
   the height budget. Inner items are centered vertically. */
.sidebar-header {
  height: var(--top-bar-height);
  box-sizing: border-box;
  padding: 0 12px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.sidebar-team-name {
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Wordmark image replacing the plain team-name text in the header. Fills the
   full header area — grows to take all available width and scales to the
   tallest size that fits the header height (object-fit:contain preserves the
   ~4:1 aspect and never crops). Horizontally centered in the header. */
.sidebar-wordmark {
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
  /* User-resizable nav logo. --nav-logo-scale defaults to 0.85 (the dial's 85%
     baseline). The dev-GUI "Nav logo size" slider (Settings ⚙ popover) overrides
     it via a documentElement inline var. transform-origin:center grows/shrinks
     the wordmark about its center (kept horizontally centered via
     object-position:center); transform never affects layout, so the flex row,
     the Beta pill and the core picker never reflow; the scale paints over the
     header box (overflow:visible) instead of resizing it. */
  transform: scale(var(--nav-logo-scale, 0.85));
  transform-origin: center;
}

.sidebar-team-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.sidebar-teammate-count {
  font-size: 10px;
  color: var(--text-muted);
}

/* ── Core picker ────────────────────────────────────────────────────────────
   Sits between the team brand row and the nav. Single button that opens a
   listbox of Cores. Switching a Core hits POST /api/cores/:id/activate, swaps
   the JWT, and hard-reloads so every view rebinds to the new Core's scope. */
.sidebar-core-picker-wrap {
  position: relative;
  padding: 10px 12px 4px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 6px;
}
.sidebar-core-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 5px 10px 5px 6px;
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
  transition: background var(--transition), border-color var(--transition);
}
.sidebar-core-picker:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong, var(--border));
}
.sidebar-core-picker[aria-expanded="true"] {
  background: var(--bg-hover);
}
/* Trailing ⋯ on the Core picker — opens the active Core's edit page.
   Sits OUTSIDE the picker capsule so its click doesn't toggle the picker
   menu. Sizing matches the picker's vertical footprint (~28px). */
/* Matches the facet rail rows' ⋯ "Manage" affordance exactly (built inline in
   wiki.js): bare muted glyph, no background/border chrome, color-only hover.
   (The mobile top-bar override below keeps a larger touch target.) */
.sidebar-core-edit-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 15px;
  line-height: 0;
  cursor: pointer;
  transition: color var(--transition);
}
.sidebar-core-edit-btn:hover {
  color: var(--text-primary);
}
.sidebar-core-picker-label {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* App-standard chevron — U+2303 ⌃ rotated via transform so stroke weight
   stays identical across orientations (matches the disclosure chevron in
   wiki.js). Closed = points down (rotate 180), open = points up (rotate 0).
   Animation is driven by the picker button's aria-expanded state, so the
   existing JS toggle is enough to flip the glyph. */
.sidebar-core-picker-chev {
  font-size: 1rem;
  line-height: 1;
  color: var(--text-secondary);
  flex-shrink: 0;
  display: inline-block;
  transform: rotate(180deg);
  transition: transform 120ms;
}
.sidebar-core-picker[aria-expanded="true"] .sidebar-core-picker-chev,
.top-bar-core-picker[aria-expanded="true"] .sidebar-core-picker-chev {
  transform: rotate(0deg);
}

.sidebar-core-menu {
  position: absolute;
  top: calc(100% - 2px);
  left: 12px;
  /* Widen beyond the picker capsule so the avatar + display name fit
     comfortably even for longer Core names. min-width sets the floor; the
     menu may still grow with the wrapper, and clamps to the viewport on
     narrow screens. */
  min-width: 240px;
  max-width: calc(100vw - 24px);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  /* Float above sidebars, top bar, command palette, global-search dropdown,
     and rail content. Matches the top-bar Core menu (.top-bar-core-menu)
     and the global-search-results dropdown so all top-level dropdowns
     stack at the same tier. Modal overlays (1500+) still sit on top. */
  z-index: 1000;
  font-family: var(--font-ui);
}
.sidebar-core-menu.hidden { display: none; }
.sidebar-core-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-family: inherit;
  font-size: var(--fs-body);
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
}
.sidebar-core-menu-item:hover { background: var(--bg-hover); }
.sidebar-core-menu-item.is-active { color: var(--accent-blue); font-weight: var(--fw-semibold); }
.sidebar-core-menu-item:disabled { opacity: 0.5; cursor: default; }
.sidebar-core-menu-check {
  width: 14px;
  text-align: center;
  color: var(--accent-blue);
  font-size: 11px;
  flex-shrink: 0;
}
.sidebar-core-menu-new .sidebar-core-menu-check {
  color: var(--text-secondary);
  font-weight: var(--fw-bold);
}
.sidebar-core-menu-name {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-core-menu-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 2px;
}


/* Sidebar: nav */
.sidebar-nav {
  /* Size to content so the decorative render (.sidebar-render, flex:1) takes
     the remaining vertical slack and fills the gap toward the footer. Still
     shrinkable + scrollable when the nav list is taller than the viewport. */
  flex: 0 1 auto;
  padding: 8px 0;
  overflow-y: auto;
}

.sidebar-group {
  margin-bottom: 4px;
}

.sidebar-group-label {
  padding: 6px 12px 3px;
  font-size: 10px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  /* Height matches the Core picker capsule directly above
     (.sidebar-core-picker: 18px avatar + 10px padding + 2px border = 30px),
     so the entire left rail reads as one consistent capsule column. The
     shared calc(6px + nudge) vertical padding still drives a ~26px
     content box; the explicit min-height pads it out to 30px without
     bumping the calc (which other rows like .sidebar-overflow-toggle
     copy from this pattern). */
  min-height: 30px;
  box-sizing: border-box;
  padding: calc(6px + var(--nav-spacing-offset)) 10px;
  /* Leading margin 18px puts the active dot ~6px from the sidebar's outer
     left edge — same column as the wiki rail's active-edit dot. Tighter
     than the previous 30px while still leaving the dot fully visible
     outside the capsule. */
  margin: 1px 6px 1px 18px;
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  cursor: pointer;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  transition: background var(--transition), color var(--transition);
  white-space: nowrap;
  overflow: visible;
  text-overflow: ellipsis;
  user-select: none;
  /* Transparent 1px baseline matches the facet rail rows
     (wiki.js buildSubRow/facet row) — same hairline language across the
     whole left rail, and the active state can paint its colored border
     without causing a 1px layout shift. */
  border: 1px solid transparent;
  background: none;
  text-align: left;
  width: calc(100% - 24px);
  font-family: var(--font-ui);
}

.sidebar-nav-item:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.sidebar-nav-item.active {
  /* Bg + border opacity track the same intensity sliders the facet rail uses
     (wiki.js buildSubRow / facet row). Multipliers match exactly:
       • bg     — clamp(0, --facet-selected-bg-intensity, 200) * 0.18%
       • border — clamp(0, --facet-selected-border-intensity, 200) * 0.5%
     Fallbacks match the :root defaults (100 / 40) so the main-nav active
     state and the facet rail's selected-row active state stay visually
     identical even when the user dials the sliders. */
  background: color-mix(
    in srgb,
    var(--accent-blue)
      calc(clamp(0, var(--facet-selected-bg-intensity, 100), 200) * 0.18%),
    transparent
  );
  border-color: color-mix(
    in srgb,
    var(--accent-blue)
      calc(clamp(0, var(--facet-selected-border-intensity, 40), 200) * 0.5%),
    transparent
  );
  color: var(--text-primary);
  font-weight: var(--fw-bold);
}

/* Active-state leading-edge indicator — small dot in the gutter to the
   left of the capsule, vertically centered. Mirrors the facets sidebar
   sub-row dot pattern (buildSubRow in wiki.js) so the whole app uses one
   active-indicator language. The bottom-tab-bar indicator below mirrors
   this dot for mobile. */
.sidebar-nav-item.active::before {
  content: '';
  position: absolute;
  left: -12px;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2.5px;
  background: var(--accent-blue);
  border-radius: 50%;
  pointer-events: none;
}

/* Sidebar overflow ("⋯ More") — same nav-item chrome as the regular items
   but anchors a popover with Logs / Onboarding etc. The wrapper is
   position:relative so the menu can absolute-position relative to the
   toggle on desktop. */
.sidebar-overflow-wrap {
  position: relative;
}
.sidebar-overflow-menu {
  position: absolute;
  left: 8px;
  top: calc(100% + 6px);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  min-width: 180px;
  /* When the menu list overflows it scrolls in place rather than running off
     the fixed sidebar. */
  max-height: calc(100vh - var(--top-bar-height) - 24px);
  overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
  /* z-index/transform tricks here couldn't beat the sidebar's WebGL canvas
     layer — the menu is now portaled to <body> on open (wireSidebarOverflow),
     so its stacking is resolved at the document root, above everything. */
}
.sidebar-overflow-menu.hidden {
  display: none;
}
.sidebar-overflow-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: calc(6px + var(--nav-spacing-offset)) 10px;
  border: none;
  background: transparent;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  text-align: left;
  cursor: pointer;
}
.sidebar-overflow-option:hover {
  background: var(--bg-hover);
}

/* ─── Conversation detail header ────────────────────────────────────────────
   Desktop: members + actions inline as a single row at top-bar-height, the
   bottom border aligning with sibling headers (sidebar header, list-col
   New Chat sticky, viewHeader).
   Mobile (max-width: 767px): the two child rows stack — members row keeps
   top-bar-height with its own bottom border; actions row sits beneath at
   ~36px with another bottom border. CSS-only switch so the JS DOM stays
   identical across breakpoints (renderDetail builds both rows always). */
.conv-detail-header {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  height: var(--top-bar-height);
  box-sizing: border-box;
  padding: 0 18px;
  border-bottom: 1px solid var(--border);
}
.conv-detail-header-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.conv-detail-header-row--members {
  flex: 1;
  flex-wrap: wrap;
}
.conv-detail-header-row--actions {
  flex-shrink: 0;
  justify-content: flex-end;
  margin-left: auto;
}

/* Sidebar-toggle icon (SF-symbol-style "sidebar.left"). Visible on every
   breakpoint — taps anchor to the leading edge of the actions row and
   toggle the conv-list-col via a class on .conv-body. Trailing action
   buttons (Distill / Delete / Merge) stay grouped on the trailing edge
   via Distill's `margin-left: auto` (set in JS).

   Visual treatment mirrors #wiki-rail-toggle (the hamburger that opens
   the facets drawer on the wiki page) — bordered elevated capsule, ☰
   glyph, primary text color. */
/* Optical centering — `‹›` glyphs render slightly below the bounding-box
   center because their visual mass sits at x-height. `padding-bottom: 2px`
   shrinks the inner content area, which (with align-items:center) shifts
   the glyph up 1px so its optical mid-line lines up with the adjacent
   label / title text instead of dropping below it. box-sizing:border-box
   keeps the outer 28×28 footprint unchanged (capsule-standard height). */
.conv-list-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0 0 2px;
  box-sizing: border-box;
  /* Full circle + visible border + raised surface so the toggle reads as
     a real button against the panel in both light and dark themes. The
     prior bg-hover-only fill disappeared into the surrounding surface. */
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-size: 17px;
  font-weight: var(--fw-semibold);
  line-height: 1;
  cursor: pointer;
  transition: background 120ms, border-color 120ms;
  flex-shrink: 0;
}
.conv-list-toggle:hover {
  background: var(--bg-hover);
  border-color: var(--text-muted);
}
/* Hides the conversation list column when the toggle is engaged. Applies
   on every breakpoint. !important is required — conv-list-col carries an
   inline `display: flex` set in JS, and inline styles beat class
   selectors without it. */
.conv-body.conv-list-hidden .conv-list-col {
  display: none !important;
}

/* Chain-mode silent rows. Two flavors:
   - .chain-silent-row--per-facet: one facet stayed silent (avatar + "X stayed
     silent" label, click "why?" expands the reason inline).
   - .chain-silent-row (no modifier): all-silent fallback (summary + per-facet
     reasons list). Both share the muted-text + rounded-tint look so the row
     visually belongs in the transcript at any --radius scale, instead of
     reading as a flat strip beside rounded peers (P2 #16). */
.chain-silent-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  color: var(--text-muted);
  font-size: 0.78rem;
  font-family: var(--font-ui);
  font-style: italic;
  position: relative;
}
.chain-silent-row--per-facet {
  flex-direction: row;
  align-items: center;
  gap: 8px;
}
.chain-silent-row__summary {
  color: var(--text-secondary);
  font-style: normal;
  line-height: 1.45;
}
.chain-silent-row__toggle {
  align-self: flex-start;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline dotted;
}
.chain-silent-row__details {
  display: none;
  margin: 4px 0 0;
  padding: 0 0 0 16px;
  list-style: disc;
  color: var(--text-secondary);
  font-style: normal;
  line-height: 1.5;
}
.chain-silent-row__details > li {
  margin: 2px 0;
}
.chain-silent-row__label {
  font-size: 0.78rem;
  color: var(--text-muted);
  font-style: italic;
}
.chain-silent-row__why {
  background: transparent;
  border: 0;
  padding: 0 4px;
  font-size: 0.72rem;
  color: var(--text-muted);
  cursor: pointer;
  text-decoration: underline dotted;
  font-family: var(--font-ui);
}
.chain-silent-row__reason {
  font-size: 0.75rem;
  color: var(--text-muted);
  font-style: italic;
  display: none;
}
.chain-silent-row__ts {
  margin-left: auto;
  font-size: 0.7rem;
  font-style: italic;
  color: var(--text-muted);
  font-family: var(--font-ui);
  flex-shrink: 0;
}

/* Conversation-list multi-participant avatar stack — desktop has plenty of
   horizontal room, so each avatar overlaps its predecessor by 7px. Mobile
   tightens the overlap to 11px so the "{N} members" label still has room
   beside the stack on a 320px-wide phone. */
.conv-list-avatar-stack > *:not(:first-child) {
  margin-left: -7px;
}
@media (max-width: 767px) {
  .conv-list-avatar-stack > *:not(:first-child) {
    margin-left: -11px;
  }
  /* Cap the visible avatar stack at 5 on phone — JS still renders up to
     7 (sorted by most-recent-activity by the worker), but the trailing
     ones hide so the count + chev capsule fits in a 320px row.
     `!important` is required because `buildAvatar` sets `display:
     inline-flex` as an inline style on each avatar wrap, and inline
     styles beat class selectors without it. */
  .conv-list-avatar-stack > *:nth-child(n+6) {
    display: none !important;
  }
  /* Drop the relative-time stamp on phone — list rows are too narrow to
     keep the timestamp + members capsule on the same line, and the topic
     row below is the higher-value information. */
  .conv-list-ts {
    display: none;
  }
}

@media (max-width: 767px) {
  /* Mobile shows the members row inline alongside the toggle + actions, so
     the avatar (single-participant) or participants capsule (multi) is
     visible at the top of every conversation. Lock to nowrap + min-width:0
     so long names truncate via ellipsis rather than wrapping to a second
     line and breaking the top-bar-height row. */
  .conv-detail-header-row--members {
    flex-wrap: nowrap;
    min-width: 0;
    overflow: hidden;
  }
}

.sidebar-nav-item .nav-icon {
  font-size: 15px;
  flex-shrink: 0;
  width: 18px;
  text-align: center;
}

.sidebar-nav-item .nav-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Sidebar: decorative auto-rotating renders (monogram + pyramid). The stack
   grows to fill the gap between the nav and the footer and centres the pair;
   each slot has a fixed height (the renders are framed to it). */
.sidebar-render-stack {
  flex: 1 1 auto;
  min-height: 160px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 12px 0;
  overflow: hidden;
}
.sidebar-render {
  width: 100%;
  height: 180px;          /* monogram slot */
}
/* three.js mounts its <canvas> into each slot. The canvas is transparent (alpha
   renderer, no scene.background) so there's no bounding box; pointer-events are
   toggled on the canvas by the raycast passthrough, so drags over a model orbit
   it while clicks elsewhere fall through. */
.sidebar-render-stack canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Sidebar: footer */
.sidebar-footer {
  flex-shrink: 0;
  padding: 10px 8px;
  border-top: 1px solid var(--border);
}

/* "Give Feedback" — sidebar entry, pinned to the very bottom of the sidebar,
   below the version footer. Text-only capsule (icon removed). */
.sidebar-feedback-btn {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(100% - 16px);
  margin: 0 8px 8px;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-size: 0.82rem;
  font-family: var(--font-ui);
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}
.sidebar-feedback-btn:hover {
  background: var(--bg-card);
  border-color: var(--accent-blue);
}

/* Mobile floating action button — the sidebar (and its feedback entry) is
   hidden on the mobile breakpoint, so this provides feedback from any page.
   Hidden on desktop; flipped on inside the max-width:767px block below. */
.feedback-fab {
  display: none;
  position: fixed;
  right: 16px;
  bottom: calc(var(--bottom-tab-bar-height, 56px) + env(safe-area-inset-bottom, 0px) + 12px);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--accent-blue);
  color: #fff;
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.32);
  z-index: 300;
}
/* The mobile floating feedback button was retired — feedback now lives in the
   bottom tab bar's More menu (data-action="feedback"). The .feedback-fab rule
   above stays display:none on every breakpoint. */

/* ─── Top Bar ────────────────────────────────────────────────────────────────── */

#top-bar {
  position: fixed;
  top: 0;
  left: var(--sidebar-width);
  right: 0;
  height: var(--top-bar-height);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  z-index: 90;
  gap: 12px;
  transition: left var(--transition);
}

.top-bar-left {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 1;
  min-width: 0;
}

.top-bar-logo {
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: -0.02em;
}

/* Mobile-only wordmark in the top-bar-left slot. Desktop hides it (the
   brand belongs on the login page / sidebar header on desktop, not the
   top bar). */
.top-bar-brand {
  display: none;
  font-family: var(--font-ui);
  font-weight: var(--fw-semibold);
  font-size: 0.95rem;
  color: var(--text-primary);
  letter-spacing: -0.01em;
  white-space: nowrap;
}
/* Mobile centered brand is the wordmark image (replaces the old "HiddenPrism"
   text). ~4:1 transparent PNG; height-capped to the top bar with width auto so
   it never crops. Only ever visible inside .top-bar-brand, which is mobile-only. */
.top-bar-brand-img {
  display: block;
  height: 22px;
  width: auto;
  object-fit: contain;
}

/* "Beta" pill on the trailing side of the wordmark. Accent-filled rounded
   capsule, uppercase, baseline-centered against the brand text. Lives inline
   inside .top-bar-brand so it follows the wordmark wherever it renders (the
   wordmark itself is mobile-only via the @media block below). */
.brand-beta {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--accent-blue);
  color: #fff;
  font-size: 0.6rem;
  font-weight: var(--fw-bold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.4;
  vertical-align: middle;
  position: relative;
  top: -1px;
}
/* In the sidebar header the pill is a flex sibling of the wordmark, so the
   header's own `gap` provides spacing — drop the inline margin to avoid
   double-spacing. (The top-bar-brand copy keeps margin-left: it's an inline
   child there, where flex gap doesn't apply.) */
.sidebar-header .brand-beta {
  margin-left: 0;
}
/* Beta badge in the top-bar right cluster (before the search glass). The
   cluster's own flex `gap` handles spacing, so drop the inline margin. */
.top-bar-beta {
  margin-left: 0;
  flex-shrink: 0;
}

/* Tiny muted version stamp tucked under the global search input on desktop.
   Absolute-positioned inside .top-bar-search (which is position:relative),
   right-aligned so it sits unobtrusively at the trailing edge. Mobile hides
   it entirely — mobile users see Version inside the bottom-tab "More" menu. */
/* Sidebar footer — version + build, sticky at the bottom of the desktop
   sidebar. Mobile shows this info inside the bottom-tab "More" menu instead
   (the desktop sidebar is hidden on mobile by an existing @media rule). */
.sidebar-footer {
  margin-top: auto;
  padding: 10px 14px 14px;
  font-size: 0.62rem;
  color: var(--text-muted);
  font-family: var(--font-ui);
  letter-spacing: 0.01em;
  border-top: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.top-bar-center {
  flex: 1;
  display: flex;
  justify-content: center;
  min-width: 0;
}

/* Page-title slot inside the top bar — leading side of the global search.
   Replaces the per-view .nav-page-header rows on the five main nav pages
   (Discover / Facets / Conversations / Account / Help). Populated by
   app.js navigate() from a VIEW_META map. Shrinks to allow the search
   field to expand into the free space; tagline ellipses then drops on
   narrow viewports (see the mobile rule below). */
.top-bar-page {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  flex: 0 1 auto;
  font-family: var(--font-ui);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
}
.top-bar-page:empty { display: none; }
.top-bar-page-title {
  font-size: 1rem;
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  flex-shrink: 0;
}
.top-bar-page-sep {
  color: var(--text-primary);
  opacity: 0.4;
  flex-shrink: 0;
}
.top-bar-page-sub {
  font-size: 0.8rem;
  color: var(--text-primary);
  opacity: 0.7;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.top-bar-breadcrumb {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-ui);
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}

.top-bar-breadcrumb-sep {
  color: var(--text-muted);
}

#top-bar-view {
  color: var(--text-primary);
}

.top-bar-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Global search — sits inside .top-bar-right as a 36px circular trigger
   between the wiki/page area and the theme toggle. Tapping the icon focuses
   the hidden input, which adds .is-search-expanded to #top-bar and the
   wrapper grows to fill the available row. Dropdown anchors to the input
   and overflows the top bar via absolute positioning. */
.top-bar-search {
  position: relative;
  flex: 0 0 36px;
  width: 36px;
  max-width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  transition: flex-basis 240ms ease, max-width 240ms ease, width 240ms ease;
}
.top-bar-search .pill-composer {
  width: 36px;
  height: 36px;
  min-height: 36px;
  border-radius: var(--radius-pill);
  padding: 0;
  overflow: hidden;
  position: relative;
  transition:
    width 240ms ease,
    border-radius 240ms ease,
    padding 240ms ease,
    background-color 240ms ease;
}
/* Magnifier icon — pure CSS via SVG mask so it inherits text color and
   animates with the wrapper. */
.top-bar-search .pill-composer::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 16px;
  height: 16px;
  background-color: var(--text-primary);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' d='M7 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0'/><path fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' d='M10 10l4 4'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' d='M7 7m-4 0a4 4 0 1 0 8 0a4 4 0 1 0 -8 0'/><path fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' d='M10 10l4 4'/></svg>");
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  transition: left 240ms ease, transform 240ms ease;
  pointer-events: none;
}
/* Hide the input text + placeholder while collapsed; the wrapper acts as the
   tap target (focus still fires because the input is the only focusable
   child). */
#global-search-input {
  width: 100%;
  box-sizing: border-box;
  color: transparent;
  caret-color: transparent;
  padding: 0 0 0 36px;
  border: 0;
  background: transparent;
  height: 100%;
  cursor: pointer;
}
.top-bar-search #global-search-input::placeholder { color: transparent; }

/* Expanded — JS toggles .is-search-expanded on focus. Wrapper grows to a
   comfortable search width on desktop / fills the row on mobile so the
   results dropdown has room to breathe. */
#top-bar.is-search-expanded .top-bar-search {
  /* Fill all available width in the row when active. Other right-cluster
     siblings (theme toggle) keep their natural space; the search takes
     the rest. Mobile @media block hides the left cluster too so the
     search really spans the whole bar there. */
  flex: 1 1 auto;
  width: auto;
  max-width: none;
}
/* Yield the row to the search on expansion: drop the page-title slot
   (the center spacer) and let the right cluster grow into the freed
   space. Without this the .top-bar-center { flex: 1 } rule keeps
   hoarding the spare width and the search can't actually widen.
   The .top-bar-left cluster stays visible on desktop so the user can
   still see/click the sidebar trigger; only .top-bar-center collapses. */
#top-bar.is-search-expanded .top-bar-center {
  display: none;
}
#top-bar.is-search-expanded .top-bar-right {
  flex: 1 1 auto;
  min-width: 0;
}
#top-bar.is-search-expanded .top-bar-search .pill-composer {
  width: 100%;
  /* Capsule (not rounded-rect) — matches the collapsed circle's pill radius so
     the expand animation reads as one continuous capsule on both breakpoints. */
  border-radius: var(--radius-pill);
  padding: 0;
}
#top-bar.is-search-expanded .top-bar-search .pill-composer::before {
  left: 12px;
  transform: translate(0, -50%);
}
#top-bar.is-search-expanded .top-bar-search #global-search-input {
  color: var(--text-primary);
  caret-color: var(--text-primary);
  padding: 0 12px 0 36px;
  cursor: text;
}
#top-bar.is-search-expanded .top-bar-search #global-search-input::placeholder {
  color: var(--text-secondary);
}
.global-search-results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 10px 28px rgba(0,0,0,0.22);
  z-index: 1000;
  font-family: var(--font-ui);
}
.global-search-section {
  padding: 6px 0;
}
.global-search-section + .global-search-section {
  border-top: 1px solid var(--border);
}
.global-search-section-header {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
  padding: 6px 12px 4px;
}
.global-search-result {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px;
  cursor: pointer;
  border: none;
  background: transparent;
  text-align: left;
  width: 100%;
  color: var(--text-primary);
  font-family: var(--font-ui);
}
.global-search-result:hover,
.global-search-result.is-active {
  background: var(--bg-hover);
}
.global-search-result-title {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.global-search-result-meta {
  font-size: 11px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Mail-style — quiet group header, leading colored dot, right-aligned meta. */
.gsr-group {
  padding: 4px 0 6px;
}
.gsr-group + .gsr-group {
  border-top: 1px solid var(--border);
  margin-top: 2px;
}
.gsr-group-header {
  font-size: 11px;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  padding: 8px 14px 4px;
}
.global-search-result.gsr-row {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
}
.gsr-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-muted);
  margin: 0 2px;
}
.gsr-row--conv .gsr-dot { background: var(--accent-blue); }
.gsr-row--wiki .gsr-dot { background: var(--accent-green); }
.gsr-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.gsr-title {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gsr-snippet {
  font-size: 11.5px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.4;
}
.gsr-meta {
  flex: none;
  margin-left: 10px;
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
}
.global-search-empty {
  padding: 14px 14px 16px;
  font-size: 12px;
  color: var(--text-secondary);
}
.global-search-result.is-more {
  opacity: 0.7;
}
.global-search-more-toggle {
  display: block;
  width: 100%;
  background: transparent;
  border: none;
  text-align: left;
  cursor: pointer;
  padding: 6px 12px;
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: 11px;
  font-style: italic;
}
.global-search-more-toggle:hover {
  color: var(--text-primary);
  background: var(--bg-hover);
}
.global-search-loading {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 14px 16px;
  font-size: 12px;
  color: var(--text-secondary);
  font-family: var(--font-ui);
}
.global-search-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-blue);
  border-radius: 50%;
  animation: global-search-spin 0.7s linear infinite;
  flex-shrink: 0;
}
@keyframes global-search-spin {
  to { transform: rotate(360deg); }
}

/* Default: mobile Core picker hidden on desktop. The @media (max-width: 767px)
   block immediately below flips this to display:flex on mobile. Rule MUST sit
   ABOVE the @media block so the cascade resolves correctly — placing it after
   the @media block (as it was originally) made source-order beat the matching
   media rule on mobile too, hiding the picker. */
.top-bar-core-picker-mobile { display: none; }

/* Mobile — let the search bar fill the available row (flex:1 + min-width:0
   from the desktop rule already does the work; we just don't cap it here).
   The is-search-expanded path below still hides the side clusters when the
   user is actively searching so the dropdown gets the whole row. */
@media (max-width: 767px) {
  /* iOS Safari auto-zooms any input with computed font-size < 16px on focus.
     Force 16px on mobile to keep the viewport stable when the user taps in. */
  #global-search-input {
    font-size: 16px;
  }
  .top-bar-team-name { display: none; }
  /* Brand wordmark — shown on mobile, absolutely centered inside the fixed
     #top-bar so the leading (Core picker) and trailing (search, settings)
     clusters can flow naturally with the flex layout without fighting the
     centered glyph. pointer-events:none lets taps fall through to whatever
     control sits behind it. */
  .top-bar-brand {
    display: flex;
    align-items: center;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    pointer-events: none;
    user-select: none;
    z-index: 1;
  }
  /* When the search field expands to fill the row, the brand would sit on
     top of the input — hide it for the duration of the expanded state. */
  #top-bar.is-search-expanded .top-bar-brand { display: none; }
  /* The mobile brand wordmark (above) already carries its own Beta badge, so
     hide the desktop top-bar Beta here to avoid showing it twice. */
  .top-bar-beta { display: none; }
  /* Mobile-only Core picker mount. The desktop sidebar copy is hidden
     here because the sidebar itself is hidden on mobile by the existing
     layout rule. */
  .top-bar-core-picker-mobile { display: flex; align-items: center; }

  /* Mobile-expanded search: hide side clusters and let the input fill the
     row so the results dropdown isn't crowded by other top-bar chrome. The
     collapsed-circle baseline + the desktop-expanded width are defined at
     the universal selectors above; this block only specializes the
     expanded variant for narrow viewports. */
  #top-bar.is-search-expanded .top-bar-left,
  #top-bar.is-search-expanded .top-bar-right > *:not(.top-bar-search) {
    display: none;
  }
  #top-bar.is-search-expanded .top-bar-search {
    flex: 1 1 auto;
    max-width: none;
  }

  /* The whole top-bar page slot is dropped on narrow viewports — the
     active row/section is already implied by the bottom-tab-bar selection
     and the in-view chrome, so the title + tagline are noise eating
     horizontal room the search and Core picker need. */
  .top-bar-page-title,
  .top-bar-page-sep,
  .top-bar-page-sub {
    display: none;
  }
  /* Mobile Core picker collapses to avatar + chevron — the Core name
     repeats info already visible in the bottom-tab-bar / page chrome and
     just steals top-bar width. The dropdown still lists names normally. */
  .top-bar-core-picker .sidebar-core-picker-label {
    display: none;
  }
}

/* Mobile Core-picker styling. Reuses most of the desktop picker visuals,
   just dropped into the top bar instead of the sidebar header. The
   default-display rule lives ABOVE the @media block (see the mobile
   picker selector inside the @media block at the top of styles.css) so
   the @media-scoped 'display: flex' wins via source order on mobile. */
.top-bar-core-picker-mobile .top-bar-core-picker-wrap {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
}
.top-bar-core-picker {
  display: flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  /* Locked to 36px to match the global search button's collapsed circle
     height — same visual control affordance in the top bar. */
  height: 36px;
  box-sizing: border-box;
  padding: 0 10px 0 6px;
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  cursor: pointer;
  max-width: 180px;
}
.top-bar-core-picker:hover { background: var(--bg-hover); }
.top-bar-core-picker[aria-expanded="true"] { background: var(--bg-hover); }
/* Mobile ⋯ edit button next to the picker — sized to match the picker /
   search button (36×36) for a consistent top-bar control row. The base
   .sidebar-core-edit-btn rule keeps its smaller desktop sizing. */
.top-bar-core-picker-mobile .sidebar-core-edit-btn {
  width: 36px;
  height: 36px;
  font-size: 16px;
}
.top-bar-core-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 240px;
  max-width: calc(100vw - 24px);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  padding: 6px;
  z-index: 1000;
}

.top-bar-team-name {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
}

.theme-toggle-btn {
  /* Background + border mirror .pill-composer so the gear reads as the
     same chrome family as the global search trigger sitting next to it
     in the top bar. */
  background: var(--bg-base);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  font-size: 16px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
  padding: 0;
}
.theme-toggle-btn:hover {
  background: var(--bg-hover);
  color: var(--accent-blue);
  border-color: var(--accent-blue);
}

/* Conversation list column — sets the em baseline for all rows. Inline
   font-size:Xem inside buildListItem multiplies against this. */
.conv-list-col {
  font-size: calc(15px * var(--conv-list-scale-desktop, 1));
}

/* Conversation list rows — selection indicator is a capsule pill in a
   reserved leading gutter. The row's inline `padding:12px 14px 12px 20px`
   (set in buildListItem) carves out the gutter; unselected rows keep the
   indent so cards don't jitter horizontally when the active row changes.
   The pill itself is drawn by ::before on the --active / --pinned-profile
   modifiers. Width/position are stable across states — only the color
   (transparent → accent) transitions. */
.conv-list-item {
  position: relative;
  /* Uniform inset on EVERY row (not just the selected one) so the selected
     rounded card has breathing room on all sides without the row shifting
     when it becomes active (no horizontal jitter — see the gutter note above). */
  margin: 3px 8px;
}
.conv-list-item::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 18%;
  bottom: 18%;
  width: 3px;
  border-radius: var(--radius-pill);
  background: transparent;
  transform: scaleY(0.2);
  transform-origin: center;
  transition: background var(--transition), transform 280ms cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
.conv-list-item--active::before,
.conv-list-item--pinned-profile::before {
  background: var(--accent-blue);
  transform: scaleY(1);
}
/* Selected/pinned rows get a rounded "card" behind their content. Radius is a
   fixed 9px (the 0.55× radius-scale value the user dialed in) — intentionally
   tighter than the message bubbles (--radius-sm) now; the two no longer match.
   The fill is set inline (bg-elevated) / by the liquid override. The row's own
   edge border-bottom is removed (inline `none` in buildListItem; the liquid
   override only ever set its colour, which a 0-width border ignores) so the
   divider no longer hugs the rounded card edge; instead the ::after below
   redraws it as a hairline in the gap beneath the card. */
.conv-list-item--active,
.conv-list-item--pinned-profile {
  border-radius: 9px;
  /* 8px margin on ALL sides so the card is evenly inset from its container
     (equal padding "out"). The larger vertical margin (vs the base 3px) also
     pairs with the shorter inline padding (12 vs 17) so the card is shorter
     while the row's total footprint stays ~constant (flex items don't collapse
     margins) — neighbours don't shift on select. */
  margin: 8px;
}
/* The card carries NO border-bottom (see buildListItem) so its own divider is
   this ::after. It sits 11px below the card — the SAME distance the divider
   above the card sits (the previous row's border-bottom = prev margin 3 + card
   margin 8 = 11px). That places the card equidistant between the two divider
   lines, so the outside spacing reads equal top↔bottom. */
.conv-list-item--active::after,
.conv-list-item--pinned-profile::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -11px;
  height: 1px;
  background: color-mix(in srgb, var(--border) 45%, transparent);
  pointer-events: none;
}
/* One-shot expand on user click. The row is re-created already-active on select
   (the list re-renders), so a CSS transition won't fire — a keyframe animation
   plays on paint instead. Only the freshly-clicked row gets --just-selected
   (see buildListItem), so background re-renders don't replay it. Subtle on
   purpose: starts at scaleY(0.35) (not 0) and settles quickly, so it reads as a
   gentle unfurl from the center rather than a flashy grow. Starts at scaleY(0.55)
   + opacity 0.85 (not 0) so it's a barely-there settle, not a grow. */
@keyframes conv-bar-expand {
  from { transform: scaleY(0.55); opacity: 0.85; }
  to   { transform: scaleY(1); opacity: 1; }
}
.conv-list-item--just-selected::before {
  animation: conv-bar-expand 180ms cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
  .conv-list-item--just-selected::before { animation: none; }
}

/* Inline rename ✎ in the conversation list row. Always visible (a pencil reads
   as "edit" on its own, and hover-gating would hide it entirely on touch where
   there is no hover). Kept faint at rest so a column of rows doesn't get noisy;
   brightens on row hover/active and on direct hover/focus. */
.conv-rename-pencil {
  position: relative; /* anchor the touch hit-area pseudo below */
  opacity: 0.5;
  transition: opacity 120ms, color 120ms;
}
.conv-list-item:hover .conv-rename-pencil,
.conv-list-item--active .conv-rename-pencil {
  opacity: 0.75;
}
/* Inline "Name this chat" rename input — neutral resting border; the unified
   input[type="text"]:focus rule (specificity 0,2,1) wins over this 0,1,0 rule
   to supply the accent border + glow on focus. outline:none kills the native
   browser focus ring that otherwise drew OUTSIDE our border — the doubled
   ring the user saw. (.input/.textarea get outline:none from their base rules;
   this bare input never did.) */
.conv-rename-input {
  border: 1px solid var(--border);
  outline: none;
}
.conv-rename-pencil:hover,
.conv-rename-pencil:focus-visible {
  opacity: 1;
  color: var(--accent-blue);
}
/* Touch only: extend the tappable area to ~44px around the glyph without
   growing the visible pencil or shifting row layout (the pseudo is absolutely
   positioned, so it takes no flow space). Desktop keeps the precise small
   target so the halo can't intercept nearby clicks. */
@media (pointer: coarse) {
  .conv-rename-pencil::after {
    content: '';
    position: absolute;
    inset: -14px;
  }
}

/* ─── Main Content ───────────────────────────────────────────────────────────── */

#main-content {
  margin-left: var(--sidebar-width);
  padding-top: var(--top-bar-height);
  height: 100vh;
  flex: 1;
  overflow: hidden;
}

/* ─── Bottom Tab Bar (mobile only) ─────────────────────────────────────────────
   Hidden on desktop; the @media (max-width: 767px) block flips it on. Lives
   here at top-level so the desktop default is `display: none`. */
#bottom-tab-bar {
  display: none;
}

/* ─── Liquid DOM — frosted-glass overlays ─────────────────────────────────────
   CSS backdrop-filter can FROST (blur the backdrop) but cannot refract/displace
   it — Chromium doesn't apply SVG reference filters to backdrop-filter — so this
   is clean frosted glass, not a displacement effect. Frost is only visible where
   a surface overlays contrasting CONTENT, so we target floating overlays that do
   (Settings popover, global-search dropdown, Core/More nav menus, bottom tab bar
   + its More menu, generic modals, and per-page sheets/popovers).

   NOT the fixed #sidebar rail: only the flat app background sits behind it (frost
   invisible), and backdrop-filter on #sidebar would make it the containing block
   for the Core menu's position:fixed (sidebar.js), clipping the menu.

   ── TUNING ──────────────────────────────────────────────────────────────────
   These tokens are the GLOBAL dials. Change a value here and it propagates to
   every Liquid DOM surface below (and the per-page rules in the Discover/Wiki/etc
   sections that also use them). Lower fill % = more see-through. The light theme
   reassigns --ld-fill / --ld-fill-content so the dark/light switch is automatic —
   individual surfaces no longer need their own light-mode rules. Surfaces that
   deliberately differ (e.g. the Discover hero sheet's deeper blur) override a
   single token locally inside their own rule. */
/* ── TUNING (global dials) ────────────────────────────────────────────────────
   color-mix is evaluated HERE with LITERAL percentages and the RESULT is exposed
   as a plain colour token; surfaces just reference the colour. (A var() in the
   *percentage* slot of color-mix isn't reliably supported — it silently dropped
   the fill and fell back to the opaque base bg, making surfaces MORE opaque.)
   Lower the % below = more see-through. The light block reassigns the colours so
   the dark/light switch is automatic. */
/* --glass-overlay-opacity is a USER DIAL (0–360, default 180 — the slider
   midpoint). It scales the fill % of every frosted overlay/sheet/modal/menu
   surface below, so a higher value makes overlays MORE opaque (less
   see-through). 180 = today's calibrated look. The scrim veil is intentionally
   NOT scaled by this dial — it's the
   page-darkening backdrop, not a "window background", so we leave --ld-scrim-bg
   on a literal percentage. The base per-surface percentages (menu 40, content 12)
   are multiplied by the dial via calc(); the %-typed calc result is supported in
   the color-mix percentage slot (the prior bug was a BARE var() there, not calc). */
:root[data-style~="liquiddom"] {
  --glass-overlay-opacity: 180;
  --ld-menu-bg:    color-mix(in srgb, var(--bg-card) calc(40% * var(--glass-overlay-opacity) / 100), transparent);  /* menus / ld-glass */
  --ld-content-bg: color-mix(in srgb, var(--bg-card) calc(12% * var(--glass-overlay-opacity) / 100), transparent);  /* large content panels (conversations/wiki) — mostly blurred backdrop. Modal sheets use --ld-menu-bg instead. */
  --ld-scrim-bg:   color-mix(in srgb, var(--bg-base) 12%, transparent);  /* modal / overlay veil — NOT scaled by the dial */
  --ld-rim:        color-mix(in srgb, var(--accent-blue) 18%, var(--border));
  --ld-blur: 22px;
  --ld-saturate: 185%;
  --ld-scrim-blur: 13px;
}
:root.light[data-style~="liquiddom"] {
  --ld-menu-bg:    color-mix(in srgb, var(--bg-card) calc(30% * var(--glass-overlay-opacity) / 100), transparent);
  --ld-content-bg: color-mix(in srgb, var(--bg-card) calc(12% * var(--glass-overlay-opacity) / 100), transparent);
  --ld-scrim-bg:   color-mix(in srgb, var(--bg-base) 12%, transparent);
}

:root[data-style~="liquiddom"] .theme-variant-popover,
:root[data-style~="liquiddom"] .global-search-results,
:root[data-style~="liquiddom"] .sidebar-core-menu,
:root[data-style~="liquiddom"] .top-bar-core-menu,
:root[data-style~="liquiddom"] .sidebar-overflow-menu,
:root[data-style~="liquiddom"] .bottom-tab-more-menu {
  background: var(--ld-menu-bg);
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
}
:root[data-style~="liquiddom"] #bottom-tab-bar {
  /* bg-surface base (not bg-card) is intentional for the rail. */
  background: color-mix(in srgb, var(--bg-surface) 40%, transparent);
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border-top: 1px solid var(--ld-rim);
  box-shadow:
    0 -1px 4px rgba(0, 0, 0, 0.10),
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 8%, transparent);
}

/* Generic modal (#modal-* and .modal-* — both forms kept in sync). Lighten the
   scrim + blur it so the page reads THROUGH it (giving the box real content to
   frost), then frost the box. Cascades to most app modals: visitor-add-voice,
   New Core, account share/danger, billing, import/export, avatar crop. */
:root[data-style~="liquiddom"] #modal-overlay,
:root[data-style~="liquiddom"] .modal-overlay {
  background: var(--ld-scrim-bg);
  -webkit-backdrop-filter: blur(var(--ld-scrim-blur)) saturate(140%);
  backdrop-filter: blur(var(--ld-scrim-blur)) saturate(140%);
}
:root[data-style~="liquiddom"] #modal-box,
:root[data-style~="liquiddom"] .modal-box {
  /* Modal sheets (generic app modal + the Add-a-voice sheet) read as floating
     windows, so they use the denser menu/theme-popover fill (--ld-menu-bg, ~58%
     at the default dial) rather than the near-transparent content fill — the
     12% content fill is meant for large blurred-backdrop panels, and looked
     glassily see-through on these small sheets. */
  background: var(--ld-menu-bg);
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
}

/* Liquid DOM — content sheets/popovers (Conversations + Wiki). Styled inline in
   JS, so !important overrides the inline background. We do NOT touch the sheets'
   *-overlay scrims (permanent inset:0 click-catchers). These use the SAME fill
   as the menus/theme selector (--ld-menu-bg) so they read identically. */
:root[data-style~="liquiddom"] #prompt-sheet,
:root[data-style~="liquiddom"] #resonance-popover-panel,
:root[data-style~="liquiddom"] #revert-sheet,
:root[data-style~="liquiddom"] #version-history-sheet {
  background: var(--ld-menu-bg) !important;
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate)) !important;
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate)) !important;
  border-color: var(--ld-rim) !important;
}

/* Liquid DOM — generic opt-in glass for inline-styled floating menus/popovers.
   Add class="ld-glass" to any JS-built floating panel to opt it into the frost;
   !important beats the inline background. Used by conversations.js list/message/
   avatar dropdowns; reuse on other pages' inline-built popovers. */
:root[data-style~="liquiddom"] .ld-glass {
  background: var(--ld-menu-bg) !important;
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate)) !important;
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate)) !important;
  border-color: var(--ld-rim) !important;
}

/* ─── Views ──────────────────────────────────────────────────────────────────── */

.view {
  display: none;
  flex-direction: column;
  height: 100%;
  overflow-y: auto;
  animation: view-fade-in 180ms ease;
}

.view.active {
  display: flex;
}

@keyframes view-fade-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ─── Layout: View Sections ──────────────────────────────────────────────────── */

/* ── .page-header ──────────────────────────────────────────────────────────
   Canonical page-level header. One row, vertically centered, with a divider.
   Tuned via the --page-header-* tokens in :root above. Used at the top of
   every primary view (Conversations, Discover, Account, Help) and every
   wiki center-panel sub-mode (Chat, Identity, Edit, Sources). The mobile
   @media block further down forces this same selector to a 40px row inline
   with the wiki hamburger toggle. */
/* Fixed height read from --page-header-height — the single knob for the
   chrome row across the 5 main-nav pages. Defaults match --top-bar-height
   so other internal alignments (sidebar header, conversation thread header)
   stay flush by default; the user can decouple via the dev GUI if they want
   a different page-header height. Vertical padding is zero — flex centering
   handles content alignment. Mobile override applies in the media query. */
.page-header {
  height: var(--page-header-height);
  box-sizing: border-box;
  padding: 0 var(--page-header-padding-x);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.page-header h1,
.page-header h2,
.page-header h3 {
  margin: 0;
  font-size: var(--page-header-title-size);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-family: var(--font-ui);
}

/* Height-only override applied to the 5 main-nav pages (Discover, Facets,
   Conversations, Account, Help). They keep .page-header for the shared
   padding/border/flex base and add .nav-page-header to opt into the
   independently-tunable shorter chrome height. */
.nav-page-header {
  height: var(--nav-page-header-height);
}

/* Legacy alias — old call sites used .view-header. Kept so any straggler
   references still get the canonical styling. New code should use .page-header. */
.view-header {
  padding: var(--page-header-padding-y) var(--page-header-padding-x);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.view-header h1,
.view-header h2 {
  font-size: var(--page-header-title-size);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  margin: 0;
}

.view-header p {
  font-size: 12px;
  color: var(--text-secondary);
}

.view-body {
  flex: 1;
  padding: 20px 24px;
  overflow-y: auto;
}

/* ─── Cards ──────────────────────────────────────────────────────────────────── */

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px;
  transition: border-color var(--transition);
}

.card:hover {
  border-color: var(--text-muted);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  gap: 8px;
}

.card-header .card-name {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-body {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 12px;
}

.card-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.card--empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48px 24px;
  text-align: center;
  color: var(--text-muted);
  font-size: 12px;
  border-style: dashed;
}

.card--empty .empty-icon {
  font-size: 31px;
  margin-bottom: 12px;
  opacity: 0.5;
}

/* ─── Buttons ────────────────────────────────────────────────────────────────── */

/* Capsule standard — mirrors the wiki sub-row template (buildSubRow in
   components/wiki.js). Renders ~28px tall on default --nav-spacing-offset,
   matching the sidebar Identity/Wiki sub-rows and the sticky New / New Facet
   CTAs. Every full-size .btn now reads in the same height family. Use
   .btn-sm / .btn-xs for deliberately smaller variants. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: calc(6px + var(--nav-spacing-offset)) 10px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  font-family: var(--font-ui);
  cursor: pointer;
  border: 1px solid transparent;
  transition: background var(--transition), color var(--transition),
              border-color var(--transition), opacity var(--transition);
  line-height: 1.2;
  min-height: 0;
  white-space: nowrap;
  user-select: none;
  letter-spacing: -0.01em;
}

.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  outline: none;
  box-shadow: none;
}

.btn-primary {
  background: var(--accent-blue);
  color: #fff;
  border-color: var(--accent-blue);
}

.btn-primary:hover:not(:disabled) {
  background: #2D94FF;
  border-color: #2D94FF;
}

/* Legacy compact modifier. With the new capsule-standard baseline on .btn,
   .btn-compact is now visually equivalent to base .btn — the class is kept
   so existing markup doesn't break, but it no longer alters sizing. */
.btn-compact {
  /* no-op — inherits capsule-standard from .btn */
}

/* Full-width modifier — combine with .btn for sticky list-header "New"
   (conversations) and sidebar "New Facet" (wiki rail) rows. Pure width
   modifier; sizing comes from the capsule-standard .btn baseline. */
.btn-block {
  width: 100%;
  min-height: 0;
  box-sizing: border-box;
}

.btn-danger {
  background: var(--accent-red);
  color: #fff;
  border-color: var(--accent-red);
}

.btn-danger:hover:not(:disabled) {
  background: #ff6b6b;
  border-color: #ff6b6b;
}

.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border-color: transparent;
}

.btn-ghost:hover:not(:disabled) {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border);
}

.btn-sm {
  padding: 6px 13px;
  font-size: 11px;
  border-radius: var(--radius-pill);
}

.btn-xs {
  padding: 4px 10px;
  font-size: 10px;
  border-radius: var(--radius-pill);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
  font-size: var(--fs-body);
}

.icon-btn:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border);
}

/* ─── Terminal ───────────────────────────────────────────────────────────────── */

.terminal {
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px 16px;
  font-family: var(--font-mono);
  font-size: 12px;
  line-height: 1.6;
  overflow-x: auto;
}

.terminal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 11px;
}

.terminal-line {
  color: var(--text-primary);
  white-space: pre-wrap;
  word-break: break-all;
}

.terminal-line + .terminal-line {
  margin-top: 2px;
}

.terminal-line--system {
  color: var(--text-muted);
}

.terminal-line--error {
  color: var(--accent-red);
}

/* ─── Discussion turn cards ─────────────────────────────────────────────── */

.discussion-turn {
  margin: 8px 0;
  border: 1px solid var(--border);
  border-left: 3px solid var(--turn-color, var(--accent-blue));
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  overflow: hidden;
}

.discussion-turn__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-card);
}

.discussion-turn__dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--turn-color, var(--accent-blue));
  flex-shrink: 0;
}

.discussion-turn__name {
  font-size: 0.82rem;
  font-weight: var(--fw-bold);
  color: var(--turn-color, var(--accent-blue));
  flex: 1;
  font-family: var(--font-ui);
}

.discussion-turn__time {
  font-size: 0.72rem;
  color: var(--text-muted);
  font-family: var(--font-ui);
}

.discussion-turn__body {
  padding: 10px 14px;
  font-size: 0.82rem;
  line-height: 1.65;
  color: var(--text-primary);
  font-family: var(--font-ui);
  white-space: pre-wrap;
}

.terminal-line--success {
  color: var(--accent-green);
}

/* ─── Badges ─────────────────────────────────────────────────────────────────── */

.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  font-size: 10px;
  font-weight: var(--fw-medium);
  font-family: var(--font-ui);
  line-height: 1.4;
  white-space: nowrap;
  letter-spacing: 0.02em;
}

.badge--leader {
  background: rgba(210, 153, 34, 0.15);
  color: var(--accent-orange);
  border: 1px solid rgba(210, 153, 34, 0.3);
}

.badge--env-dev {
  background: rgba(88, 166, 255, 0.12);
  color: var(--accent-blue);
  border: 1px solid rgba(88, 166, 255, 0.25);
}

.badge--env-prod {
  background: rgba(63, 185, 80, 0.12);
  color: var(--accent-green);
  border: 1px solid rgba(63, 185, 80, 0.25);
}

.badge--facet {
  background: rgba(188, 140, 255, 0.12);
  color: var(--accent-purple);
  border: 1px solid rgba(188, 140, 255, 0.25);
}

/* ─── Forms ──────────────────────────────────────────────────────────────────── */

.form-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
}

.form-group label {
  font-size: 11px;
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.input {
  width: 100%;
  padding: 8px 14px;
  min-height: var(--input-height);
  box-sizing: border-box;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 12px;
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.input:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.12);
}

/* Unified focus glow for every text-entry surface — the canonical look the
   user expects when they click into ANY input. Buttons, segmented toggles
   styled as buttons, color pickers, and contentEditable surfaces with their
   own focus handlers are intentionally excluded.
   Note: inline `border:` declarations on textareas (chat composers) win over
   `border-color` here due to inline-style precedence — those inputs had
   their inline border pulled out so this rule wins. */
input[type="text"]:focus,
input[type="search"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="url"]:focus,
input[type="tel"]:focus,
input:not([type]):focus,
textarea:focus,
.input:focus,
.textarea:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.12);
}

input:disabled,
textarea:disabled,
.input:disabled,
.textarea:disabled {
  opacity: 0.55;
  box-shadow: none;
}

.input::placeholder {
  color: var(--text-primary);
  opacity: 0.5;
}

.textarea {
  width: 100%;
  padding: 10px 14px;
  box-sizing: border-box;
  background: var(--bg-base);
  border: 1px solid var(--border);
  /* Matches conversation message-bubble radius (--radius-sm = 16px) so a
     multi-line input reads as the same rounded-rect shape as the bubble it
     produces. At single-line height (~36px) the 16px corners still read as
     a near-pill; once expanded past ~48px (≈2 lines) it's a clean rounded
     rect with the bubble-matching corner. */
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 12px;
  outline: none;
  resize: vertical;
  /* Default multiline floor; per-instance overrides downshift via inline
     min-height (e.g. blurb 52px, description 64px) but never below
     var(--input-height). */
  min-height: var(--input-height);
  line-height: 1.6;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.textarea:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.12);
}

/* Shared autosize behavior — see components/autosize.js. The helper writes
   data-autosize-shape="single" (1 line of content) or "multi" (2-3 lines)
   onto the "paint target" (the wrapper that carries the visible border, or
   the textarea itself when there is no wrapper). At 4+ lines the textarea
   height locks and the helper enables internal scrolling.
   The CSS only has to pick the right border-radius per shape. */
/* Specificity note: combined with .autosize-paint so these rules beat the
   per-surface .composer-textarea / .pill-composer / .onb-vignette-* border-
   radius declarations. Painted elements opt in by being passed to the helper. */
/* Multi-line radius matches the conversation message-bubble radius
   (--radius-sm) — see textEl border-radius in conversations.js. The visual
   contract: a multi-line input reads as the same rounded-rect shape as the
   message bubble it produces. */
.autosize-paint[data-autosize-shape='single'] { border-radius: var(--radius-pill, 999px); }
.autosize-paint[data-autosize-shape='multi']  { border-radius: var(--radius-sm, 16px); }
/* Smooth the corner morph as content crosses the 1↔2 line threshold. */
.autosize-paint { transition: border-radius 140ms ease, border-color 120ms; }
/* Textareas governed by the helper should never show the OS resize handle —
   the user resizes by typing, not by dragging. */
textarea.autosize-input { resize: none; }

/* Chat-style composer textarea — used in conversations.js and wiki.js
   Core/facet chat. Owns the full sizing/typography spec so callers only
   need `flex:1` (which is layout-context-specific). The unified
   textarea:focus rule above paints the blue border + glow on focus. */
.composer-textarea {
  background: var(--bg-base);
  border: 1px solid var(--border);
  /* See .textarea note: matches the conversation message-bubble radius
     (--radius-sm). 16px reads as near-pill at single-line height (~36px tall),
     and as a clean rounded rect once it grows past ~2 lines. */
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
  padding: 8px 10px;
  font-size: 0.85rem;
  font-family: var(--font-ui);
  resize: none;
  overflow-y: hidden;
  min-height: var(--input-height);
  max-height: 160px;
  line-height: 1.45;
}

/* Wiki inline inputs (ingest paste, filter, query) — same role as
   .composer-textarea but for the wiki view's smaller controls.
   inputCss() above writes the cssText; this class supplies the look. */
.wiki-inline-input {
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  outline: none;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.textarea::placeholder {
  color: var(--text-primary);
  opacity: 0.5;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-family: var(--font-ui);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
  user-select: none;
}

.chip:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}

.chip--selected {
  background: rgba(88, 166, 255, 0.12);
  border-color: var(--accent-blue);
  color: var(--accent-blue);
}

/* ─── Command Palette ────────────────────────────────────────────────────────── */

.command-palette {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 500;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 80px;
  background: rgba(1, 4, 9, 0.75);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  animation: palette-fade-in 120ms ease;
}

@keyframes palette-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.command-palette-inner {
  width: 100%;
  max-width: 560px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  animation: palette-slide-in 150ms ease;
}

@keyframes palette-slide-in {
  from {
    opacity: 0;
    transform: translateY(-12px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.command-palette-input {
  width: 100%;
  padding: 14px 16px;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  outline: none;
  caret-color: var(--accent-blue);
}

.command-palette-input::placeholder {
  color: var(--text-primary);
  opacity: 0.5;
}

.command-palette-list {
  max-height: 360px;
  overflow-y: auto;
  padding: 4px 0;
}

.command-palette-empty {
  padding: 24px 16px;
  text-align: center;
  color: var(--text-muted);
  font-size: 12px;
}

.command-palette-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  cursor: pointer;
  transition: background var(--transition);
  user-select: none;
}

.command-palette-item:hover,
.command-palette-item.selected {
  background: var(--bg-hover);
}

.command-palette-item.selected {
  background: var(--bg-hover);
}

.command-palette-item .palette-item-name {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-primary);
  flex-shrink: 0;
}

.command-palette-item .palette-item-desc {
  font-size: 11px;
  color: var(--text-muted);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.command-palette-item .palette-item-group {
  flex-shrink: 0;
}

.command-palette-footer {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
}

.palette-shortcut {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  color: var(--text-muted);
}

.palette-shortcut kbd {
  font-family: var(--font-mono);
  font-size: 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  color: var(--text-secondary);
}

/* ─── Modal ──────────────────────────────────────────────────────────────────── */

#modal-overlay {
  position: fixed;
  inset: 0;
  /* Modals are always the topmost UI by convention — they're spawned from
     within other surfaces (top bar, onboarding step 3a facet picker,
     import/export, sidebar version popup, etc.) and must overlay whatever
     spawned them. Sits above #onboarding-overlay (410) and every lower
     layer (top bar 90/95/96, mobile-tab 96, slide-out 100, palette 1000
     which is the highest non-modal layer — kept below so command palette
     never traps focus from a modal it spawned). */
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(1, 4, 9, 0.75);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  /* Backdrop fade timed to the shared `sheet-in` box entrance (see the Sheet
     entrance block further down). */
  animation: palette-fade-in 270ms ease;
}

#modal-overlay.hidden {
  display: none !important;
}

#modal-box {
  width: 100%;
  max-width: 480px;
  /* Never let a tall modal overflow the viewport (content was getting clipped
     with no way to scroll — e.g. the New Facet suggestions). Cap + scroll. */
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
  /* Entrance is the shared `sheet-in` (blur+opacity+rise) defined in the Sheet
     entrance block further down — applied via the grouped selector there. */
}

.modal-overlay {
  position: fixed;
  inset: 0;
  /* Class-form mirror of #modal-overlay above. Kept in sync — both layers
     must sit at 500 so modals stack above the onboarding overlay (410). */
  z-index: 500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(1, 4, 9, 0.75);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.modal-box {
  width: 100%;
  max-width: 480px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
}

.modal-title {
  font-size: 15px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  margin-bottom: 12px;
}

.modal-body {
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 20px;
  line-height: 1.6;
}

.modal-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* ─── Visitor multi-voice (Phase 1) ────────────────────────────────────────
   "+ Add a voice" affordance + the modal list rows opened by
   components/visitor-add-voice.js. The visitor participant chip itself is
   the shared buildParticipantsCapsule helper — see conversations.js. */
.add-voice-button { height:28px; box-sizing:border-box; padding:0 12px; border-radius:var(--radius-pill); border:1px dashed var(--border); background:transparent; color:var(--text-secondary); font-size:var(--fs-body); font-weight:var(--fw-medium); font-family:var(--font-ui); line-height:1.2; cursor:pointer; transition:color 120ms,border-color 120ms; flex-shrink:0; white-space:nowrap; }
.add-voice-button:hover { color:var(--accent-blue); border-color:var(--accent-blue); }
/* Dynamic label — three nested spans, CSS picks one per viewport width.
   Default: "+ Add member". ≤500px: "+ Add". ≤380px: "+". Keeps the button
   from clipping its sibling Delete on narrow mobile widths. */
.add-voice-button__label--short,
.add-voice-button__label--icon { display: none; }
@media (max-width: 500px) {
  .add-voice-button__label--full { display: none; }
  .add-voice-button__label--short { display: inline; }
  .add-voice-button { padding: 0 10px; }
}
@media (max-width: 380px) {
  .add-voice-button__label--short { display: none; }
  .add-voice-button__label--icon { display: inline; }
  .add-voice-button { padding: 0 8px; min-width: 28px; }
}
/* Smaller variant scoped to the discover sheet — keeps the conversations
   view's larger button untouched. */
.discover-overlay-panel .add-voice-button { height:22px; padding:0 9px; border-radius:var(--radius-pill); font-size:11.5px; }
.add-voice-list { display:grid; grid-template-columns:var(--facet-grid-cols); gap:var(--facet-grid-gap); max-height:60vh; overflow-y:auto; padding:2px; }
.add-voice-card { position:relative; padding:4px 2px; border:0; background:transparent; overflow:visible; cursor:pointer; display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:6px; transition:transform 120ms, opacity 120ms; font-family:inherit; color:inherit; text-align:center; }
.add-voice-card:hover:not(:disabled):not(.is-added) .add-voice-card-avatar { transform:translateY(-1px); box-shadow:0 4px 14px color-mix(in srgb, var(--accent-blue) 24%, transparent); }
.add-voice-card:disabled { cursor:default; }
.add-voice-card.is-adding { opacity:0.55; pointer-events:none; }
.add-voice-card.is-added .add-voice-card-avatar { box-shadow:0 0 0 2px var(--accent-green); }
.add-voice-card-avatar { width:56px; height:56px; border-radius:50%; overflow:hidden; flex:none; transition:transform 120ms, box-shadow 120ms; }
.add-voice-card-name { width:100%; font-size:0.72rem; font-weight:var(--fw-semibold); color:var(--text-primary); line-height:1.15; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.add-voice-card-check { position:absolute; top:-2px; right:calc(50% - 32px); width:18px; height:18px; border-radius:50%; background:var(--accent-green); color:#fff; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:var(--fw-semibold); line-height:1; box-shadow:0 1px 3px rgba(0,0,0,0.25); pointer-events:none; border:2px solid var(--bg-elevated); }
.add-voice-card-error { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; padding:8px; font-size:0.72rem; font-weight:var(--fw-semibold); color:#fff; background:color-mix(in srgb, var(--accent-red, #f85149) 80%, transparent); text-align:center; pointer-events:none; }
.add-voice-divider { grid-column:1 / -1; font-size:0.72rem; text-transform:uppercase; letter-spacing:0.06em; color:var(--text-muted); padding:10px 4px 4px; border-top:1px solid var(--border); margin-top:6px; }
/* No mobile column override needed: --facet-grid-cols uses auto-fill, so the
   column count adapts to the sheet width on its own (same as the Discover grid). */

/* ─── Sheet entrance ───────────────────────────────────────────────────────
   Blur + opacity + a slight rise. Shared by every centered sheet:
     • the add-voice sheet (components/visitor-add-voice.js → `.av-sheet`),
     • the shared centered modal `#modal-box` (upload-to-Core, New Facet,
       import/export, confirm dialogs, wiki modals…),
     • the discover detail panel (`.discover-overlay-panel`).
   Right-anchored drawers (prompt / revert / feedback / version-history) are
   deliberately NOT included — they slide in from the right via translateX, and
   a centered rise would fight that anchoring.

   The blur ramp also does double duty for the add-voice sheet, whose member
   list loads async: it smears the spinner→grid reflow rather than letting it
   read as a jump (the `.av-sheet-body` reservation below handles the size). */
@keyframes sheet-in {
  from { opacity: 0; filter: blur(12px); transform: translateY(8px) scale(0.97); }
  to   { opacity: 1; filter: blur(0);    transform: translateY(0)    scale(1); }
}
.modal-box.av-sheet,
#modal-box,
.discover-overlay-panel {
  animation: sheet-in 440ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, filter, transform;
}
.modal-overlay.av-sheet-overlay,
.discover-overlay { animation: palette-fade-in 340ms ease; }

/* Widen ONLY the modals that host the add-member picker (new-chat header +
   the "Bring in voices" fork) to match the existing-thread .av-sheet width —
   without ballooning every other centered modal that rides #modal-box.
   `#modal-box:has(#id)` carries two id selectors, so it outranks the base
   `#modal-box { max-width:480px }` regardless of source order. */
#modal-box:has(#newchat-member-picker),
#modal-box:has(#bring-voices-picker) {
  max-width: min(680px, calc(100vw - 32px));
}

/* Reserved body height ≈ search row + section divider + ~3 card rows. The
   common populated sheet is at least this tall, so the box opens at ~its final
   size; larger lists grow upward modestly under the blur entrance. Flex column
   so the loading spinner (flex:1) centers in the reserved space.
   Add-voice sheet only — other sheets render synchronously and don't jump. */
.av-sheet-body { min-height: 332px; display: flex; flex-direction: column; }

/* One-shot cross-fade for the spinner→grid swap (JS adds this only on the
   first populated paint). */
@keyframes av-list-fade-in { from { opacity: 0; } to { opacity: 1; } }
.add-voice-list.av-list-fade-in { animation: av-list-fade-in 300ms ease both; }

@media (prefers-reduced-motion: reduce) {
  .modal-box.av-sheet,
  #modal-box,
  .discover-overlay-panel,
  .modal-overlay.av-sheet-overlay,
  .discover-overlay,
  .add-voice-list.av-list-fade-in { animation: none; }
}

/* ─── Onboarding overlay ──────────────────────────────────────────────────────
   Centered card overlay used for first-run + manual onboarding flow.
   Mounted lazily by components/onboarding.js the first time start() is
   called; toggled visible via the .open class. The card is medium-sized
   (~520px on desktop) and adapts to full-width minus gutters on mobile.
   z-index 410 sits above every base UI layer (top bar 90/95/96, slide-out
   100) but BELOW #modal-overlay (500). Modals spawned from within
   onboarding (e.g. Step 3a facet preview, error prompts) must overlay the
   onboarding overlay, not hide behind it. */
#onboarding-overlay {
  position: fixed;
  inset: 0;
  z-index: 410;
  display: none;
  /* `safe center` centers when the card fits but falls back to start-alignment
     if the card is ever taller than the viewport — without it, a too-tall card
     gets centered with its TOP pushed off-screen (header/nav clipped, no way to
     scroll up). overflow-y:auto then lets the user reach it. */
  align-items: safe center;
  justify-content: center;
  overflow-y: auto;
  background: rgba(1, 4, 9, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  padding: 16px;
  box-sizing: border-box;
}
#onboarding-overlay.open {
  display: flex;
  animation: palette-fade-in 140ms ease;
}

.onboarding-card {
  width: 100%;
  max-width: 520px;
  max-height: calc(100vh - 32px);
  display: flex;
  flex-direction: column;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
  font-family: var(--font-ui);
  overflow: hidden;
  animation: palette-slide-in 160ms ease;
}

.onboarding-header {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px 0;
  flex-shrink: 0; /* never let the nav/header row shrink — it was clipping the back/close buttons under vertical pressure */
}
.onboarding-step-indicator {
  font-size: 0.74rem;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  flex-shrink: 0;
}
.onboarding-close {
  margin-left: auto;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-hover);
  color: var(--text-primary);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.onboarding-close:hover {
  background: var(--bg-elevated);
  border-color: var(--text-muted);
}
/* Compact capsule indicator shown when onboarding is in demo mode. Lives in
   the header (absolutely centered) so it shares a row with the back button,
   step indicator, and close ✕. Stays out of the way while still flagging
   that nothing is being saved. */
.onb-demo-pill {
  background: color-mix(in srgb, var(--accent-blue) 14%, var(--bg-elevated));
  color: var(--accent-blue);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 35%, transparent);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 0.7rem;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: default;
  white-space: nowrap;
}
.onb-demo-pill--header {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  pointer-events: auto;
}

/* Leading-edge Back button in the header — circular always-visible chrome
   (the hover state is the default) so the affordance reads as a tappable
   target without needing a hover discovery pass. Pairs with the trailing
   ✕ close on the other edge. */
.onboarding-back {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-hover);
  color: var(--text-primary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.onboarding-back:hover {
  background: var(--bg-elevated);
  border-color: var(--text-muted);
}

.onboarding-progress {
  height: 3px;
  background: var(--border);
  margin: 0 18px;
  border-radius: 2px;
  overflow: hidden;
  flex-shrink: 0;
}
.onboarding-progress-fill {
  height: 100%;
  background: var(--accent-blue);
  transition: width 200ms ease;
}

.onboarding-body {
  padding: 4px 28px 20px;
  flex: 1;
  overflow-y: auto;
  min-height: 120px;
}
.onboarding-title {
  margin: 0 0 18px;
  font-size: 1.1rem;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.onboarding-content {
  font-size: 0.9rem;
  color: var(--text-primary);
  line-height: 1.55;
}

.onboarding-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 18px 28px 22px;
  border-top: 1px solid var(--border);
}
.onboarding-prev:disabled {
  opacity: 0.4;
  cursor: default;
}

@media (max-width: 767px) {
  .onboarding-card {
    max-width: 100%;
  }
}

/* ─── Tags ───────────────────────────────────────────────────────────────────── */

.tag {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  font-size: 10px;
  font-weight: var(--fw-medium);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

/* ─── Divider ────────────────────────────────────────────────────────────────── */

.divider {
  height: 1px;
  background: var(--border);
  margin: 12px 0;
  border: none;
}

/* ─── Loading ────────────────────────────────────────────────────────────────── */

.loading {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-muted);
  font-size: 12px;
}

.loading::before {
  content: '';
  width: 14px;
  height: 14px;
  border: 2px solid var(--border);
  border-top-color: var(--accent-blue);
  border-radius: 50%;
  animation: spin 600ms linear infinite;
  flex-shrink: 0;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ─── Responsive: Sidebar Collapse ──────────────────────────────────────────── */

/* Mobile-only drawer affordances rendered by wiki.js renderLayout — hidden on
   desktop so the wiki view's 3-column layout stays byte-identical. The mobile
   @media block below switches them back on. */
#wiki-rail-toggle,
#wiki-rail-backdrop {
  display: none;
}

/* Mobile-only conversations drawer backdrop. Hidden on desktop. Inside the
   @media block below it becomes visible whenever the drawer is open
   (.conv-body is missing .conv-list-hidden) on mobile. */
#conv-list-backdrop { display: none; }

/* Wiki rail facet rows — kept indented (26px padding-left, set inline in
   components/wiki.js buildChatSidebar) so facets visually nest under Core.
   The tree-guide line + L-cap that previously drew a vertical hairline
   through the indent gutter was removed; the indent alone carries the
   parent/child relationship now. The .wiki-rail-facet-row /
   .wiki-rail-facet-row--last classes are still applied by buildChatSidebar
   in case we want to reintroduce the guide later, but no rules target them
   here. */

/* Desktop sidebar toggle — sits on the leading edge of per-mode panel
   headers (Identity title row, Wiki/Sources search bar) and toggles the
   chat rail's visibility. Mobile keeps the absolute-positioned
   #wiki-rail-toggle drawer hamburger that floats over the center panel,
   so this inline control hides on small viewports. Same chrome-stripped
   chevron treatment as `.conv-list-toggle` so all sidebar toggles read
   the same way across the app. */
.wiki-rail-collapse-btn {
  width: 28px;
  height: 28px;
  padding: 0 0 2px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Visible circular button — see .conv-list-toggle for the rationale. */
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-size: 17px;
  font-weight: var(--fw-semibold);
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 120ms, border-color 120ms;
}
.wiki-rail-collapse-btn:hover {
  background: var(--bg-hover);
  border-color: var(--text-muted);
}
/* Hides the chat rail when the desktop toggle is engaged.
   Scoped to desktop only: on mobile the rail is an off-canvas drawer
   governed by .mobile-rail-open, and renderLayout unconditionally adds
   .rail-collapsed when state.railCollapsedDesktop is true OR
   shouldHideFacetsRail() is true. Without this scope, a stale
   railCollapsedDesktop from a prior desktop session would leave the
   panel display:none on mobile and the hamburger toggle would be inert. */
@media (min-width: 768px) {
  #wiki-wrapper.rail-collapsed #wiki-left-panel {
    display: none !important;
  }
  /* Global Core wiki page: drop the "Wiki" label + page count from the search
     bar's leading slot on desktop (they duplicate the top-bar page title). */
  .wiki-root-head-hide-desktop {
    display: none !important;
  }
  /* Core Identity page: drop the in-panel "Identity" header row on desktop —
     its label + version badge are now carried by the top-bar title. */
  .wiki-identity-roothead-hide-desktop {
    display: none !important;
  }
}

@media (max-width: 767px) {
  :root {
    --sidebar-width: 0px;
    /* Mobile-only bump: a slightly taller wiki/identity page header gives the
       title and hamburger more breathing room and matches the Conversations
       page header proportions on phone screens. */
    --page-header-mobile-height: 52px;
    /* Height of the mobile bottom tab bar (excluding safe-area inset). Used to
       reserve bottom padding on full-height views so chat composers / lists
       don't sit underneath the bar. */
    --bottom-tab-bar-height: 56px;
  }

  /* Use dynamic viewport height + iOS safe-area insets so the top bar isn't
     hidden behind the notch / dynamic island and the chat input isn't hidden
     behind the home-indicator or the Safari URL bar. */
  #app {
    /* Use the SMALL viewport height (svh) rather than the dynamic height (dvh).
       dvh changes as Safari collapses/expands its chrome, and each change forces
       a layout reflow that — combined with the safe-area inset — could blink the
       fixed top bar out of view. svh is the stable smallest height, so the
       layout doesn't reflow when the toolbars collapse. */
    height: 100svh;
  }

  /* The left icon sidebar is replaced on mobile by the bottom tab bar below.
     `display: none !important` beats any inline `display:flex` left over from
     desktop layout. */
  #sidebar {
    display: none !important;
  }

  /* Mobile keeps the original 48px bar height — desktop bumped the var
     to 56px to fit the taller global search, but mobile shrinks the
     search input back to 30px so the original bar height still fits.
     --page-header-height drops to 48px on mobile to track --top-bar-height,
     keeping wiki inner headers flush with the sidebar/New Facet wrap.
     --nav-page-header-height stays at the same 40px the desktop slider uses;
     change here if the main-nav chrome should be taller on mobile. */
  :root {
    --top-bar-height: 48px;
    --page-header-height: 48px;
    --nav-page-header-height: 40px;
  }

  #top-bar {
    left: 0;
    top: 0;
    /* Offset the bar below the notch / status-bar safe area with a
       non-collapsible margin instead of padding. When mobile Safari collapses
       its chrome (scroll-to-fullscreen, fullscreen video, home-screen launch),
       env(safe-area-inset-top) can momentarily resolve to 0 during the 100dvh
       reflow; a padding-based offset folded into the bar's own height made the
       bar visually vanish. A fixed height + margin-top keeps the bar painted at
       a stable position regardless of inset collapse. */
    height: var(--top-bar-height);
    padding-top: 0;
    margin-top: env(safe-area-inset-top, 0px);
    padding-right: calc(16px + env(safe-area-inset-right));
  }

  /* Solid backdrop strip filling the status-bar safe-area region above the bar
     so it never shows through as transparent when the inset is present. Uses
     the same surface variable as #top-bar's own background. */
  #top-bar::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: env(safe-area-inset-top, 0px);
    background: var(--bg-surface);
    z-index: 90;
  }

  #main-content {
    margin-left: 0;
    /* Match the taller top bar so views start below it. */
    padding-top: calc(var(--top-bar-height) + env(safe-area-inset-top));
    padding-right: env(safe-area-inset-right);
    /* Reserve room at the bottom for the fixed tab bar + iPhone home indicator
       so views (chat composer, lists, page bottoms) aren't hidden underneath. */
    padding-bottom: calc(var(--bottom-tab-bar-height) + env(safe-area-inset-bottom));
    /* Use the SMALL viewport height (svh) — the stable smallest height that
       does NOT change as Safari collapses its chrome. 100vh wrongly includes the
       URL bar; 100dvh tracks the live chrome but reflows on every collapse,
       which (with the safe-area inset) flickered the fixed top bar. svh holds
       steady; content still scrolls inside #main-content as needed. */
    height: 100svh;
  }

  /* Bottom tab bar — fixed to the bottom of the viewport, full width.
     z-index sits above the top bar (which is 90) and below the modal overlay. */
  #bottom-tab-bar {
    display: flex;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 95;
    background: var(--bg-surface);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
    /* Subtle shadow so it visually floats above content scrolling underneath. */
    box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.08);
    /* Horizontal scroll so adding more tabs (Identity + Wiki now share this
       row alongside Discover / Conversations / Facets / More) doesn't squeeze
       the existing ones below the touch-target floor. Each tab has a fixed
       min-width below; the bar scrolls when total width exceeds the viewport.
       overscroll-behavior:contain so a horizontal flick doesn't bleed up
       into the page's vertical scroll. */
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;            /* Firefox */
    gap: 4px;
  }
  #bottom-tab-bar::-webkit-scrollbar { display: none; }  /* WebKit */

  .bottom-tab-bar-item {
    position: relative;
    /* Content-width tabs: each tab sizes to its own label and a uniform gap
       (4px on the parent) plus symmetric 12px horizontal padding makes the
       space between every adjacent pair identical regardless of label length
       (Conversations vs Wiki). With 6+ tabs the total exceeds the viewport, so
       overflow-x:auto on the parent makes the row swipeable. Fixed-width tabs
       (flex:0 0 112px) centered short labels in wide buttons, making the
       visual gaps between label texts look uneven. */
    flex: 0 0 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 56px;
    padding: calc(6px + var(--nav-spacing-offset)) 12px;
    border: none;
    background: none;
    color: var(--text-primary);
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: var(--fw-medium);
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--transition);
  }

  .bottom-tab-bar-item:active {
    background: var(--bg-hover);
  }

  /* Active state mirrors the sidebar's active-nav-item palette so the visual
     language is consistent across breakpoints. */
  .bottom-tab-bar-item.active {
    color: var(--text-primary);
    font-weight: var(--fw-bold);
  }

  /* Active-state indicator — small dot centered below the label, mirroring
     the desktop sidebar dot indicator and the facets sub-row dot pattern
     (buildSubRow in wiki.js) so the whole app uses one active-indicator
     language. Anchored to ::after on the label so it tracks the label
     center horizontally; the negative bottom offset pushes it into the
     button's bottom padding without crowding the text. The label normally
     has `overflow: hidden` (for text-overflow ellipsis) which would clip
     the ::after — override to `visible` on the active label since the
     bottom tab labels are short by design and don't need truncation. */
  .bottom-tab-bar-item.active .bottom-tab-bar-label {
    position: relative;
    overflow: visible;
  }
  .bottom-tab-bar-item.active .bottom-tab-bar-label::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -16px;
    width: 5px;
    height: 5px;
    margin-left: -2.5px;
    background: var(--accent-blue);
    border-radius: 50%;
    pointer-events: none;
  }

  .bottom-tab-bar-icon {
    font-size: 20px;
    line-height: 1;
  }

  .bottom-tab-bar-label {
    font-size: 14px;
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }

  /* Overflow popover anchored to the ☰ "More" tab. The bottom tab bar lives
     at the screen bottom, so the menu opens upward from above the bar. The
     bar itself is z-index 95; this sits one above so it floats over content
     but still under the modal overlay. */
  .bottom-tab-more-menu {
    position: fixed;
    right: max(8px, env(safe-area-inset-right));
    bottom: calc(var(--bottom-tab-bar-height) + env(safe-area-inset-bottom) + 6px);
    z-index: 96;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    min-width: 180px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  }

  .bottom-tab-more-menu.hidden {
    display: none;
  }

  .bottom-tab-more-option {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    min-height: 44px;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-family: var(--font-ui);
    font-size: 15px;
    font-weight: var(--fw-medium);
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .bottom-tab-more-option:active {
    background: var(--bg-hover);
  }

  .bottom-tab-more-icon {
    font-size: 18px;
    line-height: 1;
    width: 20px;
    text-align: center;
  }

  .bottom-tab-more-label {
    line-height: 1.2;
  }

  .top-bar-team-name,
  #top-bar-leader {
    display: none;
  }

  /* Wiki chat panel — the inline styles in wiki.js set flex:1 on the list
     and put the footer at the end of a flex column. The remaining mobile
     issue is the composer's bottom padding sitting below the home-indicator;
     add a safe-area cushion that doesn't affect desktop. */
  #wiki-center-panel > div:last-child {
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
  }

  /* Chat composer textarea + send button — bump font-size to 16px so iOS
     Safari doesn't auto-zoom the page when the input gets focus. (Anything
     below 16px triggers the zoom; once zoomed, the layout stops fitting.)
     Touch targets ≥44px on send. */
  #wiki-center-panel textarea {
    font-size: 16px !important;
    min-height: 44px !important;
  }
  /* Section-refine chat composer opts out of the 16px bump above so its
     size matches the canonical conversations msgTextarea (.composer-textarea
     at 0.85rem). Accept the iOS auto-zoom-on-focus tradeoff for visual parity. */
  #wiki-center-panel textarea.section-refine-composer {
    font-size: 0.85rem !important;
    min-height: var(--input-height) !important;
  }
  #wiki-center-panel .btn {
    min-height: 44px;
  }
  /* Small-capsule variants opt out of the 44px touch floor — they're
     explicitly sized for tighter chrome (edit-header Save button, etc.)
     and otherwise inflate to full-button height on mobile. */
  #wiki-center-panel .btn.btn-sm,
  #wiki-center-panel .btn.btn-xs {
    min-height: 0;
  }
  /* Section-refine action buttons (Send / Save → Preview / Cancel) opt OUT
     of both the wiki-wide 44px floor above and the .mm-chat-composer .btn
     stretch rule below. Specificity (id + 2 classes) wins over both;
     !important neutralizes the .mm-chat-composer !important downstream. */
  #wiki-center-panel .section-refine-actions .btn {
    min-height: 0 !important;
    align-self: auto !important;
  }
  /* Same opt-out for any pill-composer button (Send / Create / Sounds-like-me
     etc.) inside the wiki center panel. These are the small-capsule action
     buttons nested inside a .pill-composer wrapper — sized to ~28px by the
     shared .pill-composer-action rule and meant to match the Conversations
     Send button regardless of which view they appear in. Without this the
     wiki-wide 44px floor stretches the wiki Create button (and the Core/
     facet chat Send) on mobile and breaks visual parity with the rest of
     the app's chat composers. */
  #wiki-center-panel .pill-composer-action {
    min-height: 0 !important;
  }
  /* Opt out: root-action chrome buttons (Backfill format hints, Wipe & Reset)
     sit in the Identity page-header on mobile and need to render at the same
     compact size as desktop. The 44px touch-target floor would balloon them
     and blow the 44px page-header row. */
  #wiki-center-panel .wiki-root-actions .btn {
    min-height: 0;
  }
  /* Opt out: the Core/facet upload CTA button ("↑ Upload to your Core") sits
     inside its own framed card above the section list. The 44px touch floor
     balloons it well past its desktop capsule height (the card already gives
     it ample tap room), so pin it back to the shared .btn baseline to match
     desktop. */
  #wiki-center-panel .core-upload-cta .btn {
    min-height: 0;
  }

  /* Chat sidebar (left wiki rail) on mobile: collapse to an off-canvas drawer.
     The wrapper (#wiki-wrapper) holds the rail + center; the rail is positioned
     absolutely and slid offscreen by default. A hamburger (#wiki-rail-toggle)
     and backdrop (#wiki-rail-backdrop) are rendered alongside; the wrapper
     gets .mobile-rail-open when the drawer is visible. Desktop is unaffected
     because all of these rules live inside this @media block. */
  #wiki-left-panel,
  :root[data-style~="liquid"] #wiki-wrapper #wiki-left-panel {
    width: 64vw !important;
    max-width: 256px;
    position: absolute !important;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 30;
    transform: translateX(-100%);
    transition: transform 220ms ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.18);
    /* Force opaque surface on mobile — mirrors the .conv-list-col exception
       at line ~4661. On mobile this rail is an off-canvas drawer that overlays
       the center panel; the liquid translucent override (line ~4515) paints a
       55% translucent surface and the previous fix (commit 0863f50) couldn't
       beat it.

       Why this works where 0863f50 didn't: the previous attempt used
       `#wiki-left-panel` + !important (1 ID, specificity 100). The global
       liquid rule at line ~4515 is `:root[data-style~="liquid"] #wiki-left-panel`
       + !important (1 ID + 2 classes, specificity 120) AND comes later in
       source order — so it won on both counts. We now ALSO target the liquid
       form, but qualified with `#wiki-wrapper` ancestor (2 IDs + 2 classes,
       specificity 220) so it beats the global liquid rule on specificity
       even though it appears earlier in the file. */
    background: var(--bg-surface) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  /* Open state — slides the drawer in. Must match the specificity of the
     liquid-qualified off-canvas rule above (`:root[data-style~="liquid"]
     #wiki-wrapper #wiki-left-panel`, spec 220) or it'll lose to it under the
     default liquid theme and the panel will stay at translateX(-100%) even
     when .mobile-rail-open is set — backdrop fades in but no panel appears.
     Bare `#wiki-wrapper.mobile-rail-open #wiki-left-panel` alone is only
     spec 210 (2 IDs + 1 class), so we add a parallel liquid-prefixed selector
     that reaches spec 230 to win cleanly. Same dual-selector pattern used
     above for the off-canvas position rule. */
  #wiki-wrapper.mobile-rail-open #wiki-left-panel,
  :root[data-style~="liquid"] #wiki-wrapper.mobile-rail-open #wiki-left-panel {
    transform: translateX(0);
  }

  /* The facets list needs its OWN opaque container so the surface reads as a
     clear white card — matching the "New Facet" button row wrap above it,
     which paints `background:var(--bg-surface);border-bottom:1px solid var(--border)`
     inline in wiki.js buildChatSidebar (line ~1319). The list lives inside
     `.wiki-chat-sidebar` (the wrap that holds both the New Facet row AND the
     facet list), but the global liquid rule at line ~4522 sets
     `.wiki-chat-sidebar { background: transparent !important }` — so even
     after the rail panel is opaque (above), the list area visually merges
     with the center pane behind it. Painting --bg-surface explicitly here
     (with the same 2-ID specificity trick — `#wiki-wrapper #wiki-left-panel`
     ancestors — so we beat the global liquid `.wiki-chat-sidebar` rule)
     guarantees a single solid card-like surface from the New Facet row all
     the way down through the list. */
  #wiki-left-panel .wiki-chat-sidebar,
  :root[data-style~="liquid"] #wiki-wrapper #wiki-left-panel .wiki-chat-sidebar {
    background: var(--bg-surface) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }

  /* Mobile keeps the absolute-positioned drawer hamburger; hide the
     desktop in-panel toggle so we don't render two side-by-side
     toggles for the same surface. */
  .wiki-rail-collapse-btn {
    display: none;
  }

  /* Hamburger toggle — unified 28px capsule height matches the rest of the
     capsule control family (desktop .wiki-rail-collapse-btn is also 28×28).
     left:16px lines up with #top-bar's 16px gutter directly above. */
  #wiki-rail-toggle {
    position: absolute;
    /* Centered inside the 52px page header row: (52 − 28) / 2 = 12px. */
    top: 12px;
    left: 16px;
    z-index: 25;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Visible circular button — matches .conv-list-toggle and
       .wiki-rail-collapse-btn. `padding-bottom: 2px` optically centers
       `‹›` glyphs whose visual mass sits at x-height. */
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--text-primary);
    font-size: 17px;
    font-weight: var(--fw-semibold);
    line-height: 1;
    cursor: pointer;
    padding: 0 0 2px;
    box-sizing: border-box;
    transition: background 120ms, border-color 120ms;
  }
  #wiki-rail-toggle:hover {
    background: var(--bg-hover);
    border-color: var(--text-muted);
  }

  /* Hide the off-canvas drawer affordances on surfaces with no facet to
     anchor the rail (root Core pages, etc.). renderLayout adds .rail-collapsed
     via shouldHideFacetsRail; on desktop that hides the panel, but on mobile
     the panel is off-canvas, so without this the hamburger would still let the
     user open a facets drawer that has no business showing on a Core page. */
  #wiki-wrapper.rail-collapsed #wiki-rail-toggle,
  #wiki-wrapper.rail-collapsed #wiki-rail-backdrop {
    display: none !important;
  }

  /* Make the hamburger float above center-panel content. All center-panel
     variants (chat, page-viewer, edit, identity) get an inner top spacer so
     the hamburger column (16px gutter + 32px width + 8px gap = 56px) is
     reserved on the left edge of any first-child header, keeping the search
     input / avatar / title from tucking under it. */
  #wiki-center-panel > div:first-child {
    padding-left: 56px !important;
  }
  /* Edit / Identity panels paint padding directly on #wiki-center-panel
     (no internal header), so push the top-edge content below the hamburger.
     Inline `padding:24px 32px` from wiki.js requires !important to override.
     Chat / page / identity opt out via data-mode — their header children
     share a 40px row with the hamburger, matching the Conversations /
     Discover / Facets page header height for cross-view parity. */
  #wiki-center-panel {
    padding-top: 40px !important;
  }
  #wiki-center-panel[data-mode] {
    padding-top: 0 !important;
  }
  /* Wiki page-header on mobile: forced to a single row inline with the
     hamburger toggle. Height pulled from --page-header-mobile-height (set
     in :root) so this stays in sync with the desktop header padding tokens.
     padding-left: 56px reserves the hamburger column (16px gutter + 32px
     toggle + 8px gap) so chrome aligns with the wiki menu bar; padding-right:
     16px matches that bar's trailing gutter. Search bar in page-viewer mode
     has `min-height:43px` inline so !important is required to shrink it down
     to the standard row. */
  #wiki-center-panel > .page-header,
  #wiki-center-panel[data-mode="page"] > div:first-child {
    min-height: var(--page-header-mobile-height) !important;
    max-height: var(--page-header-mobile-height) !important;
    padding-top: 4px !important;
    padding-bottom: 4px !important;
    padding-left: 56px !important;
    padding-right: 16px !important;
    box-sizing: border-box !important;
    overflow: hidden;
  }
  /* Wiki menu bar (Sources + page-viewer) — match the 44px page-header height
     used by Identity / Account / Help so all page title rows line up across
     views. Override the data-mode="page" clamp via the same selector
     specificity. */
  /* Specificity note: the rule above (`#wiki-center-panel > div:first-child`)
     uses :first-child which gives it (1,1,1). To win over its
     `padding-left: 44px !important`, add :first-child here too — that
     bumps this selector to (1,2,1) and lets the !important padding land. */
  #wiki-center-panel > .wiki-menu-bar:first-child,
  #wiki-center-panel[data-mode="page"] > .wiki-menu-bar:first-child {
    min-height: var(--page-header-mobile-height) !important;
    max-height: var(--page-header-mobile-height) !important;
    padding-top: 4px !important;
    padding-bottom: 4px !important;
    /* 16px gutter (matches #top-bar's `padding: 0 16px` so the wiki bar's
       left/right edges line up with the version capsule and other top-bar
       elements directly above) + 32px hamburger + 8px gap = 56px. */
    padding-left: 56px !important;
    padding-right: 16px !important;
    overflow: visible;
  }

  /* Backdrop — semi-transparent overlay covering everything except the rail.
     Hidden by default; only shown when drawer is open. */
  #wiki-rail-backdrop {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 20;
  }
  #wiki-wrapper.mobile-rail-open #wiki-rail-backdrop {
    display: block;
  }

  /* Citations and long URLs in chat bubbles — prevent horizontal overflow
     that pushes the whole layout off-screen. */
  #wiki-center-panel,
  #wiki-center-panel * {
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* Conversations list column on mobile: full off-canvas drawer pattern
     mirroring the wiki rail. The list is positioned absolutely and overlays
     the detail panel; the existing in-header chevron (.conv-list-toggle)
     stays in place and drives the open/close state. Default state on mobile
     is "closed" — conversations.js init() detects mobile and sets
     .conv-list-hidden on .conv-body so the user lands on the detail panel
     with the drawer hidden, exactly like facets.
     The override `display: flex !important` defeats the global
     `.conv-body.conv-list-hidden .conv-list-col { display: none !important }`
     rule so the drawer can animate via transform instead of snapping. */
  .conv-body { position: relative; }
  .conv-list-col {
    width: 64vw !important;
    max-width: 256px !important;
    position: absolute !important;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 30;
    transform: translateX(0);
    transition: transform 220ms ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.18);
    background: var(--bg-surface);
    font-size: calc(15px * var(--conv-list-scale-mobile, 1));
  }
  .conv-body.conv-list-hidden .conv-list-col {
    display: flex !important;
    transform: translateX(-100%);
    pointer-events: none;
  }

  /* Backdrop — semi-transparent overlay covering the detail pane while the
     drawer is open. Visible only when .conv-list-hidden is absent (drawer
     open) on mobile. Tap closes the drawer. */
  #conv-list-backdrop {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 20;
  }
  .conv-body:not(.conv-list-hidden) #conv-list-backdrop {
    display: block;
  }

  /* Composer (textarea + Send button) — keep desktop sizing on mobile too
     (textarea ~38px, Send button ~30px). The previous 44px touch-target
     override was reverted in favor of visual parity with desktop. */

  /* Ensure all forms' inputs use 16px on mobile to prevent iOS zoom. */
  input[type="text"],
  input[type="email"],
  input[type="password"],
  input[type="search"],
  input[type="url"],
  textarea,
  select {
    font-size: 16px;
  }
}

/* ─── Account page ───────────────────────────────────────────────────────────── */

.account-page {
  max-width: 680px;
  padding: 28px 28px 40px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.account-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.account-panel--danger {
  border-color: rgba(248, 81, 73, 0.35);
}

.account-panel-title {
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.account-panel-title--danger {
  color: var(--accent-red);
}

.account-panel-body {
  padding: 16px;
}

.account-panel-desc {
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 14px;
}

/* Profile grid */
.account-profile-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 24px;
}

.account-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.account-field-label {
  font-size: 10px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.account-field-value {
  font-size: 12px;
  color: var(--text-primary);
  font-family: var(--font-ui);
}

/* Status grid */
.account-status-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-status-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}

.account-status-row--meta {
  color: var(--text-secondary);
  margin-top: 4px;
}

.account-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.account-status-dot--green  { background: var(--accent-green); }
.account-status-dot--amber  { background: var(--accent-orange); }

.account-status-label {
  color: var(--text-secondary);
  min-width: 130px;
}

.account-status-value {
  color: var(--text-primary);
}

/* Danger zone */
.account-danger-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.account-danger-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.account-danger-row--muted {
  opacity: 0.5;
}

.account-danger-info {
  flex: 1;
  min-width: 0;
}

.account-danger-info strong {
  display: block;
  font-size: 12px;
  color: var(--text-primary);
  margin-bottom: 4px;
}

.account-danger-info p {
  font-size: 11px;
  color: var(--text-secondary);
  line-height: 1.6;
}

/* Shared states */
.account-loading {
  font-size: 12px;
  color: var(--text-muted);
}

.account-error {
  font-size: 12px;
  color: var(--accent-red);
}

.account-output {
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px;
  font-size: 11px;
  font-family: var(--font-mono);
  color: var(--text-primary);
  white-space: pre-wrap;
  max-height: 240px;
  overflow-y: auto;
  margin: 0;
}

/* ─── Import / Export page ───────────────────────────────────────────────────── */

.ie-page {
  max-width: 760px;
  padding: 28px 28px 40px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.ie-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.ie-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  gap: 12px;
}

.ie-panel-title {
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.ie-panel-body {
  padding: 16px;
}

.ie-panel-desc {
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 14px;
}

/* Bundles table */
.ie-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}

.ie-table th {
  text-align: left;
  padding: 6px 10px;
  font-size: 10px;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border);
}

.ie-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.ie-table tr:last-child td {
  border-bottom: none;
}

.ie-table tr:hover td {
  background: var(--bg-hover);
}

.ie-cell-name {
  font-family: var(--font-ui);
  color: var(--text-primary);
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ie-cell-date {
  color: var(--text-secondary);
  white-space: nowrap;
}

.ie-cell-size {
  color: var(--text-muted);
  white-space: nowrap;
}

.ie-cell-actions {
  display: flex;
  gap: 4px;
  justify-content: flex-end;
  white-space: nowrap;
}

/* Row highlight (post-reset) */
@keyframes ie-row-highlight {
  0%   { background: rgba(88, 166, 255, 0.18); }
  100% { background: transparent; }
}

.ie-row--highlight td {
  animation: ie-row-highlight 3s ease-out forwards;
}

/* Import from file */
.ie-import-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.ie-file-input {
  flex: 1;
  font-size: 12px;
  color: var(--text-secondary);
  font-family: var(--font-ui);
}

/* Shared states */
.ie-loading {
  font-size: 12px;
  color: var(--text-muted);
}

.ie-empty {
  font-size: 12px;
  color: var(--text-secondary);
  padding: 8px 0;
}

.ie-error {
  font-size: 12px;
  color: var(--accent-red);
}

.ie-output {
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px;
  font-size: 11px;
  font-family: var(--font-mono);
  color: var(--text-primary);
  white-space: pre-wrap;
  max-height: 240px;
  overflow-y: auto;
  margin: 0;
}

/* === bundle shares === */

/* Per-row "Shares (n)" badge; rendered as a clickable button styled like a pill */
.bs-shares-badge {
  background: rgba(63, 185, 80, 0.12);
  color: var(--accent-green);
  border: 1px solid rgba(63, 185, 80, 0.25);
  cursor: pointer;
  font: inherit;
  padding: 2px 7px;
  border-radius: var(--radius-pill);
  font-size: 10px;
  font-weight: var(--fw-medium);
  font-family: var(--font-ui);
  margin-left: 4px;
}
.bs-shares-badge:hover {
  background: rgba(63, 185, 80, 0.22);
}

/* Share status pill — slightly tighter to fit alongside other content */
.bs-status-pill {
  margin-left: 6px;
}

/* Create-share modal form */
.bs-share-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.bs-share-intro {
  margin: 0;
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
}
.bs-field-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.bs-field-label {
  font-size: 10px;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.bs-radio-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 16px;
}
.bs-radio {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-primary);
  font-family: var(--font-ui);
  cursor: pointer;
  user-select: none;
}
.bs-radio input[type="radio"] {
  cursor: pointer;
}
.bs-error {
  font-size: 11px;
  color: var(--accent-red);
  background: rgba(248, 81, 73, 0.08);
  border: 1px solid rgba(248, 81, 73, 0.25);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}

/* Success / copy-link UI */
.bs-success-intro {
  margin: 0 0 10px 0;
  font-size: 12px;
  color: var(--text-primary);
}
.bs-link-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.bs-link-input {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-primary);
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  min-width: 0;
}
.bs-meta {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  color: var(--text-secondary);
}
.bs-token-line code {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
}
.bs-copy-status {
  margin-top: 8px;
  font-size: 11px;
  color: var(--accent-green);
}

/* Shares list modal */
.bs-shares-list-wrap {
  min-width: 320px;
}
.bs-shares-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.bs-share-row {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--bg-base);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.bs-share-row--inactive {
  opacity: 0.6;
}
.bs-share-row-main {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.bs-share-token {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1px 6px;
}
.bs-share-uses {
  font-size: 11px;
  color: var(--text-secondary);
  margin-left: auto;
}
.bs-share-row-meta {
  font-size: 10px;
  color: var(--text-muted);
}
.bs-share-row-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
  margin-top: 4px;
}

/* === /bundle shares === */

/* === billing === */
.billing-balance-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 4px 0 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}
.billing-balance-label {
  font-size: 12px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.billing-balance-value {
  font-size: 28px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.billing-balance-unit {
  font-size: var(--fs-body);
  color: var(--text-secondary);
}
.billing-refresh-btn {
  margin-left: auto;
}

.billing-frozen-banner {
  background: rgba(248, 81, 73, 0.08);
  border: 1px solid rgba(248, 81, 73, 0.35);
  color: var(--accent-red);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: var(--fs-body);
  line-height: 1.5;
}
.billing-frozen-banner strong {
  margin-right: 6px;
}

.billing-section {
  margin-bottom: 18px;
}
.billing-section:last-child {
  margin-bottom: 0;
}
.billing-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.billing-section-title {
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 8px 0;
}
.billing-section-header .billing-section-title {
  margin-bottom: 0;
}

.billing-card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
@media (max-width: 720px) {
  .billing-card-grid {
    grid-template-columns: 1fr;
  }
}

.billing-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.billing-card-name {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.billing-card-price {
  font-size: 22px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.billing-card-period {
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  color: var(--text-secondary);
  margin-left: 2px;
}
.billing-card-tokens {
  font-size: 12px;
  color: var(--text-secondary);
}
.billing-buy-btn {
  margin-top: 6px;
  align-self: stretch;
}
.billing-card-error {
  font-size: 12px;
  color: var(--accent-red);
  margin-top: 4px;
  word-break: break-word;
}

.billing-empty {
  font-size: var(--fs-body);
  color: var(--text-muted);
  padding: 8px 0;
}

.billing-history {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.billing-history-row {
  display: grid;
  grid-template-columns: minmax(140px, 1.4fr) 64px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  font-size: 12px;
}
.billing-history-row:nth-child(odd) {
  background: var(--bg-surface);
}
.billing-history-date {
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.billing-history-type {
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
}
.billing-history-label {
  color: var(--text-primary);
}
.billing-history-tag {
  display: inline-block;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0 4px;
  margin-left: 4px;
}
.billing-history-amt {
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semibold);
  text-align: right;
}
.billing-history-amt--pos {
  color: var(--accent-green);
}
.billing-history-amt--neg {
  color: var(--accent-red);
}
.billing-history-amt--neutral {
  color: var(--text-muted);
}
/* === /billing === */

/* ─── Identity grouped sections (SwiftUI Form-style cards) ──────────────────── */

.identity-section-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0;
  margin-bottom: 10px;
  overflow: hidden;
}
/* Section-refine chat "typing" indicator — three pulsing dots that replace the
   old "Thinking…" text while a turn/finalize is in flight. */
.section-chat-typing { display: inline-flex; align-items: center; gap: 4px; padding: 6px 4px; }
.section-chat-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-muted);
  animation: section-typing-bounce 1.2s infinite ease-in-out both;
}
.section-chat-typing span:nth-child(1) { animation-delay: -0.24s; }
.section-chat-typing span:nth-child(2) { animation-delay: -0.12s; }
.section-chat-typing span:nth-child(3) { animation-delay: 0s; }
@keyframes section-typing-bounce {
  0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
  40% { transform: scale(1); opacity: 1; }
}
.identity-section-card > h3.identity-section-header {
  font-size: 0.8125rem;
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-primary);
  margin: 0;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  user-select: none;
  transition: background 120ms;
}
.identity-section-card > h3.identity-section-header:hover {
  background: var(--bg-hover);
}
.identity-section-card.collapsed > h3.identity-section-header {
  border-bottom: none;
}
.identity-section-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.identity-section-trailing {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
  text-transform: none;
  letter-spacing: 0;
  font-weight: var(--fw-regular);
}
.identity-section-status {
  font-size: 0.8125rem;
  color: var(--text-primary);
  opacity: 0.7;
  font-family: var(--font-ui);
}
.identity-section-status--empty {
  font-style: italic;
}
.identity-section-chev {
  color: var(--text-muted);
  font-size: 0.85em;
  flex-shrink: 0;
  /* Single ⌃ glyph rotated by parent state — keeps stroke weight identical
     across orientations. Default (expanded) points down; .collapsed → right. */
  display: inline-block;
  transition: transform 120ms ease;
  transform: rotate(180deg);
}
.identity-section-card.collapsed .identity-section-chev {
  transform: rotate(90deg);
}
.identity-section-body {
  padding: 12px 18px 14px;
}
.identity-section-card.collapsed > .identity-section-body {
  display: none;
}
/* The "Chat to refine" footer is a direct child of the card (pinned, see
   wiki.js) rather than inside the body, so collapse must hide it explicitly —
   the body's `display:none` above no longer covers it. */
.identity-section-card.collapsed > .identity-section-refine {
  display: none;
}
/* Expanded topic (read mode): never taller than the visible center-panel area.
   Cap to the viewport minus the top bar + sticky page header + a little room
   above/below, then scroll the body internally. The header row stays pinned
   (flex-shrink:0) so the title is always visible while long content scrolls
   under it. Short topics keep their natural height — max-height only caps, it
   doesn't stretch.
   Scoped to :not([data-refine-active]) so the "Chat to refine" mode keeps its
   own viewport-bounded transcript scroll (buildSectionChat) — no nested scroll. */
.identity-section-card:not(.collapsed):not([data-refine-active]) {
  display: flex;
  flex-direction: column;
  /* Trailing offset is the breathing room split above + below the card inside
     the visible area (on top of the top bar + sticky header chrome). Larger =
     shorter card, more space above/below. */
  max-height: calc(100vh - var(--top-bar-height) - var(--page-header-height) - 120px);
}
.identity-section-card:not(.collapsed):not([data-refine-active]) > h3.identity-section-header {
  flex-shrink: 0;
}
.identity-section-card:not(.collapsed):not([data-refine-active]) > .identity-section-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
/* Pinned "Chat to refine" footer: only renders in expanded read mode (display
   set HERE, not inline, so the collapsed `display:none` above wins). Never
   shrinks, so it stays visible at the bottom while the body above it scrolls. */
.identity-section-card:not(.collapsed):not([data-refine-active]) > .identity-section-refine {
  display: flex;
  flex-shrink: 0;
}
/* Empty topics open straight into "Chat to refine" — give that disclosure group
   the same viewport cap (and centering via scrollActiveRefineIntoView). Here the
   body is a flex column so the chat fills it: header pinned (above), composer
   pinned at the bottom, and the transcript scrolls in the middle — one scroll
   surface, capped to the viewport. The transcript's own flex sizing lives in
   buildSectionChat (wiki.js). */
.identity-section-card[data-refine-active] {
  display: flex;
  flex-direction: column;
  /* Chat-to-refine gets 25% more height than the plain expanded cap above —
     refining is interactive (transcript + composer) and benefits from the room.
     Clamped so it never overflows the viewport on tall screens: keep at least
     24px of breathing room below the chrome. */
  max-height: min(
    calc((100vh - var(--top-bar-height) - var(--page-header-height) - 120px) * 1.25),
    calc(100vh - var(--top-bar-height) - var(--page-header-height) - 24px)
  );
  /* Empty topics render a sparse chat (one opener + composer) so the card sizes
     to its small content and looks too short. Give it a floor so the transcript
     area gets ~2x the whitespace of a content-sized empty chat. The body is
     flex:1 1 auto with the composer pinned at the bottom, so this extra height
     lands as transcript whitespace. Clamped under the max-height ceiling above so
     it never overflows the viewport (keep ≥24px below the chrome on short screens). */
  min-height: min(
    55vh,
    calc(100vh - var(--top-bar-height) - var(--page-header-height) - 24px)
  );
}
.identity-section-card[data-refine-active] > h3.identity-section-header {
  flex-shrink: 0;
}
.identity-section-card[data-refine-active] > .identity-section-body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.identity-section-body > p,
.identity-section-body > li {
  margin: 4px 0;
}
/* Wiki markdown lists — the renderer wraps consecutive `- ` / `* ` lines in
   <ul class="wiki-ul">. Give it real left padding so bullets are visible
   and the text aligns nicely under the section content. */
.wiki-ul {
  list-style: disc outside;
  padding-left: 1.4em;
  margin: 4px 0;
}
.wiki-ul > .wiki-li {
  margin: 2px 0;
}
.identity-section-body > br:first-child,
.identity-section-body > br + br {
  display: none;
}

/* ─── Help View ──────────────────────────────────────────────────────────────── */

.help-view {
  padding: 24px;
  max-width: 760px;
}

.help-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 24px;
  padding: 12px 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.help-toc-link {
  font-size: var(--fs-body);
  color: var(--text-secondary);
  text-decoration: none;
  padding: 4px 10px;
  border-radius: 4px;
  transition: background 0.15s, color 0.15s;
}

.help-toc-link:hover {
  background: var(--bg-base);
  color: var(--text-primary);
}

.help-section {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 24px;
  margin-bottom: 20px;
  scroll-margin-top: 16px;
}

.help-section-title {
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 16px;
}

.help-section-body {
  color: var(--text-primary);
  font-size: var(--fs-body);
  line-height: 1.65;
}

.help-section-body h4 {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 20px 0 8px;
}

.help-section-body h4:first-child {
  margin-top: 0;
}

.help-section-body p {
  margin: 0 0 12px;
}

.help-section-body ul,
.help-section-body ol {
  margin: 0 0 12px;
  padding-left: 22px;
}

.help-section-body li {
  margin-bottom: 6px;
}

.help-section-body strong {
  color: var(--text-primary);
  font-weight: 600;
}

.help-section-body em {
  color: var(--text-secondary);
  font-style: italic;
}

/* ── Help disclosure groups ───────────────────────────────────────────────
   Each topic inside a help section is a native <details> element with a
   custom summary. Multi-open by default — independent state per group. */
.help-disclosure {
  position: relative;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 10px;
  overflow: hidden;
  transition: border-color 0.15s;
}

.help-disclosure:last-child {
  margin-bottom: 0;
}

.help-disclosure:hover {
  border-color: var(--text-muted);
}

.help-disclosure[open] {
  border-color: var(--accent-blue);
}

/* Active indicator — a vertical capsule pill in a reserved leading gutter,
   mirroring .conv-list-item::before so the whole app speaks one active-indicator
   language. Lives in base CSS (all styles). Hidden (transparent + collapsed)
   until the disclosure is [open], then the bar grows + tints in via the
   transition below. NOTE: a <details> element persists across open/close (it's
   not re-created like a conversation row), so the [open] TRANSITION fires on its
   own — we do NOT also run the conv-bar-expand keyframe here, since running both
   at once made the bar redraw twice (the double-draw glitch). Horizontal inset
   (left:10px) gives the bar breathing room from the card edge. */
.help-disclosure::before {
  content: '';
  position: absolute;
  left: 10px;
  top: 8%;
  bottom: 8%;
  width: 3px;
  border-radius: var(--radius-pill);
  background: transparent;
  transform: scaleY(0.2);
  transform-origin: center;
  transition: background var(--transition), transform 280ms cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}
.help-disclosure[open]::before {
  background: var(--accent-blue);
  transform: scaleY(1);
}
@media (prefers-reduced-motion: reduce) {
  .help-disclosure::before { transition: none; }
}

.help-disclosure > summary {
  list-style: none;
  cursor: pointer;
  /* Extra left padding clears the leading indicator bar (left:10px + 3px wide)
     with comfortable leading space before the title text. */
  padding: 12px 16px 12px 28px;
  display: flex;
  align-items: center;
  gap: 10px;
  /* Match the identities-page topic/facet rows (wiki view, font-size:var(--fs-body))
     and the help section title — the topic label was reading a notch large at 14px. */
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--text-primary);
  user-select: none;
}

.help-disclosure > summary::-webkit-details-marker {
  display: none;
}

.help-disclosure-body {
  padding: 0 16px 14px 38px;
  font-size: var(--fs-body);
  line-height: 1.65;
  color: var(--text-primary);
}

.help-disclosure-body p {
  margin: 0 0 12px;
}

.help-disclosure-body p:last-child {
  margin-bottom: 0;
}

.help-disclosure-body ul,
.help-disclosure-body ol {
  margin: 0 0 12px;
  padding-left: 22px;
}

.help-disclosure-body li {
  margin-bottom: 6px;
}

.help-disclosure-body strong {
  color: var(--text-primary);
  font-weight: 600;
}

.help-disclosure-body em {
  color: var(--text-secondary);
  font-style: italic;
}


/* ─── Liquid / Glass ───────────────────────────────────────────────────────────
   Internal codename "Hush". One material, one light source, one accent. Uses
   the app's own palette — shades of --accent-blue and the neutral surface
   variables — so it reads as the same product, not a different one. Both
   modes share a single ruleset because the variables already swap.
   Scoped to #view-help and #view-discover (and discover-overlay) only.

   Selector note: rules below use [data-style~="liquid"] (token list) so that
   either `data-style="liquid"` (Liquid) or `data-style="liquid glass"` (Glass)
   matches. The Glass surface style is the Liquid surface PLUS a specular edge
   glare on circles and capsules — those glare rules use [data-style~="glass"]
   and live at the bottom of this file. */

:root[data-style~="liquid"] #view-help {
  background-color: var(--bg-base);
  background-image:
    radial-gradient(120% 60% at 50% -20%,
      color-mix(in srgb, var(--accent-blue) 12%, transparent) 0%,
      color-mix(in srgb, var(--accent-blue) 5%, transparent) 38%,
      transparent 72%),
    linear-gradient(180deg,
      transparent 78%,
      color-mix(in srgb, var(--accent-blue) 4%, transparent) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

/* TOC — quiet links over a hairline tinted with the accent. No strip. */
:root[data-style~="liquid"] .help-toc {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 28px;
  padding: 0 0 14px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 18%, var(--border));
  border-radius: 0;
  box-shadow: none;
}

:root[data-style~="liquid"] .help-toc-link {
  padding: 6px 10px;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-secondary);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  text-decoration: none;
  transition: color 200ms ease;
}

:root[data-style~="liquid"] .help-toc-link:hover {
  color: var(--accent-blue);
  background: transparent;
}

/* Section — one card, one blur, one hairline. Generous interior. */
:root[data-style~="liquid"] .help-section {
  margin: 0 0 24px;
  padding: 32px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--bg-card) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 10%, var(--border));
  /* Drop shadow removed by default — keep only the inset top-highlight. To
     re-add as a hover-animated drop shadow later, append the drop layer back:
     0 18px 50px -24px color-mix(in srgb, var(--bg-base) 60%, black). */
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 6%, transparent);
}

/* Title — normal weight, no caps, no tracking. A label, not a poster. */
:root[data-style~="liquid"] .help-section-title {
  margin: 0 0 22px;
  font-size: var(--fs-body);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
}

/* Body — looser leading. Let it breathe. Font-size tracks --fs-body so it
   matches site body copy (13px default, 14px under the Uniform-14 toggle). */
:root[data-style~="liquid"] .help-section-body {
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--text-primary);
}

/* Disclosure — no border, no card. A soft capsule pill in the leading gutter
   mirrors .conv-list-item::before and .sidebar-nav-item.active so the whole
   app speaks one active-indicator language. */
:root[data-style~="liquid"] .help-disclosure {
  position: relative;
  margin: 0 0 4px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  box-shadow: none;
  overflow: hidden;
  transition: background-color 240ms ease;
}

:root[data-style~="liquid"] .help-disclosure::before {
  /* Capsule indicator on the leading edge — same geometry/style as the base
     .help-disclosure::before above. Taller (8% inset) and inset 10px from the
     edge for horizontal breathing room. Grows + tints in via the [open]
     transition; the conv-bar-expand keyframe was removed because running it
     alongside the transition double-drew the bar. */
  content: '';
  position: absolute;
  left: 10px;
  top: 8%;
  bottom: 8%;
  width: 3px;
  border-radius: var(--radius-pill);
  background: transparent;
  transform: scaleY(0.2);
  transform-origin: center;
  transition: background var(--transition), transform 280ms cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
}

:root[data-style~="liquid"] .help-disclosure[open] {
  background: color-mix(in srgb, var(--accent-blue) 6%, transparent);
}

:root[data-style~="liquid"] .help-disclosure[open]::before {
  background: var(--accent-blue);
  transform: scaleY(1);
}

@media (prefers-reduced-motion: reduce) {
  :root[data-style~="liquid"] .help-disclosure::before { transition: none; }
}

:root[data-style~="liquid"] .help-disclosure > summary {
  list-style: none;
  cursor: pointer;
  padding: 12px 16px 12px 28px;
  display: flex;
  align-items: center;
  gap: 12px;
  /* Match the base summary size (var(--fs-body)) — was 15px under glass. */
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-primary);
  transition: padding 220ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Open — the card grows taller with breathing room above + below so the
   content sits inset from the tinted background's top/bottom edges. */
:root[data-style~="liquid"] .help-disclosure[open] > summary {
  padding-top: 18px;
  padding-bottom: 18px;
}

:root[data-style~="liquid"] .help-disclosure > summary::-webkit-details-marker {
  display: none;
}

:root[data-style~="liquid"] .help-disclosure-body {
  padding: 4px 16px 20px 30px;
  font-size: var(--fs-body);
  line-height: 1.68;
  color: var(--text-secondary);
}

/* ── Discover view ─────────────────────────────────────────────────────────── */

:root[data-style~="liquid"] #view-discover {
  background-color: var(--bg-base);
  background-image:
    radial-gradient(120% 60% at 50% -20%,
      color-mix(in srgb, var(--accent-blue) 12%, transparent) 0%,
      color-mix(in srgb, var(--accent-blue) 5%, transparent) 38%,
      transparent 72%),
    linear-gradient(180deg,
      transparent 78%,
      color-mix(in srgb, var(--accent-blue) 4%, transparent) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

/* Toolbar — translucent over the ambient, hairline rule beneath. The
   default rule already sets sticky+z-index; we only retint surfaces. */
:root[data-style~="liquid"] .discover-toolbar {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] .discover-toolbar-search,
:root[data-style~="liquid"] .discover-toolbar-sort {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}

:root[data-style~="liquid"] .discover-toolbar-search:focus,
:root[data-style~="liquid"] .discover-toolbar-sort:focus {
  border-color: var(--accent-blue);
}

/* Cards — same translucent card recipe as .help-section. Override the inline
   `border-left: 3px solid ${facet.color}` from discover-card.js with
   !important so the colored peek doesn't bleed through under glass. */
:root[data-style~="liquid"] .discover-card {
  background: color-mix(in srgb, var(--bg-card) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 10%, var(--border)) !important;
  border-radius: var(--radius-md);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 5%, transparent),
    0 12px 32px -20px color-mix(in srgb, var(--bg-base) 60%, black);
}

:root[data-style~="liquid"] .discover-card:hover {
  border-color: color-mix(in srgb, var(--accent-blue) 24%, var(--border)) !important;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 6%, transparent),
    0 18px 40px -22px color-mix(in srgb, var(--bg-base) 50%, black);
}

/* Chips — translucent pill with a faint accent tint. */
:root[data-style~="liquid"] .discover-chip {
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

/* Overlay — discover-overlay.js sets the panel/backdrop styles inline via
   element.style.cssText, so we need !important to win. The facet-color
   left border (panel.style.borderLeft) is left untouched: we override
   top/right/bottom individually, not the shorthand. */
:root[data-style~="liquid"] .discover-overlay {
  background: color-mix(in srgb, var(--bg-base) 50%, transparent) !important;
  backdrop-filter: blur(12px) saturate(160%) !important;
  -webkit-backdrop-filter: blur(12px) saturate(160%) !important;
}

:root[data-style~="liquid"] .discover-overlay-panel {
  background: color-mix(in srgb, var(--bg-card) 55%, transparent) !important;
  backdrop-filter: blur(28px) saturate(180%) !important;
  -webkit-backdrop-filter: blur(28px) saturate(180%) !important;
  border: 1px solid color-mix(in srgb, var(--accent-blue) 12%, var(--border)) !important;
  border-radius: var(--radius-md) !important;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 6%, transparent),
    0 18px 50px -24px color-mix(in srgb, var(--bg-base) 60%, black) !important;
}

:root[data-style~="liquid"] .discover-overlay-header {
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

:root[data-style~="liquid"] .discover-overlay-prompt {
  background: color-mix(in srgb, var(--bg-elevated) 45%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}

:root[data-style~="liquid"] .discover-overlay-prompt:hover {
  background: color-mix(in srgb, var(--bg-hover) 55%, transparent);
  border-color: var(--accent-blue);
}

/* Liquid DOM — stronger, branded treatment for the Discover detail sheet (the
   hero overlay: click a public facet). Placed AFTER the liquid rules above so it
   wins on source order at equal specificity; !important beats discover-overlay.js
   inline styles. Uses the global --ld-* tokens, with local overrides for the
   deeper "hero" blur and stronger rim so it reads as distinct from plain Liquid.
   The light/dark fill switch is automatic via --ld-fill-content / --ld-scrim. */
:root[data-style~="liquiddom"] .discover-overlay {
  background: var(--ld-scrim-bg) !important;
  backdrop-filter: blur(16px) saturate(165%) !important;
  -webkit-backdrop-filter: blur(16px) saturate(165%) !important;
}
:root[data-style~="liquiddom"] .discover-overlay-panel {
  --ld-blur: 34px;   /* hero: deeper frost than the default */
  /* hero: stronger accent rim than the shared --ld-rim */
  /* Match the theme/menu sheet opacity (--ld-menu-bg, ~58%) — the content fill
     (~12%) left this facet sheet glassily transparent vs. the settings sheet. */
  background: var(--ld-menu-bg) !important;
  backdrop-filter: blur(var(--ld-blur)) saturate(190%) !important;
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(190%) !important;
  border: 1px solid color-mix(in srgb, var(--accent-blue) 22%, var(--border)) !important;
  border-radius: var(--radius-md) !important;
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 10%, transparent),
    0 24px 60px -26px color-mix(in srgb, var(--bg-base) 70%, black) !important;
}

/* ── Wiki view (sidebar "Facets" routes here in cloud) ─────────────────────── */

:root[data-style~="liquid"] #view-wiki {
  background-color: var(--bg-base);
  background-image:
    radial-gradient(120% 60% at 50% -20%,
      color-mix(in srgb, var(--accent-blue) 12%, transparent) 0%,
      color-mix(in srgb, var(--accent-blue) 5%, transparent) 38%,
      transparent 72%),
    linear-gradient(180deg,
      transparent 78%,
      color-mix(in srgb, var(--accent-blue) 4%, transparent) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

/* Left rail — translucent strip with accent-tinted right hairline. The inline
   style.cssText on #wiki-left-panel sets background + border-right, so both
   need !important to win. */
:root[data-style~="liquid"] #wiki-left-panel {
  background: color-mix(in srgb, var(--bg-surface) 55%, transparent) !important;
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-right: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

:root[data-style~="liquid"] .wiki-chat-sidebar {
  background: transparent !important;
  border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

/* Rail rows — the default dot/capsule indicator (buildSubRow in wiki.js
   and .sidebar-nav-item.active::before) already does its job, so glass
   leaves the rows untouched. Just the panel surface and toolbar get
   retinted above. */

:root[data-style~="liquid"] #view-wiki > .page-header,
:root[data-style~="liquid"] .wiki-menu-bar {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] .identity-section-card {
  background: color-mix(in srgb, var(--bg-card) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 10%, var(--border));
  border-radius: var(--radius-md);
}

/* ── Identity card depth ─────────────────────────────────────────────────
   The neutral drop shadow behind .identity-section-card and the .core-upload-cta
   that sits above it. Split out of the card rule so both surfaces share ONE
   source of truth, scaled by --identity-card-depth-intensity (0–200). The shadow
   color mixes var(--bg-base) toward black at 60% × (intensity/100) — so 0 → 0%
   mix (transparent, no shadow), 100 → 60%, 200 → clamped at 100%. The inset top
   highlight (subtle 1px lit edge) is preserved at full strength regardless,
   since it reads as material, not depth. Shadow-only — no accent glow.

   Theme + hover drive the intensity (no user control):
     light  → rests at 200 (strong), eases DOWN to 100 on hover
     dark   → rests at 0   (none),   eases IN  to 40  on hover
   Resting values live on :root.light (200) and :root (0, the dark default);
   hover overrides are the theme-scoped :hover rules below. Since a plain CSS
   custom property is not animatable on its own, we @property-register the var
   as a <number> so it interpolates, and transition it on the cards — the
   box-shadow then follows frame-by-frame. We ALSO transition box-shadow directly
   as graceful degradation for engines without @property (it animates the shadow
   even if the var snaps). @property is supported in all current
   Chromium/Safari/Firefox; older engines fall back to the box-shadow transition,
   which still tweens between the resting and hover shadows. */
@property --identity-card-depth-intensity {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

:root[data-style~="liquid"] .identity-section-card,
:root[data-style~="liquid"] .core-upload-cta {
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 5%, transparent),
    0 6px 16px -10px color-mix(in srgb,
      var(--bg-base) calc(clamp(0, var(--identity-card-depth-intensity, 0), 200) * 0.6%),
      black);
  /* Tween the registered var (preferred) AND the box-shadow (fallback). */
  transition: --identity-card-depth-intensity 200ms ease, box-shadow 200ms ease;
}

/* Hover overrides — theme-scoped, set on the individual card. :hover adds
   specificity so these out-specify the resting values above. */
:root.light .identity-section-card:hover,
:root.light .core-upload-cta:hover {
  --identity-card-depth-intensity: 100;
}
:root:not(.light) .identity-section-card:hover,
:root:not(.light) .core-upload-cta:hover {
  --identity-card-depth-intensity: 40;
}

:root[data-style~="liquid"] .identity-section-card > h3.identity-section-header {
  background: transparent;
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--text-primary);
}

:root[data-style~="liquid"] .identity-section-card.collapsed > h3.identity-section-header {
  border-bottom: 0;
}

:root[data-style~="liquid"] .identity-section-card > h3.identity-section-header:hover {
  background: color-mix(in srgb, var(--accent-blue) 5%, transparent);
}

:root[data-style~="liquid"] .wiki-content {
  font-size: 15px;
  line-height: 1.72;
  color: var(--text-primary);
}

:root[data-style~="liquid"] .wiki-link {
  color: var(--accent-blue);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 30%, transparent);
}

:root[data-style~="liquid"] .wiki-link:hover {
  border-bottom-color: var(--accent-blue);
}

:root[data-style~="liquid"] .wiki-inline-code,
:root[data-style~="liquid"] .wiki-code-block {
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 10%, var(--border));
  border-radius: var(--radius-sm);
}

:root[data-style~="liquid"] #wiki-center-panel .pill-composer,
:root[data-style~="liquid"] #wiki-center-panel .input,
:root[data-style~="liquid"] #wiki-center-panel textarea,
:root[data-style~="liquid"] #wiki-center-panel select {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}

/* Strip the Liquid translucent fill from inputs nested inside a
   pill-composer. Without this, the textarea/input got its own 50%
   surface fill on top of the pill's identical 50% fill — two stacked
   translucent layers compound to a brighter inner rectangle with a
   hairline-sharp edge where they meet. The pill carries the only fill;
   the nested control stays transparent so it reads as a single layer. */
:root[data-style~="liquid"] #wiki-center-panel .pill-composer > textarea,
:root[data-style~="liquid"] #wiki-center-panel .pill-composer > input,
:root[data-style~="liquid"] #wiki-center-panel .pill-composer > .composer-textarea,
:root[data-style~="liquid"] #wiki-center-panel .pill-composer > .input,
:root[data-style~="liquid"] #wiki-center-panel .pill-composer > .textarea {
  background: transparent;
}

:root[data-style~="liquid"] #wiki-center-panel .pill-composer:focus-within,
:root[data-style~="liquid"] #wiki-center-panel .input:focus,
:root[data-style~="liquid"] #wiki-center-panel textarea:focus,
:root[data-style~="liquid"] #wiki-center-panel select:focus {
  border-color: var(--accent-blue);
}

:root[data-style~="liquid"] #wiki-rail-toggle,
:root[data-style~="liquid"] .wiki-rail-collapse-btn {
  /* Bumped from 55% → 82% so the circle reads on glass panels in both
     light and dark themes. Border strengthened to a solid hairline with
     an accent tint so the edge is visible against the blurred surface. */
  background: color-mix(in srgb, var(--bg-card) 82%, transparent);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 28%, var(--border));
}

:root[data-style~="liquid"] #wiki-rail-toggle:hover,
:root[data-style~="liquid"] .wiki-rail-collapse-btn:hover {
  background: color-mix(in srgb, var(--bg-card) 92%, transparent);
  border-color: var(--accent-blue);
}

/* ── Conversations view ────────────────────────────────────────────────────── */

:root[data-style~="liquid"] #view-discussions {
  background-color: var(--bg-base);
  background-image:
    radial-gradient(120% 60% at 50% -20%,
      color-mix(in srgb, var(--accent-blue) 12%, transparent) 0%,
      color-mix(in srgb, var(--accent-blue) 5%, transparent) 38%,
      transparent 72%),
    linear-gradient(180deg,
      transparent 78%,
      color-mix(in srgb, var(--accent-blue) 4%, transparent) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

:root[data-style~="liquid"] #view-discussions .page-header {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] #view-discussions .conv-body {
  background: transparent;
}

/* Conv list panel intentionally NOT retinted — on mobile it becomes an
   absolute slide-out drawer that needs to stay opaque (background:
   var(--bg-surface) from the mobile breakpoint at line ~2773). A
   translucent override breaks the drawer's opacity over the chat behind. */
:root[data-style~="liquid"] #view-discussions .conv-list-col {
  border-right-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

/* Conv rows — drop the inline opaque background so the translucent panel
   reads through. JS writes item.style.background on hover/active, so the
   resting state needs !important to win. The .conv-list-item::before
   capsule already exists in default CSS and paints from --accent-blue;
   under glass we just soften the at-rest pill so the gutter has a faint
   accent presence. */
:root[data-style~="liquid"] #view-discussions .conv-list-item {
  background: transparent !important;
  border-bottom-color: color-mix(in srgb, var(--border) 40%, transparent) !important;
  transition: background-color 200ms ease;
}

:root[data-style~="liquid"] #view-discussions .conv-list-item--active {
  background: color-mix(in srgb, var(--accent-blue) 10%, transparent) !important;
  border-radius: 9px;
  /* Beat the all-rows border-bottom-color !important above so the card edge is
     clean; the ::after redraws the divider in the gap below. */
  border-bottom-color: transparent !important;
}

:root[data-style~="liquid"] #view-discussions .conv-list-item--pinned-profile {
  background: color-mix(in srgb, var(--accent-blue) 6%, transparent) !important;
  border-radius: 9px;
  border-bottom-color: transparent !important;
}

:root[data-style~="liquid"] #view-discussions .conv-list-item--active::before,
:root[data-style~="liquid"] #view-discussions .conv-list-item--pinned-profile::before {
  background: var(--accent-blue);
}

:root[data-style~="liquid"] #view-discussions .conv-detail-header {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] #view-discussions .conv-list-toggle {
  /* Match #wiki-rail-toggle / .wiki-rail-collapse-btn under glass:
     opaque-ish fill + tinted hairline border so the circle is visible
     against the panel in both light and dark themes. */
  background: color-mix(in srgb, var(--bg-card) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 28%, var(--border));
}
:root[data-style~="liquid"] #view-discussions .conv-list-toggle:hover {
  background: color-mix(in srgb, var(--bg-card) 92%, transparent);
  border-color: var(--accent-blue);
}

:root[data-style~="liquid"] #view-discussions .new-chat-meta-row {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent) !important;
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

/* Message bubbles — conversations.js sets inline `border-left: 3px solid
   ${identityColor}` + a solid bg. Kill the coloured peek with the `border`
   shorthand + !important (resets all four edges including width), and
   retint the bubble to translucent glass.
   Scope covers both #view-discussions (conversations) AND #wiki-center-panel
   (Identity → section "Chat to refine") so both surfaces render the same
   translucent bubble with the slider-controlled hairline border. */
:root[data-style~="liquid"] #view-discussions .conv-excerpt-bubble,
:root[data-style~="liquid"] #wiki-center-panel .conv-excerpt-bubble,
:root[data-style~="liquid"] #resonance-popover-panel .conv-excerpt-bubble {
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent) !important;
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  /* Border opacity is driven by --incoming-bubble-border-intensity.
     The inner color-mix is the calibrated baseline color (10% accent
     tint on var(--border)); the outer mix fades that to transparent
     so intensity=0 fully removes all 4 edges instead of falling
     through to a plain var(--border) layer underneath. */
  border: 1px solid color-mix(
    in srgb,
    color-mix(in srgb, var(--accent-blue) 10%, var(--border))
      calc(clamp(0, var(--incoming-bubble-border-intensity, 20), 200) * 1%),
    transparent
  ) !important;
  border-radius: var(--radius-sm) !important;
  padding: 10px 14px !important;
}

/* iMessage-blue treatment for the user's own bubbles — solid accent-blue
   under all themes (including liquid) so "Me" matches the .btn-primary
   "New" button exactly with no transparency tint. Covers both
   #view-discussions and #wiki-center-panel so the Identity refine chat
   gets the same iMessage-blue Me bubble as conversations. */
:root[data-style~="liquid"] #view-discussions .conv-excerpt-bubble.is-me,
:root[data-style~="liquid"] #wiki-center-panel .conv-excerpt-bubble.is-me,
:root[data-style~="liquid"] #resonance-popover-panel .conv-excerpt-bubble.is-me {
  background: var(--accent-blue) !important;
  border: 1px solid var(--accent-blue) !important;
}

/* Text color on the user's own bubble mirrors .btn-primary: white across
   every theme so the bubble reads as iMessage-style white-on-blue. */
.conv-excerpt-bubble.is-me {
  color: #fff;
}

/* Bubble shape — single line → capsule, 2+ lines → rounded rect. Driven by
   data-bubble-shape (set in conversations.js applyBubbleShape after layout)
   rather than an inline style, because the Liquid/Glass rule above pins
   border-radius with !important. Base = rounded rect (covers the Flat style,
   where the inline radius used to live, and bubbles not yet measured). */
.conv-excerpt-bubble {
  border-radius: var(--radius-sm);
}
.conv-excerpt-bubble[data-bubble-shape="single"] {
  border-radius: var(--radius-pill);
}
/* Re-assert the capsule under Liquid/Glass, whose .conv-excerpt-bubble rule
   sets `border-radius: var(--radius-sm) !important` and would otherwise win. */
:root[data-style~="liquid"] #view-discussions .conv-excerpt-bubble[data-bubble-shape="single"],
:root[data-style~="liquid"] #wiki-center-panel .conv-excerpt-bubble[data-bubble-shape="single"],
:root[data-style~="liquid"] #resonance-popover-panel .conv-excerpt-bubble[data-bubble-shape="single"] {
  border-radius: var(--radius-pill) !important;
}

/* Click-to-reveal trigger — a trailing-edge text link that stands in for the
   action footer while a reply is gated (see attachRevealTrigger in
   conversations.js). Typography matches the "Sounds like me" resonate link:
   0.7rem, --font-ui, --text-primary at 0.7 opacity. */
.conv-reveal-trigger {
  font-size: 0.7rem;
  font-family: var(--font-ui);
  color: var(--text-primary);
  opacity: 0.7;
  cursor: pointer;
  user-select: none;
  transition: opacity 120ms;
}
.conv-reveal-trigger:hover {
  opacity: 1;
}
/* The reply text, blurred in place until reveal. overflow:hidden on the bubble
   clips the blur halo. On reveal the blur transitions to none so the same
   bubble simply sharpens into focus. */
.conv-reveal-text {
  display: block;
  cursor: pointer;
  filter: blur(10px);
  -webkit-filter: blur(10px);
  transition: filter 320ms ease, -webkit-filter 320ms ease;
}
.conv-reveal-text.is-revealed {
  cursor: auto;
  filter: none;
  -webkit-filter: none;
}
@media (prefers-reduced-motion: reduce) {
  .conv-reveal-text { transition: filter 1ms linear, -webkit-filter 1ms linear; }
}


:root[data-style~="liquid"] #view-discussions .chain-silent-row {
  background: color-mix(in srgb, var(--bg-elevated) 45%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
}

:root[data-style~="liquid"] #view-discussions .mm-chat-composer {
  background: color-mix(in srgb, var(--bg-base) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border-top: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border)) !important;
}

:root[data-style~="liquid"] #view-discussions .pill-composer {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}
:root[data-style~="liquid"] #view-discussions .pill-composer:focus-within {
  border-color: var(--accent-blue);
}

/* ── Account view ──────────────────────────────────────────────────────────── */

:root[data-style~="liquid"] #view-account {
  background-color: var(--bg-base);
  background-image:
    radial-gradient(120% 60% at 50% -20%,
      color-mix(in srgb, var(--accent-blue) 12%, transparent) 0%,
      color-mix(in srgb, var(--accent-blue) 5%, transparent) 38%,
      transparent 72%),
    linear-gradient(180deg,
      transparent 78%,
      color-mix(in srgb, var(--accent-blue) 4%, transparent) 100%);
  background-attachment: fixed;
  background-repeat: no-repeat;
  background-size: cover;
}

:root[data-style~="liquid"] .account-panel {
  background: color-mix(in srgb, var(--bg-card) 55%, transparent);
  backdrop-filter: blur(20px) saturate(160%);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 10%, var(--border));
  border-radius: var(--radius-md);
  /* Drop shadow removed by default — keep only the inset top-highlight. To
     re-add as a hover-animated drop shadow later, append the drop layer back:
     0 12px 32px -20px color-mix(in srgb, var(--bg-base) 60%, black). */
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 5%, transparent);
}

:root[data-style~="liquid"] .account-panel--danger {
  border-color: color-mix(in srgb, var(--accent-red) 30%, var(--border));
}

:root[data-style~="liquid"] .account-panel-title {
  font-size: var(--fs-body);
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-muted);
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
  background: transparent;
}

:root[data-style~="liquid"] .account-panel-title--danger {
  color: var(--accent-red);
  border-bottom-color: color-mix(in srgb, var(--accent-red) 24%, var(--border));
}

:root[data-style~="liquid"] .account-field-label {
  text-transform: none;
  letter-spacing: 0;
  font-size: 11px;
  color: var(--text-muted);
}

:root[data-style~="liquid"] .account-field-value {
  font-size: var(--fs-body);
}

:root[data-style~="liquid"] .account-danger-row {
  border-bottom-color: color-mix(in srgb, var(--accent-red) 16%, var(--border));
}

/* Error banner — stay solid + red-tinted so it reads as an alert, not chrome. */
:root[data-style~="liquid"] .account-error {
  background: color-mix(in srgb, var(--accent-red) 10%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent-red) 35%, var(--border));
  color: var(--accent-red);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-size: var(--fs-body);
  line-height: 1.5;
}

:root[data-style~="liquid"] .billing-balance-row {
  border-bottom-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] .billing-balance-label,
:root[data-style~="liquid"] .billing-section-title {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--text-muted);
  font-size: 12px;
}

:root[data-style~="liquid"] .billing-frozen-banner {
  background: color-mix(in srgb, var(--accent-red) 10%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent-red) 35%, var(--border));
  border-radius: var(--radius-sm);
}

:root[data-style~="liquid"] .billing-card {
  background: color-mix(in srgb, var(--bg-surface) 55%, transparent);
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 12%, var(--border));
  border-radius: var(--radius-md);
}

:root[data-style~="liquid"] .billing-card-error {
  color: var(--accent-red);
}

:root[data-style~="liquid"] .billing-empty {
  color: var(--text-muted);
}

:root[data-style~="liquid"] .billing-history-row:nth-child(odd) {
  background: color-mix(in srgb, var(--bg-surface) 35%, transparent);
}

:root[data-style~="liquid"] .billing-history-type {
  text-transform: none;
  letter-spacing: 0;
}

:root[data-style~="liquid"] .billing-history-tag {
  border-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

/* ── Onboarding flow (modal overlay) ───────────────────────────────────────── */

/* Scrim veil + glass sheet now share the same --ld-* tokens as every other
   modal/sheet (settings popover, generic modals, menus). This makes the
   onboarding window track the glass-overlay-opacity dial and read identically
   to the rest of the app instead of its old hand-rolled 55% mix. */
:root[data-style~="liquiddom"] #onboarding-overlay {
  background: var(--ld-scrim-bg);
  backdrop-filter: blur(var(--ld-scrim-blur)) saturate(140%);
  -webkit-backdrop-filter: blur(var(--ld-scrim-blur)) saturate(140%);
}

:root[data-style~="liquiddom"] #onboarding-overlay .onboarding-card,
:root[data-style~="liquiddom"] #onboarding-overlay .onboarding-card.onboarding-card-v2 {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-md);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--text-primary) 6%, transparent),
    0 24px 64px -24px color-mix(in srgb, var(--bg-base) 60%, black);
}

:root[data-style~="liquid"] .onboarding-step-indicator {
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
  color: var(--text-muted);
}

:root[data-style~="liquid"] .onboarding-progress {
  background: color-mix(in srgb, var(--accent-blue) 12%, transparent);
}
:root[data-style~="liquid"] .onboarding-progress-fill {
  background: var(--accent-blue);
}

:root[data-style~="liquid"] .onboarding-close,
:root[data-style~="liquid"] .onboarding-back {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border));
  color: var(--text-secondary);
}
:root[data-style~="liquid"] .onboarding-close:hover,
:root[data-style~="liquid"] .onboarding-back:hover {
  background: color-mix(in srgb, var(--accent-blue) 12%, transparent);
  border-color: var(--accent-blue);
  color: var(--text-primary);
}

:root[data-style~="liquid"] .onboarding-footer,
:root[data-style~="liquid"] .onb-footer,
:root[data-style~="liquid"] .onb-composer-row {
  background: transparent;
  border-top: 1px solid color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] .onboarding-title {
  font-weight: 500;
  letter-spacing: 0;
}

:root[data-style~="liquid"] .onb-subtle {
  color: var(--text-muted);
}

/* Error banner — stays solid red-tinted so it reads as alert, not chrome. */
:root[data-style~="liquid"] .onb-error {
  background: color-mix(in srgb, var(--accent-red) 10%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent-red) 35%, var(--border));
  color: var(--accent-red);
  border-radius: var(--radius-sm);
}

:root[data-style~="liquid"] .onb-textarea,
:root[data-style~="liquid"] .onb-vignette-correction-input {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}
:root[data-style~="liquid"] .onb-textarea:focus,
:root[data-style~="liquid"] .onb-vignette-correction-input:focus {
  border-color: var(--accent-blue);
}

/* Inner tiles sit on the already-frosted card, so they share the card's
   --ld-* material and are separated by the --ld-rim hairline rather than a
   lighter fill. Same tokens = same dial response as the sheet. */
:root[data-style~="liquiddom"] .onb-path-card {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-md);
}
:root[data-style~="liquid"] .onb-path-header {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  color: var(--text-muted);
}

:root[data-style~="liquid"] .onb-chip {
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}
:root[data-style~="liquid"] .onb-chip:hover {
  background: color-mix(in srgb, var(--accent-blue) 10%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 28%, var(--border));
}
:root[data-style~="liquid"] .onb-chip.selected {
  background: var(--accent-blue);
  border-color: var(--accent-blue);
}

:root[data-style~="liquiddom"] .onb-intro-card {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-md);
}
:root[data-style~="liquiddom"] .onb-intro-card:hover {
  background: color-mix(in srgb, var(--accent-blue) 8%, var(--ld-menu-bg));
  border-color: color-mix(in srgb, var(--accent-blue) 24%, var(--border));
}
:root[data-style~="liquiddom"] .onb-intro-card.expanded {
  border-color: var(--accent-blue);
}

:root[data-style~="liquiddom"] .onb-card,
:root[data-style~="liquiddom"] .onb-section-card,
:root[data-style~="liquiddom"] .onb-vignette-card {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-sm);
}
:root[data-style~="liquiddom"] .onb-card.status-done {
  background: color-mix(in srgb, var(--accent-green, #50c878) 6%, var(--ld-menu-bg));
}
:root[data-style~="liquiddom"] .onb-card.status-in_progress {
  background: color-mix(in srgb, var(--accent-blue) 6%, var(--ld-menu-bg));
}
:root[data-style~="liquiddom"] .onb-card.expanded {
  border-color: color-mix(in srgb, var(--accent-blue) 28%, var(--border));
  background: color-mix(in srgb, var(--accent-blue) 4%, var(--ld-menu-bg));
}
:root[data-style~="liquid"] .onb-card-header:hover {
  background: color-mix(in srgb, var(--accent-blue) 8%, transparent);
}
:root[data-style~="liquid"] .onb-card-body {
  border-top-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}

/* Bubbles — under glass the parent expanded card is already translucent, so
   the bubble needs real opacity + a hairline to read as a distinct surface
   instead of melting into the card behind it. Mirrors the iMessage-style
   radius from .conv-excerpt-bubble. */
:root[data-style~="liquid"] .onb-bubble {
  background: color-mix(in srgb, var(--bg-elevated) 50%, transparent);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  /* Border opacity is driven by --incoming-bubble-border-intensity — matches
     the conversations incoming bubble (styles.css ~5576): the `border`
     shorthand resets all four edges (including the flat-theme 3px accent
     left-stripe) to a single hairline. Do NOT re-add border-left here; the
     colored left-edge is intentionally killed under glass to match
     .conv-excerpt-bubble. */
  border: 1px solid color-mix(
    in srgb,
    color-mix(in srgb, var(--accent-blue) 10%, var(--border))
      calc(clamp(0, var(--incoming-bubble-border-intensity, 20), 200) * 1%),
    transparent
  );
  border-radius: var(--radius-sm);
  padding: 10px 14px;
}
/* Re-assert the capsule under Liquid/Glass — the liquid .onb-bubble rule above
   pins border-radius: var(--radius-sm) at higher specificity than the
   attribute selector, so it would otherwise win. Mirrors the matching
   .conv-excerpt-bubble[data-bubble-shape="single"] liquid override. */
:root[data-style~="liquid"] .onb-bubble[data-bubble-shape="single"] {
  border-radius: var(--radius-pill);
}
/* Solid iMessage-blue "Me" bubble under glass — matches .conv-excerpt-bubble.is-me,
   which stays opaque accent-blue across every theme rather than the translucent
   accent tint the incoming bubble uses. */
:root[data-style~="liquid"] .onb-bubble-user {
  background: var(--accent-blue);
  border: 1px solid var(--accent-blue);
}
:root[data-style~="liquid"] .onb-composer-inline-row {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}
:root[data-style~="liquid"] .onb-composer-inline-row:focus-within {
  border-color: var(--accent-blue);
}

:root[data-style~="liquid"] .onb-add-topic-toggle {
  background: color-mix(in srgb, var(--bg-surface) 50%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 12%, var(--border));
}
:root[data-style~="liquid"] .onb-add-topic-toggle:hover:not([disabled]) {
  background: color-mix(in srgb, var(--accent-blue) 10%, transparent);
  border-color: var(--accent-blue);
}
:root[data-style~="liquiddom"] .onb-add-topic-panel {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-sm);
}
:root[data-style~="liquid"] .onb-add-topic-header {
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
}
:root[data-style~="liquid"] .onb-add-topic-row:hover:not([disabled]) {
  background: color-mix(in srgb, var(--accent-blue) 10%, transparent);
}

/* Clone-from-identity popover — same frosted-glass treatment as the
   add-topic menu so the new Step 1 surface matches the rest of the app under
   the liquid style. The toggle stays plain text (no capsule). */
:root[data-style~="liquiddom"] .onb-clone-panel {
  background: var(--ld-menu-bg);
  backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  -webkit-backdrop-filter: blur(var(--ld-blur)) saturate(var(--ld-saturate));
  border: 1px solid var(--ld-rim);
  border-radius: var(--radius-sm);
}
:root[data-style~="liquid"] .onb-clone-row:hover:not([disabled]) {
  background: color-mix(in srgb, var(--accent-blue) 10%, transparent);
}

:root[data-style~="liquid"] .onb-section-divider {
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
  font-weight: 500;
}
:root[data-style~="liquid"] .onb-section-divider::before,
:root[data-style~="liquid"] .onb-section-divider::after {
  background: color-mix(in srgb, var(--accent-blue) 14%, var(--border));
}

:root[data-style~="liquid"] .onb-vignette-card--error {
  border-color: color-mix(in srgb, var(--accent-red) 24%, var(--border));
}
:root[data-style~="liquid"] .onb-vignette-title {
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
  font-weight: 500;
}

:root[data-style~="liquid"] .onb-demo-pill {
  background: color-mix(in srgb, var(--accent-blue) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent-blue) 35%, var(--border));
  text-transform: none;
  letter-spacing: 0;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root[data-style~="liquid"] .help-section,
  :root[data-style~="liquid"] .discover-card,
  :root[data-style~="liquid"] .discover-toolbar,
  :root[data-style~="liquid"] #wiki-left-panel,
  :root[data-style~="liquid"] .identity-section-card,
  :root[data-style~="liquid"] #view-wiki > .page-header,
  :root[data-style~="liquid"] .wiki-menu-bar,
  :root[data-style~="liquid"] #view-discussions .page-header,
  :root[data-style~="liquid"] #view-discussions .conv-detail-header,
  :root[data-style~="liquid"] #view-discussions .mm-chat-composer,
  :root[data-style~="liquid"] #view-discussions .pill-composer,
  :root[data-style~="liquid"] .account-panel,
  :root[data-style~="liquid"] .billing-card,
  :root[data-style~="liquid"] .onboarding-card,
  :root[data-style~="liquid"] .onboarding-card.onboarding-card-v2,
  :root[data-style~="liquid"] .onb-path-card,
  :root[data-style~="liquid"] .onb-card,
  :root[data-style~="liquid"] .onb-section-card,
  :root[data-style~="liquid"] .onb-vignette-card,
  :root[data-style~="liquid"] .onb-add-topic-panel,
  :root[data-style~="liquid"] .onb-clone-panel { background: var(--bg-card); }
  :root[data-style~="liquid"] #onboarding-overlay { background: rgba(1, 4, 9, 0.7); }
  :root[data-style~="liquid"] .discover-overlay-panel,
  :root[data-style~="liquid"] #wiki-left-panel,
  :root[data-style~="liquid"] #view-discussions .new-chat-meta-row { background: var(--bg-card) !important; }
}

/* ─── Wiki menu-bar capsules ──────────────────────────────────────────────────
   Shared chrome for the wiki center-panel menu bar's two pill controls:
   the page-picker dropdown trigger and the list/graph view toggle. Sibling
   capsules to .top-bar-core-picker / .pill-composer — same --bg-base fill,
   1px --border rim, full pill radius — so they participate in the same glass
   specular rim treatment when data-style~="glass" is active. */

.wiki-page-picker {
  box-sizing: border-box;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  padding: calc(6px + var(--nav-spacing-offset)) 14px;
  outline: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.wiki-view-toggle {
  display: flex;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 2px;
}

/* ─── Glass — specular edge glare ─────────────────────────────────────────────
   The Glass surface style is Liquid PLUS a thin, Apple-style specular highlight
   that traces the rim of circles and capsules. Activated when the root carries
   `data-style="liquid glass"` (token-list selector). A single ::after pseudo
   overlays the host element, painted with a conic-gradient and masked to the
   rim only via `padding: 1px` + `mask-composite: exclude`. The glare layers on
   top of the existing Liquid hairline border, never replaces it.

   Light theme variant: white-on-white disappears, so light-mode glare uses a
   soft accent tint instead of pure white. See :root.light[data-style~="glass"]
   block at the bottom of this section.

   Targets:
   - .conv-list-toggle             (rail toggle circle in conversation header)
   - .wiki-rail-collapse-btn       (desktop wiki rail collapse circle)
   - #wiki-rail-toggle             (mobile wiki rail hamburger circle)
   - .sidebar-core-picker          (sidebar Core picker capsule)
   - .top-bar-core-picker          (mobile top-bar Core picker capsule)
   - .top-bar-search .pill-composer (search trigger — pill that collapses to a
                                     36px circle when not expanded)
   - .wiki-page-picker             (wiki center-panel "Pages — …" dropdown trigger)
   - .wiki-view-toggle             (wiki menu-bar list↔graph segmented pill)
   - .chip                         (selectable filter pills; opted in so dense
                                     chip groups pick up the same raised rim)

   Border treatment: the same selector list (minus .chip--selected, which keeps
   its accent-blue rim) also adopts var(--border-glass) — a slightly lifted
   border color so the rim still reads as "raised" when the glare is dialed
   low via --glass-glare-brightness.

   Implementation note: each host needs `position: relative` (already set on
   .top-bar-search wrappers; we re-assert on the toggle/circle classes to be
   safe). The ::after inherits border-radius from the host so it adapts whether
   the host is a circle (999px) or rounded capsule (--radius-sm). */

:root[data-style~="glass"] .conv-list-toggle,
:root[data-style~="glass"] .wiki-rail-collapse-btn,
:root[data-style~="glass"] #wiki-rail-toggle,
:root[data-style~="glass"] .sidebar-core-picker,
:root[data-style~="glass"] .top-bar-core-picker,
:root[data-style~="glass"] .top-bar-search .pill-composer,
:root[data-style~="glass"] .wiki-page-picker,
:root[data-style~="glass"] .wiki-view-toggle,
:root[data-style~="glass"] .chip,
:root[data-style~="glass"] .btn:not(:disabled),
:root[data-style~="glass"] .add-voice-button,
:root[data-style~="glass"] .new-chat-member-chip,
:root[data-style~="glass"] .conv-list-participants-capsule,
:root[data-style~="glass"] .conv-add-member-btn,
:root[data-style~="glass"] .glass-rim {
  position: relative;
}

/* Glass-rim border color — applied to capsules that carry the glare so the
   border reads as "raised" even at low --glass-glare-brightness. Skipped on
   .chip--selected because that state already paints an accent-blue rim. */
:root[data-style~="glass"] .conv-list-toggle,
:root[data-style~="glass"] .wiki-rail-collapse-btn,
:root[data-style~="glass"] #wiki-rail-toggle,
:root[data-style~="glass"] .sidebar-core-picker,
:root[data-style~="glass"] .top-bar-core-picker,
:root[data-style~="glass"] .wiki-page-picker,
:root[data-style~="glass"] .wiki-view-toggle,
:root[data-style~="glass"] .add-voice-button,
:root[data-style~="glass"] .new-chat-member-chip,
:root[data-style~="glass"] .conv-list-participants-capsule,
:root[data-style~="glass"] .conv-add-member-btn,
:root[data-style~="glass"] .glass-rim,
:root[data-style~="glass"] .chip:not(.chip--selected) {
  border-color: var(--border-glass);
}

:root[data-style~="glass"] .conv-list-toggle::after,
:root[data-style~="glass"] .wiki-rail-collapse-btn::after,
:root[data-style~="glass"] #wiki-rail-toggle::after,
:root[data-style~="glass"] .sidebar-core-picker::after,
:root[data-style~="glass"] .top-bar-core-picker::after,
:root[data-style~="glass"] .top-bar-search .pill-composer::after,
:root[data-style~="glass"] .wiki-page-picker::after,
:root[data-style~="glass"] .wiki-view-toggle::after,
:root[data-style~="glass"] .chip::after,
:root[data-style~="glass"] .btn:not(:disabled)::after,
:root[data-style~="glass"] .add-voice-button::after,
:root[data-style~="glass"] .new-chat-member-chip::after,
:root[data-style~="glass"] .conv-list-participants-capsule::after,
:root[data-style~="glass"] .conv-add-member-btn::after,
:root[data-style~="glass"] .glass-rim::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  pointer-events: none;
  /* Conic-gradient sweeps a bright highlight through the upper-left rim and
     decays to transparent through the lower half. Brightest stop is 315deg
     (upper-left), with a secondary glint at 0deg/45deg (upper-right shoulder)
     to mimic a directional light. */
  background:
    conic-gradient(
      from 0deg,
      rgba(255, 255, 255, 0.55) 0deg,
      rgba(255, 255, 255, 0.25) 45deg,
      rgba(255, 255, 255, 0)    135deg,
      rgba(255, 255, 255, 0)    225deg,
      rgba(255, 255, 255, 0.65) 315deg,
      rgba(255, 255, 255, 0.55) 360deg
    );
  /* Mask to a ring (rim only) via padding-box subtract content-box.
     The `linear-gradient(#000 0 0)` is a solid mask source; the composite
     `exclude` punches out the inner content-box leaving the 1px rim. */
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
          mask-composite: exclude;
  /* Slight blend so the glare feels like reflected light rather than paint. */
  mix-blend-mode: screen;
  /* 0.9 is the calibrated default; --glass-glare-brightness (0–200, default
     100) linearly scales it so the settings popover slider drives intensity
     in both directions. */
  opacity: calc(0.9 * (var(--glass-glare-brightness, 100) / 100));
}

/* Light theme — pure white glare disappears against light surfaces. Swap the
   bright stops for a faint accent-tinted highlight at lower alpha. Keep the
   transparent lower-half stops so the rim still fades into shadow. */
:root.light[data-style~="glass"] .conv-list-toggle::after,
:root.light[data-style~="glass"] .wiki-rail-collapse-btn::after,
:root.light[data-style~="glass"] #wiki-rail-toggle::after,
:root.light[data-style~="glass"] .sidebar-core-picker::after,
:root.light[data-style~="glass"] .top-bar-core-picker::after,
:root.light[data-style~="glass"] .top-bar-search .pill-composer::after,
:root.light[data-style~="glass"] .wiki-page-picker::after,
:root.light[data-style~="glass"] .wiki-view-toggle::after,
:root.light[data-style~="glass"] .chip::after,
:root.light[data-style~="glass"] .btn:not(:disabled)::after,
:root.light[data-style~="glass"] .add-voice-button::after,
:root.light[data-style~="glass"] .new-chat-member-chip::after,
:root.light[data-style~="glass"] .conv-list-participants-capsule::after,
:root.light[data-style~="glass"] .conv-add-member-btn::after,
:root.light[data-style~="glass"] .glass-rim::after {
  background:
    conic-gradient(
      from 0deg,
      color-mix(in oklab, var(--accent-blue), white 30%) 0deg,
      color-mix(in oklab, var(--accent-blue) 70%, transparent) 45deg,
      rgba(0, 0, 0, 0)                                   135deg,
      rgba(0, 0, 0, 0)                                   225deg,
      color-mix(in oklab, var(--accent-blue), white 30%) 315deg,
      color-mix(in oklab, var(--accent-blue), white 30%) 360deg
    );
  /* Bring overall alpha down so the tint stays specular, not chromatic.
     Scaled by --glass-glare-brightness (0–200, default 100) for parity with
     the dark variant. */
  opacity: calc(0.35 * (var(--glass-glare-brightness, 100) / 100));
  mix-blend-mode: normal;
}

/* === Discover revamp === */
/* Self-contained block for the Discover view (cards + toolbar + overlay
   sections). All rules use existing CSS variables; revertable as a unit. */

.discover-card {
  /* Inherits .card padding/background; left border is set inline so the
     facet's own color carries through. Falls back to var(--border) when
     color is null. */
  display: flex;
  flex-direction: column;
  transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
}

.discover-card:hover {
  /* Don't override the inline left border-color — only hint the right edges. */
  border-top-color: var(--text-muted);
  border-right-color: var(--text-muted);
  border-bottom-color: var(--text-muted);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
}

.discover-card-accent {
  /* Reserved hook for future "featured" framing on top of the left border. */
  position: relative;
}

/* ── Discover avatar tiles ──────────────────────────────────────────────────
   Circle + name; clicking opens the full profile overlay (no hover detail).
   Built in components/discover-card.js. */
.discover-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 8px 4px;
  background: none;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  font: inherit;
  color: var(--text-primary);
}
.discover-tile:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }
.discover-tile-avatar {
  transition: transform var(--transition), box-shadow var(--transition);
}
.discover-tile:hover .discover-tile-avatar,
.discover-tile:focus-visible .discover-tile-avatar {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.28);
}
.discover-tile-name {
  max-width: 100%;
  font-size: 0.82rem;
  font-weight: var(--fw-medium);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.discover-toolbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 24px;
  background: var(--bg-base);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.discover-toolbar-search {
  flex: 1;
  min-width: 0;
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 7px 12px;
  font-size: 0.875rem;
  outline: none;
  transition: border-color var(--transition);
  /* Strip iOS Safari's native search affordance + macOS dark form-control. */
  -webkit-appearance: none;
  appearance: none;
}

/* Hide the iOS/macOS Safari "x" clear button — we already have full control
   via the input value; the native button reads as a misaligned grey square
   on dark backgrounds. */
.discover-toolbar-search::-webkit-search-decoration,
.discover-toolbar-search::-webkit-search-cancel-button,
.discover-toolbar-search::-webkit-search-results-button,
.discover-toolbar-search::-webkit-search-results-decoration {
  -webkit-appearance: none;
  display: none;
}

.discover-toolbar-search:focus {
  border-color: var(--accent-blue);
}

.discover-toolbar-sort {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  /* Right pad reserves room for the chevron we draw via background-image,
     since appearance:none strips the native arrow. */
  padding: 7px 30px 7px 10px;
  font-size: 0.85rem;
  cursor: pointer;
  outline: none;
  /* Strip iOS native select styling so the dark theme actually applies. */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--text-secondary) 50%),
    linear-gradient(135deg, var(--text-secondary) 50%, transparent 50%);
  background-position:
    calc(100% - 14px) 50%,
    calc(100% - 9px)  50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.discover-toolbar-sort:focus {
  border-color: var(--accent-blue);
}

@media (max-width: 767px) {
  .discover-toolbar {
    padding: 8px 16px;
    gap: 8px;
  }
  .discover-toolbar-sort {
    /* Slightly tighter on phones where horizontal real estate is precious. */
    padding: 7px 26px 7px 8px;
  }
}

.discover-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-size: 0.72rem;
  line-height: 1.6;
  border-radius: var(--radius-pill, 100px);
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  white-space: nowrap;
}

.discover-chip-muted {
  opacity: 0.85;
  color: var(--text-secondary);
}

.discover-overlay-section {
  display: block;
}

.discover-overlay-prompt {
  text-align: left;
  height: var(--input-height);
  padding: 0 18px;
  box-sizing: border-box;
  font-size: 0.875rem;
  line-height: 1.45;
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
  font-family: inherit;
}

.discover-overlay-prompt:hover {
  border-color: var(--accent-blue);
  background: var(--bg-hover);
}

/* ─── Onboarding revamp (adaptive 3-step Core build) ──────────────────────────
   Replaces the original placeholder shell. The base .onboarding-card,
   .onboarding-header, .onboarding-step-indicator, .onboarding-close,
   .onboarding-body, .onboarding-title styles above stay live; the new layout
   widens the card on desktop so the Step 2 card list and Step 3 section
   preview fit comfortably. */

#onboarding-overlay .onboarding-card.onboarding-card-v2 {
  /* Roomier card for every page EXCEPT the logo landing (intro page 1, which
     gets .onboarding-card--compact to restore the original size). Wider and
     taller so the Step 2 card list, Step 3 preview, and forms get more white
     space around them. min(...) clamps against the viewport so the card can
     never exceed it; width:100% (base rule) + the mobile override below keep
     small screens working. */
  max-width: min(880px, calc(100vw - 32px));
  /* Looser vertical cap than the compact landing, still well short of a
     full-screen takeover. The body has overflow-y:auto so long content scrolls
     inside; this just lets taller pages breathe. */
  max-height: min(880px, calc(100vh - 64px));
}

/* Logo landing (intro page 1) keeps the original compact size — unchanged from
   before the enlarge. */
#onboarding-overlay .onboarding-card.onboarding-card-v2.onboarding-card--compact {
  max-width: 720px;
  max-height: min(720px, calc(100vh - 96px));
}

.onb-subtle {
  margin: 0 0 20px;
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.onb-error {
  margin: 8px 0;
  padding: 8px 10px;
  border: 1px solid var(--accent-red);
  border-radius: var(--radius-sm);
  background: rgba(255, 70, 70, 0.08);
  color: var(--accent-red);
  font-size: 0.85rem;
}
.onb-spacer { flex: 1; }
.onb-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 24px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}

/* ── Intro (pre-onboarding explainer) ── */
/* Concept primer shown once before Step 1. Three-sentence hook, then a grid of
   clickable topic cards that expand inline. Cards are <button> elements so the
   whole surface is keyboard-focusable; text is left-aligned to read as content,
   not a control. Reuses .onboarding-title and .onb-footer from above. */
.onb-intro-hero {
  width: 100%;
  margin: 6px 0 12px;
  display: flex;
  justify-content: center;
}
/* HiddenPrism wordmark (transparent PNG) — full content width (matches the
   hook text + card grid below). */
.onb-intro-hero-img {
  display: block;
  width: 100%;
  height: auto;
}
/* Dev-only drag-resize (shared by the onboarding hero ?ob=intro and the sidebar
   monogram ?dev=1 — see components/dev-resize.js). Native resize handle
   (bottom-right); the bloom stage's ResizeObserver re-fits the canvas live. */
.dev-resizable {
  resize: both;
  overflow: hidden;
  min-width: 140px;
  min-height: 140px;
  max-width: 100%;
  position: relative;
  outline: 1px dashed color-mix(in srgb, var(--accent-blue) 55%, transparent);
  outline-offset: 4px;
}
.dev-resize-readout {
  position: absolute;
  left: 6px;
  bottom: 6px;
  z-index: 2;
  padding: 2px 6px;
  font: 600 11px/1.2 var(--font-mono, ui-monospace, monospace);
  color: var(--text-secondary);
  background: color-mix(in srgb, var(--bg-card) 70%, transparent);
  border-radius: 4px;
  pointer-events: none;          /* never block the resize drag */
  user-select: none;
}
.onb-intro-hook {
  margin: 0 0 22px;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-secondary);
  text-align: center;
}
/* Intro page 1 (landing) — just hero + hook + CTA. Center the content block
   vertically so the lone logo/hook don't sit cramped at the top, and give the
   hook a touch more presence since it's the only copy on the page. */
.onb-intro-landing {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 360px;
}
.onb-intro-landing .onb-intro-hook {
  font-size: 1.02rem;
  /* Breathing room between the hook and the CTA below. */
  margin-bottom: 32px;
}
/* Page 1 only — extra white space above and below the wordmark so the lone
   logo + hook feel airy. Logo keeps its original full-width hero size. */
.onb-intro-landing .onb-intro-hero {
  margin: 28px 0 32px;
}
/* Intro page 2 — "How does it work?" heading sits where the hero+hook were. */
.onb-intro-howto {
  margin: 4px 0 18px;
  font-size: 1.35rem;
  font-weight: 650;
  line-height: 1.3;
  color: var(--text-primary);
  text-align: center;
}
.onb-intro-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}
/* Full-width closing card — spans both columns on its own row. */
.onb-intro-card.is-wide { grid-column: 1 / -1; }
.onb-intro-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  color: var(--text-primary);
  font-family: var(--font-ui);
  cursor: pointer;
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
}
.onb-intro-card:hover {
  background: var(--bg-hover);
  border-color: var(--text-muted);
}
.onb-intro-card.expanded {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 1px var(--accent-blue) inset;
}
.onb-intro-card-head {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 8px;
}
.onb-intro-card-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--accent-blue) 16%, transparent);
  color: var(--accent-blue);
  font-size: 0.72rem;
  font-weight: var(--fw-semibold);
  line-height: 1;
}
.onb-intro-card-emoji { font-size: 1.25rem; line-height: 1; justify-self: start; }
.onb-intro-card-toggle {
  font-size: 1.05rem;
  line-height: 1;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
}
.onb-intro-card-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.onb-intro-card-summary {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--text-secondary);
}
.onb-intro-card-detail {
  /* Collapsed by default; the .expanded card eases it open (max-height +
     opacity transition) so content below slides down — no jump, no rebuild. */
  margin: 0;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  padding-top: 0;
  border-top: 0 solid var(--border);
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--text-muted);
  transition: max-height 240ms ease, opacity 200ms ease,
              padding-top 240ms ease, margin-top 240ms ease;
}
.onb-intro-card.expanded .onb-intro-card-detail {
  max-height: 360px;
  opacity: 1;
  margin-top: 6px;
  padding-top: 10px;
  border-top-width: 1px;
}
/* Read-progress check-circle (topic cards 1–5) */
.onb-intro-card-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--text-muted);
  transition: color 160ms;
}
.onb-intro-card-check.is-checked {
  color: var(--accent-green);
}
/* CTA card highlight — fires when all 5 topics have been read */
.onb-intro-card.is-cta-ready {
  border-color: var(--accent-green);
  box-shadow: 0 0 0 1px var(--accent-green) inset,
              0 0 12px color-mix(in srgb, var(--accent-green) 22%, transparent);
}
.onb-intro-card.is-cta-ready:hover {
  border-color: var(--accent-green);
  background: color-mix(in srgb, var(--accent-green) 6%, var(--bg-card));
}
:root[data-style~="liquid"] .onb-intro-card.is-cta-ready {
  border-color: var(--accent-green);
  box-shadow: 0 0 0 1px var(--accent-green) inset,
              0 0 16px color-mix(in srgb, var(--accent-green) 30%, transparent);
}
:root[data-style~="liquid"] .onb-intro-card.is-cta-ready:hover {
  border-color: var(--accent-green);
  background: color-mix(in srgb, var(--accent-green) 8%, color-mix(in srgb, var(--bg-card) 50%, transparent));
}
@media (max-width: 720px) {
  .onb-intro-grid { grid-template-columns: 1fr; }
}

/* ── Step 1 ── */

.onb-textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.9rem;
  line-height: 1.5;
  resize: vertical;
  /* Fill the full height of the "Write it" pane — the pane is a flex column
     and .onb-paths stretches both cards to equal height, so flex:1 makes the
     textarea grow to match the taller (chips) pane. */
  flex: 1;
  min-height: 120px;
}
.onb-textarea:focus {
  outline: none;
  border-color: var(--accent-blue);
}

/* Step 1 two-pane layout — left pane = freeform write, right pane = topic
   chips. Each pane is a bordered card so the visual parallelism reads as
   "two equal options" instead of "input with filter chips". Stacks at the
   720px breakpoint (the same breakpoint as the onboarding card itself). */
.onb-paths {
  display: flex;
  gap: 16px;
  margin: 8px 0 12px;
  align-items: stretch;
}
.onb-path-card {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}
.onb-path-header {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  font-weight: var(--fw-semibold);
  display: flex;
  align-items: center;
  gap: 4px;
}
.onb-path-icon {
  font-size: 0.95rem;
  opacity: 0.85;
}
@media (max-width: 720px) {
  .onb-paths { flex-direction: column; }
}

.onb-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}
.onb-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.82rem;
  cursor: pointer;
  transition: background 120ms, border-color 120ms, color 120ms;
}
.onb-chip:hover { background: var(--bg-hover); }
.onb-chip.selected {
  background: var(--accent-blue);
  border-color: var(--accent-blue);
  color: #fff;
}
.onb-chip-emoji { font-size: 0.95rem; }

/* Add-your-own topic input — lives below the chip row in Step 1's Tap pane.
   Visual language mirrors .onb-textarea (same border, radius, font) so it
   reads as an input rather than a chip. Pairs with a compact .onb-chip-
   custom-add button that uses the same selected-chip accent color. */
.onb-chip-custom-row {
  display: flex;
  gap: 6px;
  margin-top: 4px;
  align-items: stretch;
}
.onb-chip-custom-input {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  padding: 6px 12px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.82rem;
  line-height: 1.4;
}
.onb-chip-custom-input:focus {
  outline: none;
  border-color: var(--accent-blue);
}
.onb-chip-custom-add {
  padding: 6px 14px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.82rem;
  cursor: pointer;
  transition: background 120ms, border-color 120ms, color 120ms;
}
.onb-chip-custom-add:hover {
  background: var(--accent-blue);
  border-color: var(--accent-blue);
  color: #fff;
}

/* ── Step 2 (cards) ── */

.onb-card-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 16px;
}
.onb-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-base);
  overflow: hidden;
  transition: border-color 120ms;
}
/* Expanded cards return to the base background regardless of status. The
   status tint (in_progress blue / done green) stays visible on collapsed
   cards as a progress signal in the list; once a card is open, the message
   thread inside should read against a clean white surface, not a tinted
   one. Specificity-wise the .expanded rule must override the .status-*
   rules below — declaration order does that here. */
.onb-card.status-done { background: rgba(80, 200, 120, 0.05); }
.onb-card.status-in_progress { background: rgba(91, 141, 239, 0.04); }
.onb-card.expanded { border-color: var(--accent-blue); background: var(--bg-base); }

.onb-card-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  cursor: pointer;
  user-select: none;
}
.onb-card-header:hover { background: var(--bg-hover); }
.onb-card-status {
  display: inline-flex;
  width: 18px;
  height: 18px;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  color: var(--text-muted);
  flex-shrink: 0;
}
.onb-card.status-done .onb-card-status { color: var(--accent-green, #50c878); }
.onb-card.status-in_progress .onb-card-status { color: var(--accent-blue); }
.onb-card-title {
  flex: 1;
  font-size: 0.9rem;
  color: var(--text-primary);
  font-weight: var(--fw-medium, 500);
}
.onb-card-toggle {
  color: var(--text-muted);
  font-size: 0.85rem;
}
/* Compact inline "Done + Save" on the collapsed card header's trailing edge.
   Reuses the .btn .btn-primary blue look, sized small to sit beside the toggle
   without inflating the header row. Shown only when the card is answered but
   not yet done; replaced by the green ✓ badge once marked done. */
.onb-card-done-inline {
  flex-shrink: 0;
  padding: 4px 10px;
  font-size: 0.78rem;
  line-height: 1.2;
}

.onb-card-body {
  padding: 16px 18px 18px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* Step 2 interview messages mirror the facet conversation message anatomy
   (conversations.js): a flex-column message item carrying an avatar + name
   speaker badge ABOVE a .conv-excerpt-bubble-equivalent bubble. iMessage-style
   alignment (interviewer leading / "You" trailing) lives on the .onb-msg
   column so BOTH the badge and the bubble hug the same edge. The bubble itself
   matches .conv-excerpt-bubble: 7px 12px padding, 0.875rem / line-height 1.3,
   radius-sm rounded rect (capsule when single-line), a bg-elevated surface with
   a 3px accent left-border on the incoming (interviewer) side, and a solid
   accent-blue + white-text "You" bubble. Tokens only — no hardcoded colors. */
.onb-msg {
  display: flex;
  flex-direction: column;
  gap: 5px;
  max-width: 80%;
  align-self: flex-start;
  align-items: flex-start;
}
.onb-msg-user {
  align-self: flex-end;
  align-items: flex-end;
}
/* Speaker badge row above the bubble — mirrors the conversations headerRow
   (display:flex; align-items:center; gap:6px). */
.onb-msg-header {
  display: flex;
  align-items: center;
  gap: 6px;
}
.onb-bubble {
  padding: 7px 12px;
  /* Base = rounded rect (radius-sm); single-line bubbles become a capsule via
     data-bubble-shape="single" below — mirrors .conv-excerpt-bubble. The shape
     is attribute-driven (not inline) so the Liquid/Glass radius override can
     re-assert the capsule with matching specificity. */
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border-left: 3px solid var(--accent-blue);
  font-size: 0.875rem;
  line-height: 1.3;
  color: var(--text-primary);
  max-width: 100%;
}
.onb-bubble-user {
  background: var(--accent-blue);
  border-left: 0;
  color: #fff;
}
.onb-bubble-text { white-space: pre-wrap; word-wrap: break-word; color: var(--text-primary); }
.onb-bubble-user .onb-bubble-text { color: #fff; }
/* Single-line interview bubble → capsule, 2+ lines → rounded rect. Driven by
   data-bubble-shape (set in onboarding.js applyOnbBubbleShape after layout) so
   it survives the Liquid/Glass radius override below — mirrors
   .conv-excerpt-bubble[data-bubble-shape] in conversations.js. */
.onb-bubble[data-bubble-shape="single"] {
  border-radius: var(--radius-pill);
}

.onb-composer {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 48px;
}
/* Single-row composer pill — textarea (or input) on the leading edge, an
   action button (Send / Create / etc.) sitting INSIDE the same capsule on
   the trailing edge as a smaller nested pill with ~4px breathing room on
   all sides. Border + focus ring sit on the wrapper, NOT on the input, so
   the two children read as one control. Used by:
     - onboarding step 2 + step 3 composers (.onb-composer-* — original)
     - wiki section-refine, Core/facet chat, "+ New section" Create
     - conversations existing-convo + new-chat footers
     - discover-overlay "Ask anything"
   New surfaces should prefer the generic .pill-composer / .pill-composer-input
   / .pill-composer-action triplet. The .onb-composer-* selectors are kept
   as aliases for the existing onboarding markup. */
.onb-composer-inline-row,
.pill-composer {
  display: flex;
  /* Center-align children so the textarea's single-line text shares the
     pill's vertical center with the Send button. With stretch (flex default)
     the textarea inflates to the pill's height while its text stays glued to
     the content-box top — text floats up, button stays mid-line, baselines
     diverge. Autosize multi-line growth still works: the textarea sets its
     own height, the pill grows with it, and align-items:center keeps the
     button vertically centered against the taller surface. */
  align-items: center;
  /* Floor the pill height so surfaces without a nested action button (e.g.
     the top-bar global search) still read as a 32px capsule. Composers with
     a Send/Create button match this exactly via the button's 24+4+4 margin
     box; the floor is just insurance for the button-less case. */
  min-height: 32px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  /* Base shape is pill (radius gets clamped to height/2 by the browser at
     the single-line ~38px composer height). Surfaces wired to
     components/autosize.js override this when the inner textarea grows
     past 1 line — `.autosize-paint[data-autosize-shape='multi']` drops
     the radius to var(--radius-sm) (the message-bubble radius) so the
     multi-line composer reads as the same rounded rect as the bubbles it
     produces. Static pills (e.g. the wiki "+ New section" Create input,
     which is a single-line <input> with nothing to morph into) keep the
     pill silhouette because they never get the .autosize-paint class.
     Net result: every chat-style composer reads as a pill at rest, and
     only the textareas with auto-grow turn into rounded rects. */
  border-radius: var(--radius-pill);
  transition: border-color 120ms;
}
.onb-composer-inline-row:focus-within,
.pill-composer:focus-within { border-color: var(--accent-blue); }

.onb-composer-input,
.pill-composer-input {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  padding: 4px 12px;
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.85rem;
  line-height: 1.45;
  resize: none;
  /* Capsule = 32px = button height 24 + 4+4 margin. Locking input height
     equal to button margin-box kills the align-self:center offset so the
     visible top/bottom gap equals the trailing margin (both 4px). */
  min-height: 32px;
  max-height: 160px;
  overflow-y: auto;
}
.onb-composer-input:focus,
.pill-composer-input:focus {
  outline: none;
  /* The global textarea:focus rule adds a 3px box-shadow glow. Inside the
     composer pill the textarea has no border-radius, so the shadow draws as
     a square around the inner field while the visible pill is a rounded
     rect. Strip the inner glow — the outer pill's :focus-within border
     color is the active-state cue. */
  box-shadow: none;
}

/* Strip border/bg/radius from any pre-styled input/textarea nested inside
   the pill so the outer capsule is the only visible border. Specificity
   beats .composer-textarea / .input / .textarea base rules. */
.pill-composer > .composer-textarea,
.pill-composer > .input,
.pill-composer > .textarea,
.pill-composer > textarea,
.pill-composer > input {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  flex: 1;
  min-width: 0;
  /* Drop the base 38px floor that .composer-textarea ships with. The pill's
     height is driven by the Send button's margin-box (24+4+4 = 32px); the
     textarea sizes to its own single-line content (~19px) and is vertically
     centered inside the pill by align-items:center on the parent. Result:
     the placeholder/text shares the pill's center with the button. autosize.js
     drives multi-line growth via inline height, unaffected by min-height. */
  min-height: 0;
  padding: 0 12px;
  font-size: 0.85rem;
  line-height: 1.45;
}
.pill-composer > .composer-textarea:focus,
.pill-composer > .input:focus,
.pill-composer > .textarea:focus,
.pill-composer > textarea:focus,
.pill-composer > input:focus {
  border: none;
  box-shadow: none;
  outline: none;
}

/* Mirror of the .pill-composer > X strip rule for onboarding's parallel
   wrapper class. Onboarding composers (.onb-composer-inline-row) hold an
   .onb-composer-input textarea — without this override the textarea kept
   its 32px floor and the text sat glued to the content-box top while the
   centered parent + Send button stayed mid-pill. Result: same baseline
   misalignment the .pill-composer surfaces hit before. */
.onb-composer-inline-row > .composer-textarea,
.onb-composer-inline-row > .input,
.onb-composer-inline-row > .textarea,
.onb-composer-inline-row > textarea,
.onb-composer-inline-row > input,
.onb-composer-inline-row > .onb-composer-input {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  flex: 1;
  min-width: 0;
  min-height: 0;
  padding: 0 12px;
  font-size: 0.85rem;
  line-height: 1.45;
}
.onb-composer-inline-row > .composer-textarea:focus,
.onb-composer-inline-row > .input:focus,
.onb-composer-inline-row > .textarea:focus,
.onb-composer-inline-row > textarea:focus,
.onb-composer-inline-row > input:focus,
.onb-composer-inline-row > .onb-composer-input:focus {
  border: none;
  box-shadow: none;
  outline: none;
}

.onb-composer-send,
.pill-composer-action {
  flex-shrink: 0;
  /* Sized to its own padding so the nested button reads at the capsule-standard
     ~28px height. `align-self: center` is load-bearing — the parent pill
     uses `align-items: stretch`, which would otherwise force the button to
     match the textarea height once the composer grows past one line. Without
     this override the Send button stretches to a tall rectangle as the user
     types into line 2+. Center (not flex-end / flex-start) keeps the button
     vertically centered in the wrapper at every line count — at 1 line it's
     visually centered in the ~36px pill; at 3 lines it stays centered in the
     ~70px rounded rect.
     margin: 5px is load-bearing. With a 28px-tall button + 5+5 = 38px
     margin-box, the button exactly fills the pill's single-line content
     height (var(--input-height) = 38px) — align-self:center adds zero
     centering offset, so the visible gap from button to pill inner edge is
     a uniform 5px on top, bottom, and trailing sides. Drops to 4px and the
     1px center offset gives top gap = 5px but right gap = 4px (visibly
     uneven). Multi-line states (composer grown past 1 row) get extra
     vertical centering space; the trailing gap stays 5px regardless.
     padding: trailing gets a larger value than leading so the label has
     visible breathing room from the capsule's trailing edge (matches the
     additional vertical padding the button carries). Height is unchanged —
     the shorthand keeps vertical at calc(6px + offset). */
  align-self: center;
  margin: 4px;
  height: 24px;
  box-sizing: border-box;
  border-radius: var(--radius-pill);
  padding: 0 14px;
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: var(--fw-medium);
  line-height: 1;
  min-height: 0;
}
.onb-composer-row {
  display: flex;
  gap: 6px;
  /* Trailing-align the action button (Done + Save / Skip) so it sits at the
     trailing edge underneath the composer input. */
  justify-content: flex-end;
  /* Divider between the reply composer and the Done + Save / Skip button —
     same token-based treatment as the bottom .onb-footer (Start over /
     Continue) separator so both read as the same rule. */
  margin-top: 24px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
}

.onb-step2-meta {
  flex: 1;
  margin-top: 16px;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* Topic row — holds the Add-a-topic affordance on the leading edge and the
   "Answer these for me" button on the trailing edge, separated by a flex
   spacer. align-items:flex-start keeps both controls top-aligned even when the
   add-topic menu expands downward. */
.onb-topic-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 8px 0 4px;
}
.onb-topic-row .onb-answer-all-btn {
  margin-left: auto;
  align-self: flex-start;
  flex-shrink: 0;
}

/* Add-topic menu — sits between the Step 2 card list and the meta line.
   Lists Step 1 buckets the user did not pick; tapping one POSTs to
   /api/onboarding/cards/add and the new card auto-expands. */
.onb-add-topic-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 8px 0 4px;
}
.onb-add-topic-toggle {
  align-self: flex-start;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--text-secondary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: background 120ms, color 120ms, border-color 120ms;
}
.onb-add-topic-toggle:hover:not([disabled]) {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.onb-add-topic-toggle[disabled] { opacity: 0.5; cursor: not-allowed; }
.onb-add-topic-toggle.open { color: var(--text-primary); }
.onb-add-topic-icon { font-weight: var(--fw-semibold); }
.onb-add-topic-caret { font-size: 0.7rem; }
.onb-add-topic-panel {
  margin-top: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 280px;
  overflow-y: auto;
}
.onb-add-topic-header {
  padding: 6px 10px 4px;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.onb-add-topic-row {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 8px;
  background: transparent;
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 0.85rem;
  color: var(--text-primary);
  text-align: left;
}
.onb-add-topic-row:hover:not([disabled]) { background: var(--bg-hover); }
.onb-add-topic-row[disabled] { opacity: 0.5; cursor: not-allowed; }

/* ─── Step 1 subhead + clone-from-identity menu ───────────────────────────── */
/* The "two ways" hint leads; the clone-from-identity popover sits trailing. */
.onb-step1-subhead {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.onb-step1-subhead-text { margin: 0; flex: 1 1 auto; }
@media (max-width: 720px) {
  .onb-step1-subhead { flex-direction: column; align-items: stretch; gap: 8px; }
}

.onb-clone-wrap { position: relative; flex: 0 0 auto; }
/* Plain text button — no capsule/pill. Sits on the trailing edge of the
   Step 1 subtitle row. */
.onb-clone-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  font-size: var(--fs-body);
  color: var(--text-secondary);
  background: transparent;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  transition: color 120ms ease;
}
.onb-clone-toggle:hover:not([disabled]) { color: var(--text-primary); }
.onb-clone-toggle[disabled] { opacity: 0.6; cursor: not-allowed; }
.onb-clone-toggle.open { color: var(--text-primary); }
.onb-clone-caret {
  display: inline-flex;
  align-items: center;
  opacity: 0.8;
  transition: transform 160ms ease;
}
.onb-clone-caret.open { transform: rotate(180deg); }

.onb-clone-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 20;
  width: 300px;
  max-width: min(90vw, 340px);
  padding: 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}
@media (max-width: 720px) {
  .onb-clone-panel { right: auto; left: 0; width: 100%; }
}
/* Drop-up + horizontally-centered variant — used by the Step 1 "shortcut" card
   at the bottom of the step, where a downward menu would fall below the fold. */
.onb-clone-panel.drop-up {
  top: auto;
  bottom: calc(100% + 6px);
  right: auto;
  left: 50%;
  transform: translateX(-50%);
}
@media (max-width: 720px) {
  .onb-clone-panel.drop-up { left: 0; transform: none; width: 100%; }
}
.onb-clone-header {
  padding: 6px 10px 8px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-muted);
}
.onb-clone-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 8px 10px;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.onb-clone-row:hover:not([disabled]) { background: var(--bg-hover); }
.onb-clone-row[disabled] { opacity: 0.5; cursor: not-allowed; }
.onb-clone-row-label { font-size: var(--fs-body); font-weight: 600; color: var(--text-primary); }
.onb-clone-row-blurb { font-size: 12px; line-height: 1.35; color: var(--text-muted); }
.onb-add-topic-emoji {
  display: inline-flex;
  width: 18px;
  justify-content: center;
  flex-shrink: 0;
}
.onb-add-topic-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Step 3 (Core preview) ── */

/* Step 2 → Step 3 transition animations. Step 3 chrome (title, subtitle,
   section cards) flips in immediately when the user clicks Continue on
   Step 2, while /api/onboarding/finalize runs in the background. The
   skeleton bodies pulse until the real markdown lands; section bodies
   then fade in (driven by .onb-fade-in being added in JS). */
@keyframes onb-fade-up {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes onb-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes onb-skeleton-pulse {
  0%, 100% { opacity: 0.4; }
  50%      { opacity: 0.7; }
}

.onb-step3 > .onboarding-title { animation: onb-fade-up 320ms ease both; }
.onb-step3 > .onb-subtle        { animation: onb-fade-up 320ms 60ms ease both; }

/* Section cards stagger in — :nth-child offsets each by ~80ms so the
   cascade reads as motion rather than a single block reveal. */
.onb-step3 .onb-section-card { animation: onb-fade-up 320ms ease both; }
.onb-step3 .onb-section-card:nth-child(1) { animation-delay: 120ms; }
.onb-step3 .onb-section-card:nth-child(2) { animation-delay: 200ms; }
.onb-step3 .onb-section-card:nth-child(3) { animation-delay: 280ms; }
.onb-step3 .onb-section-card:nth-child(4) { animation-delay: 360ms; }
.onb-step3 .onb-section-card:nth-child(5) { animation-delay: 440ms; }

/* Body content fades in once the real data arrives. JS tags filled section
   bodies with .onb-fade-in; skeleton bodies use the pulse animation below. */
.onb-section-body.onb-fade-in { animation: onb-fade-in 320ms ease both; }

/* Skeleton body inside a loading section card — fixed height, soft pulse.
   Uses color-mix on the primary text token so it adapts to light/dark. */
.onb-section-body--skeleton {
  min-height: 32px;
  margin-top: 6px;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--text-primary) 10%, transparent),
    color-mix(in srgb, var(--text-primary) 18%, transparent),
    color-mix(in srgb, var(--text-primary) 10%, transparent));
  animation: onb-skeleton-pulse 1400ms ease-in-out infinite;
}

/* ── Step 3 loading indicator (cycling status lines) ──────────────────────────
   Shown while finalize runs. Text-only: the cycling status lines communicate
   progress; no graphic. */
.onb-loading-mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 14px 0 20px;
}
/* Stacked status lines, sequenced by per-line animation-delay (set inline in
   JS). Base opacity 0 so a line is invisible until its keyframe window — this
   is what lets positive animation-delay work without a flash of all lines. */
.onb-loading-lines {
  position: relative;
  height: 1.2em;
  width: 100%;
  text-align: center;
}
.onb-loading-line {
  position: absolute;
  left: 0;
  right: 0;
  margin: 0;
  opacity: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  animation: onb-line-cycle 8s linear infinite;
}
@keyframes onb-line-cycle {
  0%   { opacity: 0; transform: translateY(4px); }
  3%   { opacity: 1; transform: translateY(0); }
  22%  { opacity: 1; transform: translateY(0); }
  25%  { opacity: 0; transform: translateY(-4px); }
  100% { opacity: 0; transform: translateY(-4px); }
}
/* Reduced motion: hold the first line, drop the cycle so nothing
   text-level jitters. */
@media (prefers-reduced-motion: reduce) {
  .onb-loading-line { animation: none; opacity: 0; }
  .onb-loading-line:first-child { opacity: 1; }
}

/* Inline retry row when finalize errors during the skeleton phase. */
.onb-step3-retry-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

/* ── Step 3 vignette card (flag: ONBOARDING_VIGNETTE_ENABLED) ─────────────── */
/* A single narrative card with a hook title + 2-5 sentence body. Replaces
   the section accordion as the default Step 3 surface. Goes through view →
   refine state transitions in onboarding.js. */
.onb-step3-vignette-wrap > .onboarding-title { animation: onb-fade-up 320ms ease both; }
.onb-step3-vignette-wrap > .onb-subtle        { animation: onb-fade-up 320ms 60ms ease both; }
.onb-step3-vignette-wrap .onb-vignette-card   { animation: onb-fade-up 320ms 120ms ease both; }

.onb-vignette-card {
  padding: 24px 28px;
  margin: 20px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, var(--radius-sm));
  background: var(--bg-base);
  display: flex;
  flex-direction: column;
  gap: 12px;
  transition: opacity 180ms ease;
}
.onb-vignette-card--dim {
  opacity: 0.55;
}
.onb-vignette-card--error {
  border-color: color-mix(in srgb, var(--text-primary) 18%, transparent);
}
.onb-vignette-title-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.onb-vignette-bullet {
  font-size: 0.95rem;
  color: var(--text-muted);
  line-height: 1;
}
.onb-vignette-title {
  margin: 0;
  font-size: 0.92rem;
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.onb-vignette-body {
  margin: 0;
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--text-primary);
  white-space: pre-wrap;
}

/* Skeleton state — three pulsing bars stand in for the title + body lines
   so the layout doesn't reflow when the real prose arrives. */
.onb-vignette-card--skeleton .onb-vignette-title-skel,
.onb-vignette-card--skeleton .onb-vignette-body-skel {
  height: 12px;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--text-primary) 10%, transparent),
    color-mix(in srgb, var(--text-primary) 18%, transparent),
    color-mix(in srgb, var(--text-primary) 10%, transparent));
  animation: onb-skeleton-pulse 1400ms ease-in-out infinite;
}
.onb-vignette-card--skeleton .onb-vignette-title-skel { width: 40%; }
.onb-vignette-card--skeleton .onb-vignette-body-skel  { height: 14px; }
.onb-vignette-card--skeleton .onb-vignette-body-skel--short { width: 70%; }

/* Refine composer — textarea + helper line beneath the dimmed vignette. */
.onb-vignette-correction {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0 14px;
}
.onb-vignette-correction-input {
  width: 100%;
  resize: vertical;
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-base);
  color: var(--text-primary);
  line-height: 1.5;
}
.onb-vignette-correction-input:focus {
  outline: none;
  border-color: var(--accent-blue);
}


.onb-core-sections {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 8px 0 12px;
}
.onb-section-card {
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-base);
}
/* Labeled rule between the filled-section group and the collapsed empty
   group. Hidden when either group is empty (controlled in JS). */
.onb-section-divider {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 6px 0 2px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
}
.onb-section-divider::before,
.onb-section-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* Title row used by every section card in read mode — title on the leading
   edge, Chat-to-refine button pinned to the trailing edge. Body (when the
   section is filled) renders below this row. */
.onb-section-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.onb-section-title-row .onb-section-title {
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.onb-section-title-row .onb-section-refine-btn {
  flex-shrink: 0;
}

/* Strict-accordion toggle affordance — the whole title row is the hit-target
   for opening / closing a section card. Cursor is the only treatment on the
   already-open card (where the click is a no-op); collapsed rows still get
   the standard pointer. */
.onb-section-title-row--toggle {
  cursor: pointer;
  user-select: none;
}
.onb-section-card--open .onb-section-title-row--toggle {
  cursor: default;
}
.onb-section-title-row--toggle:focus-visible {
  outline: 2px solid var(--accent, var(--text-primary));
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
.onb-section-chevron {
  display: inline-block;
  flex-shrink: 0;
  width: 12px;
  font-size: 0.7rem;
  line-height: 1;
  color: var(--text-muted);
  transition: transform 160ms ease;
  transform: rotate(0deg);
}
.onb-section-chevron--open {
  transform: rotate(90deg);
}

/* Collapsed-card teaser — single-line preview of the section body, truncated
   with an ellipsis. Empty-state teaser is muted further. */
.onb-section-teaser {
  margin-top: 4px;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.onb-section-teaser--empty {
  color: var(--text-muted);
  font-style: italic;
}

/* Empty / placeholder-only section: same title-row layout, just tighter
   padding (no body below) and muted title color. */
.onb-section-card--empty {
  padding: 8px 12px;
}
.onb-section-card--empty .onb-section-title {
  color: var(--text-secondary);
}
.onb-section-title {
  margin: 0 0 6px;
  font-size: 0.92rem;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.onb-section-body {
  margin-top: 6px;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-primary);
}
.onb-section-body p { margin: 4px 0; }
.onb-section-body ul { margin: 4px 0; padding-left: 20px; }
.onb-section-body li { margin: 2px 0; }
.onb-saving { font-size: 0.78rem; color: var(--text-muted); font-style: italic; }

/* Step 3 per-section refine — chat to refine button (in title row) plus
   chat / preview swap-out. Mirrors the wiki.js identity-section-refine
   pattern but inlined into the onboarding section card. */
.onb-section-refine-btn {
  font-size: 0.78rem;
  transition: background var(--transition), color var(--transition),
              border-color var(--transition), border-radius 160ms ease,
              opacity var(--transition);
}
/* Capsule shape on interactive states — hover, keyboard-focus, mid-click. */
.onb-section-refine-btn:hover:not(:disabled),
.onb-section-refine-btn:focus-visible:not(:disabled),
.onb-section-refine-btn:active:not(:disabled) {
  border-radius: var(--radius-pill);
}
.onb-section-refine {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.onb-section-refine-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
  margin-top: 4px;
}
.onb-section-refine-label--muted { color: var(--text-muted); opacity: 0.8; }
.onb-section-body--original { opacity: 0.6; }

.onb-step3-ctas {
  display: flex;
  gap: 10px;
  margin-top: 32px;
  padding-top: 22px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
  justify-content: flex-end;
}
/* Done button on Step 3 borrows the .btn-ghost hover chrome as its resting
   state so it reads as a real choice next to the primary CTA — without it
   the ghost button looks like inert text. Hover still nudges color via the
   base .btn-ghost:hover rule (no override needed). */
.onb-step3-ctas .onb-step3-done {
  background: var(--bg-hover);
  color: var(--text-primary);
  border-color: var(--border);
}

/* ── Step 3a (facet picker) ── */

.onb-suggestion-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin: 8px 0 4px;
}
.onb-suggestion-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  padding: 12px 14px;
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-ui);
  cursor: pointer;
  gap: 4px;
  transition: border-color 120ms, background 120ms;
}
.onb-suggestion-card:hover { background: var(--bg-hover); border-color: var(--accent-blue); }
.onb-suggestion-card.custom {
  border-style: dashed;
}
.onb-suggestion-title {
  font-size: 0.92rem;
  font-weight: var(--fw-semibold);
}
.onb-suggestion-desc {
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.4;
}

/* ── Facet preview modal form ── */

.onb-facet-preview-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.onb-form-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.onb-form-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.onb-form-input,
.onb-form-textarea,
.onb-form-color {
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.88rem;
  padding: 8px 10px;
  box-sizing: border-box;
  width: 100%;
}
.onb-form-textarea { min-height: 140px; resize: vertical; line-height: 1.5; }
.onb-form-color { padding: 2px; height: 40px; cursor: pointer; }
.onb-form-input:focus,
.onb-form-textarea:focus { outline: none; border-color: var(--accent-blue); }

@media (max-width: 599px) {
  #onboarding-overlay .onboarding-card.onboarding-card-v2,
  #onboarding-overlay .onboarding-card.onboarding-card-v2.onboarding-card--compact { max-width: 100%; }
  .onb-suggestion-grid { grid-template-columns: 1fr; }
  /* Stack the Step 3 CTAs vertically — uniform width, uniform gaps. Visual
     order is reflowed via flex `order`: primary first (thumb-accessible),
     Done in the middle, Start over (destructive) demoted to the bottom.
     DOM order stays unchanged so desktop layout is unaffected. */
  .onb-step3-ctas {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .onb-step3-ctas .btn {
    flex: 1 1 auto;
    width: 100%;
    margin-top: 0;
  }
  .onb-step3-ctas .btn-primary { order: 0; }
  .onb-step3-ctas .btn-ghost   { order: 1; }
  .onb-step3-ctas .btn-danger  { order: 2; }
  /* Desktop's leading-edge spacer is unneeded in the vertical stack. */
  .onb-step3-ctas .onb-spacer { display: none; }
}

/* ─── Core switcher strip ────────────────────────────────────────────────────
   Horizontal chip row rendered in the wiki rail header. One chip per Core
   (max 3). Each chip carries a trailing star that marks/sets the default
   Core. The "Add Core" CTA lives in the New Facet row below the strip. */

.core-switcher-strip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.core-switcher-strip::-webkit-scrollbar { display: none; }

.core-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px 3px 4px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: 0.75rem;
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: border-color var(--transition), background var(--transition);
  white-space: nowrap;
  flex-shrink: 0;
}
.core-chip:hover:not(:disabled):not(.core-chip-active) {
  background: var(--bg-hover);
  border-color: var(--text-secondary);
}
.core-chip:disabled { opacity: 0.5; cursor: not-allowed; }

.core-chip-active {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 1px var(--accent-blue);
  cursor: default;
}

.core-chip-avatar {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.core-chip-initial {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg-hover);
  color: var(--text-secondary);
  font-size: 0.65rem;
  font-weight: var(--fw-semibold);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.core-chip-name {
  max-width: 72px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Trailing star on each core chip — hollow ☆ marks a non-default Core
   (click to set as default), filled ★ marks the current default. Rendered
   as a <span role="button"> because the chip itself is a <button> and
   nested buttons are invalid HTML. */
.core-chip-star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 2px;
  font-size: 0.85rem;
  line-height: 1;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
  transition: color var(--transition), transform var(--transition);
}
.core-chip-star:hover { transform: scale(1.15); }
.core-chip-star:focus-visible {
  outline: 1px solid var(--accent-blue);
  outline-offset: 1px;
  border-radius: 2px;
}
.core-chip-star-active {
  color: #f5b400;
  cursor: default;
}
.core-chip-star-active:hover { transform: none; }

/* New-Core modal tab bar */
.core-modal-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 2px;
}
.core-modal-tab {
  padding: 6px 12px;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 0.82rem;
  cursor: pointer;
  transition: color var(--transition), border-color var(--transition);
  margin-bottom: -1px;
}
.core-modal-tab:hover { color: var(--text-primary); }
.core-modal-tab--active {
  color: var(--text-primary);
  border-bottom-color: var(--accent-blue);
  font-weight: var(--fw-medium);
}

/* ─── Discover inline member picker (Variant B) ──────────────────────────────
   Chip row and picker container live inside the discover overlay panel —
   no new stacking context, no z-index collision with the modal system. */
/* Staged-member capsule under the discover composer — one chip per facet the
   visitor has brought in via the add-voice sheet. Members are added/removed in
   the sheet (components/visitor-add-voice.js, opened in stage mode); this row
   reflects the result and offers a per-chip remove. */
.discover-stage-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  padding: 6px 0;
}

/* Cards at cap that are not yet staged are non-interactive and dimmed. */
.add-voice-card.is-cap-disabled {
  opacity: 0.4;
  pointer-events: none;
  cursor: not-allowed;
}

.add-voice-cap-hint {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-bottom: 6px;
  padding: 4px 8px;
  border-radius: var(--radius-sm, 6px);
  background: var(--bg-elevated);
}

/* ─── Integrations view ──────────────────────────────────────────────────────
   Front-end-only mock. Cards look production-ready; Connect is gated off and
   resolves to a "coming soon" disabled state. See components/integrations.js. */
.integrations-view {
  padding: 24px;
  max-width: 960px;
  font-family: var(--font-ui);
}

.integrations-intro {
  margin-bottom: 24px;
}

.integrations-intro-title {
  margin: 0 0 6px;
  font-size: 1.4rem;
  color: var(--text-primary);
}

.integrations-intro-sub {
  margin: 0;
  max-width: 60ch;
  color: var(--text-muted);
  line-height: 1.5;
}

.integrations-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
  align-items: start;
}

.integrations-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 0.15s, transform 0.15s;
}

.integrations-card:hover {
  border-color: var(--accent-blue);
}

.integrations-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.integrations-card-title {
  margin: 0;
  font-size: 1.1rem;
  color: var(--text-primary);
}

.integrations-pill {
  flex: none;
  padding: 3px 12px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  white-space: nowrap;
}

.integrations-card-tagline {
  margin: 0;
  font-size: 0.92rem;
  color: var(--text-primary);
}

.integrations-card-footer {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: auto;
  padding-top: 4px;
}

.integrations-inline-note {
  font-size: 0.82rem;
  color: var(--text-muted);
}

@media (max-width: 640px) {
  .integrations-view {
    padding: 16px;
  }
  .integrations-grid {
    grid-template-columns: 1fr;
  }
}
