/* 25-typography-surfaces.css — Geist / Space Grotesk, pitch-black canvas,
 * library-grade surfaces, and the focused chat view.
 *
 * Loaded AFTER the inline <style> blocks so it wins over the legacy
 * rules. Loaded after 20-design-system.css so it can refine them.
 *
 * Type system
 *   Geist          → everything (body, UI, chat)
 *   Space Grotesk  → UI headers and titles only
 *   JetBrains Mono → micro-labels, code, data
 *
 * The old stack was DM Serif Display (display) / Syne (body) /
 * JetBrains Mono (mono). DM Serif is a decorative serif — it is dropped.
 */

/* ── 1 · pitch black canvas ──────────────────────────────
 * The app had an animated "photon field": a drifting 80px grid plus
 * four chromatic radial washes on body::before / body::after. That is
 * the "animated mess behind" — it is removed and the canvas becomes
 * flat #000. ─────────────────────────────────────────────── */
body::before,
body::after {
  display: none !important;
  animation: none !important;
}

body,
.nothing-dark,
.nothing-light {
  background: #000000 !important;
}

/* The blueprint registration frame on .app-shell::after is the same
 * idea in miniature — drop it too. */
.app-shell::after { display: none !important; }

/* Kill the ambient accent washes that mode switching drives. */
body.gen-active,
body.gen-reasoning,
body.gen-text {
  --bg-accent-1: transparent;
  --bg-accent-2: transparent;
  --bg-accent-3: transparent;
}

/* ── 2 · type system ─────────────────────────────────────
 * Geist Sans has a single `wght` axis (100–900) and no width axis, so
 * "sharper lines, less weight" is achieved with a lighter real weight
 * (400 rather than 500), negative tracking, and antialiased smoothing
 * which thins the strokes instead of blurring them.
 *
 * Space Grotesk is a geometric grotesque with squarer joins and a
 * larger x-height — it reads sharper than Geist at display sizes, so
 * it carries the headers and titles.
 * ───────────────────────────────────────────────────────── */
:root {
  --display: 'Space Grotesk', 'Geist', system-ui, sans-serif;
  --body: 'Geist', system-ui, -apple-system, sans-serif;
  --font: var(--body);
  --mono: 'JetBrains Mono', ui-monospace, monospace;

  /* Industry-standard chat rhythm.
   * ChatGPT: 1rem/1.5 body, ~1.5rem horizontal bubble padding.
   * Claude: 0.875rem/1.6 body, ~0.875rem bubble padding.
   * We sit between the two: 14px/1.55 reading weight, 10px/14px bubble
   * padding, 16px block spacing. */
  --chat-pad-x: 14px;
  --chat-pad-y: 10px;
  --chat-gap: 14px;
  --chat-block-gap: 10px;
}

body {
  font-family: var(--body) !important;
  font-weight: 400 !important;
  font-variation-settings: "wght" 400;
  letter-spacing: -0.011em !important;
  font-feature-settings: "ss01", "cv01", "tnum";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* Every UI header / title in Space Grotesk. */
.stat-lab,
.ql-title,
.ql-lbl,
.dash-header .stat-lab,
.study-header .stat-lab,
.config-modal-title,
.profile-modal h3,
.confirm-title,
.unlock-title,
.deckstudio-panel-title,
.deckstudio-title,
.page-title,
h1, h2, h3, h4 {
  font-family: var(--display) !important;
  font-variation-settings: normal;
}

h1, h2, h3, h4 {
  letter-spacing: -0.028em !important;
  text-transform: none !important;
  font-weight: 600 !important;
}

/* The response body owns its own heading scale — the library `.h-1` /
 * `.h-2` / `.h-3` / `.h-4` steps. The generic rule above has the same
 * specificity as `.response-content h3` but comes later, so it wins;
 * these restore the body scale. */
.response-content h1, .response-content h2, .response-content h3, .response-content h4,
.streaming-content h1, .streaming-content h2, .streaming-content h3, .streaming-content h4,
.response-streaming h1, .response-streaming h2, .response-streaming h3, .response-streaming h4 {
  text-transform: none !important;
  letter-spacing: -0.01em !important;
}
.response-content h1, .streaming-content h1, .response-streaming h1 {
  font: 700 22px/1.15 var(--body) !important;
  font-weight: 700 !important;
}
.response-content h2, .streaming-content h2, .response-streaming h2 {
  font: 600 16px/1.25 var(--body) !important;
  font-weight: 600 !important;
}
.response-content h3, .streaming-content h3, .response-streaming h3 {
  font: 700 10px/1.2 var(--mono) !important;
  font-weight: 700 !important;
  text-transform: uppercase !important;
  letter-spacing: 0.18em !important;
}
.response-content h4, .streaming-content h4, .response-streaming h4 {
  font: 600 14px/1.25 var(--body) !important;
  font-weight: 600 !important;
}

/* The old code uppercased headings with a wide tracking. The
 * library wants a quiet title, not a shouted one. */
.profile-modal h3,
.config-modal-title,
.unlock-title,
.confirm-title {
  text-transform: none !important;
  letter-spacing: -0.02em !important;
}

/* ── 3 · no gradient text anywhere ──────────────────────
 * The app painted "strong" and reasoning labels with
 * `background-clip: text` over a copper→emerald→amethyst gradient.
 * That gradient is the loudest thing on screen and it fights the
 * library language. Every gradient-text rule is neutralised. */
.msg.bot strong,
.response-content strong,
.streaming-content strong,
.response-streaming strong,
.reasoning-text,
.thinking-live-header .reasoning-text,
.thinking-header,
.resource-summary-card div,
.chat-session-item div,
.plan-card-value,
.stat-value,
.deckstudio-title,
.unlock-title,
.assistant-loader-label {
  background: none !important;
  -webkit-background-clip: initial !important;
  background-clip: initial !important;
  -webkit-text-fill-color: currentColor !important;
  color: inherit;
  filter: none !important;
  text-shadow: none !important;
}

.msg.bot strong,
.response-content strong,
.streaming-content strong,
.response-streaming strong {
  color: var(--c-ink) !important;
  font-weight: 600 !important;
}

.reasoning-text,
.thinking-live-header .reasoning-text {
  color: var(--c-accent) !important;
}

/* ── 4 · modals → library/modal ─────────────────────────
 * library/modal/style.css: flat `rgba(0,0,0,0.9)` panel, 1px
 * --border-2, inset bezel shadow, a plain header / body / footer
 * split, and a scale-in animation. The app's modals had an
 * animated gradient rail on top (the "gradient on the top of
 * modals") and a `linear-gradient` body. Both go. ─────────── */

/* Base modal surface — all modals use this */
.modal,
.config-modal,
.profile-modal,
.confirm-modal,
.settings-modal,
.openrouter-warn-modal,
.fallback-warn-modal,
.html-preview-modal,
.user-editor-modal,
.native-input-modal,
.native-confirm-modal,
.download-filename-modal,
.edit-modal,
.glass-glow {
  background: rgba(0, 0, 0, 0.9) !important;
  border: 1px solid var(--border-2) !important;
  border-radius: 0 !important;
  box-shadow: var(--shadow-inset-bezel), 0 24px 64px rgba(0, 0, 0, 0.6) !important;
  backdrop-filter: blur(20px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(20px) saturate(140%) !important;
}

/* Kill legacy gradient rails & animations on all modal-like surfaces */
.modal::before,
.config-modal::before,
.profile-modal::before,
.confirm-modal::before,
.settings-modal::before,
.openrouter-warn-modal::before,
.fallback-warn-modal::before,
.html-preview-modal::before,
.user-editor-modal::before,
.native-input-modal::before,
.native-confirm-modal::before,
.download-filename-modal::before,
.edit-modal::before,
.glass-glow::before {
  display: none !important;
  animation: none !important;
  content: none !important;
}

/* Modal entry/exit animations — CSS-owned, not anime.js */
.modal,
.config-modal,
.profile-modal,
.confirm-modal,
.settings-modal,
.openrouter-warn-modal,
.fallback-warn-modal,
.html-preview-modal,
.user-editor-modal,
.native-input-modal,
.native-confirm-modal,
.download-filename-modal,
.edit-modal {
  animation: modalIn 280ms var(--ease, cubic-bezier(.2,.8,.2,1)) !important;
}
.modal.is-leaving,
.config-modal.is-leaving,
.profile-modal.is-leaving,
.confirm-modal.is-leaving,
.settings-modal.is-leaving,
.openrouter-warn-modal.is-leaving,
.fallback-warn-modal.is-leaving,
.html-preview-modal.is-leaving,
.user-editor-modal.is-leaving,
.native-input-modal.is-leaving,
.native-confirm-modal.is-leaving,
.download-filename-modal.is-leaving,
.edit-modal.is-leaving {
  animation: modalOut 200ms var(--ease, cubic-bezier(.4,0,.6,1)) forwards !important;
}

@keyframes modalIn {
  from { opacity: 0; transform: translate(-50%, -50%) translateY(16px) scale(0.98); filter: blur(4px); }
  to   { opacity: 1; transform: translate(-50%, -50%) translateY(0) scale(1); filter: blur(0); }
}
@keyframes modalOut {
  to { opacity: 0; transform: translate(-50%, -50%) translateY(8px) scale(0.99); filter: blur(4px); }
}

/* Reduced motion: disable modal animations (keep centering transform) */
@media (prefers-reduced-motion: reduce) {
  .modal, .config-modal, .profile-modal, .confirm-modal, .settings-modal,
  .openrouter-warn-modal, .fallback-warn-modal, .html-preview-modal,
  .user-editor-modal, .native-input-modal, .native-confirm-modal,
  .download-filename-modal, .edit-modal {
    animation: none !important;
    transition: none !important;
    transform: translate(-50%, -50%) !important;
  }
}

/* Focus-within copper glow (library/glass .gs--focus pattern) */
.modal:focus-within,
.config-modal:focus-within,
.profile-modal:focus-within,
.confirm-modal:focus-within,
.settings-modal:focus-within,
.openrouter-warn-modal:focus-within,
.fallback-warn-modal:focus-within,
.html-preview-modal:focus-within,
.user-editor-modal:focus-within,
.native-input-modal:focus-within,
.native-confirm-modal:focus-within,
.download-filename-modal:focus-within,
.edit-modal:focus-within {
  box-shadow:
    var(--shadow-inset-bezel),
    0 0 0 1px var(--c-accent),
    0 0 32px var(--c-accent-glow),
    0 24px 64px rgba(0,0,0,0.6) !important;
}

/* Modal header / body / footer rhythm from library/modal */
.modal-header,
.config-modal-header {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 12px !important;
  padding: 14px 18px !important;
  border-bottom: 1px solid var(--border-2) !important;
  background: linear-gradient(180deg, rgba(255,255,255,0.04), transparent) !important;
  flex-shrink: 0 !important;
}

.modal-title,
.config-modal-title,
.confirm-title,
.settings-modal h2,
.settings-modal h3,
.profile-modal h2,
.profile-modal h3 {
  font: 600 16px/1.25 var(--display) !important;
  letter-spacing: -0.02em !important;
  margin: 0 !important;
  color: var(--c-ink) !important;
}

.modal-body,
.config-options,
.gen-settings-panel {
  padding: 18px !important;
  flex: 1 !important;
  overflow: auto !important;
  min-height: 0 !important;
}

.modal-footer {
  display: flex !important;
  justify-content: flex-end !important;
  gap: 8px !important;
  padding: 12px 18px !important;
  border-top: 1px solid var(--border-2) !important;
  background: rgba(0,0,0,0.3) !important;
  flex-shrink: 0 !important;
}

/* Modal close button — icon-btn style */
.modal-header .icon-btn,
.modal-header .glass-icon-btn,
.config-modal-close,
.config-modal-header .icbtn {
  width: 32px !important;
  height: 32px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  background: rgba(255,255,255,0.02) !important;
  border: 1px solid var(--border-2) !important;
  color: var(--c-ink-dim) !important;
  box-shadow: var(--shadow-inset-light) !important;
  cursor: pointer !important;
  border-radius: 0 !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  transition: background 150ms, border-color 150ms, color 150ms !important;
}
.modal-header .icon-btn:hover,
.modal-header .glass-icon-btn:hover,
.config-modal-close:hover,
.config-modal-header .icbtn:hover {
  color: var(--c-ink) !important;
  border-color: var(--border-accent) !important;
  background: rgba(255,255,255,0.04) !important;
}

/* Modal overlay — consistent backdrop */
.modal-overlay,
.config-overlay,
.confirm-overlay,
.edit-overlay,
.openrouter-warn-overlay,
.fallback-warn-overlay,
.html-preview-overlay,
.native-input-overlay,
.whatsnew-overlay,
.settings-modal-overlay,
.share-modal-overlay,
.plan-comparison-modal-overlay {
  position: fixed !important;
  inset: 0 !important;
  background: rgba(0,0,0,0.6) !important;
  backdrop-filter: blur(4px) !important;
  -webkit-backdrop-filter: blur(4px) !important;
  z-index: var(--z-overlay, 400) !important;
  animation: modalOverlayIn 160ms ease !important;
}
.modal-overlay.is-leaving,
.config-overlay.is-leaving,
.confirm-overlay.is-leaving,
.edit-overlay.is-leaving,
.openrouter-warn-overlay.is-leaving,
.fallback-warn-overlay.is-leaving,
.html-preview-overlay.is-leaving,
.native-input-overlay.is-leaving,
.whatsnew-overlay.is-leaving,
.settings-modal-overlay.is-leaving,
.share-modal-overlay.is-leaving,
.plan-comparison-modal-overlay.is-leaving {
  animation: modalOverlayOut 200ms ease forwards !important;
}

@keyframes modalOverlayIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalOverlayOut { to { opacity: 0; } }

/* Ensure modal content sits above overlay */
.modal,
.config-modal,
.profile-modal,
.confirm-modal,
.settings-modal,
.openrouter-warn-modal,
.fallback-warn-modal,
.html-preview-modal,
.user-editor-modal,
.native-input-modal,
.native-confirm-modal,
.download-filename-modal,
.edit-modal,
.whatsnew-modal,
.share-modal,
.plan-comparison-modal {
  z-index: var(--z-modal, 500) !important;
}

/* ── 5 · dropdowns / menus → library/menu-item ───────────
 * library/menu-item: 7px 12px rows, 12px body weight, a 4px
 * right-slide on hover, copper for the active row, and a
 * right-aligned mono `kbd`. ─────────────────────────────── */
.menu-list,
.dropdown-menu,
.edit-model-dropdown,
.model-dropdown,
.context-menu,
.dropdown-list {
  list-style: none;
  margin: 0;
  padding: 4px;
  background: rgba(10, 10, 14, 0.94) !important;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid var(--border-2) !important;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6) !important;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 200px;
}

.m-item,
.dropdown-item,
.edit-model-dropdown-item,
.model-dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 12px !important;
  font: 500 12px var(--body) !important;
  color: var(--c-ink) !important;
  background: transparent !important;
  border: 0 !important;
  text-align: left;
  width: 100%;
  transform: translateX(0);
  transition: transform 220ms var(--ease), background 150ms, color 150ms !important;
  border-radius: 0 !important;
}
.m-item:hover,
.dropdown-item:hover,
.edit-model-dropdown-item:hover,
.model-dropdown-item:hover {
  background: var(--c-accent-wash) !important;
  transform: translateX(4px);
  color: var(--c-ink) !important;
}
.m-item.is-active,
.dropdown-item.is-active,
.edit-model-dropdown-item.is-active {
  color: var(--c-accent) !important;
  background: rgba(204, 144, 80, 0.18) !important;
}

/* ── 6 · chips → library/chip ─────────────────────────── */
.resource-badge,
.token-badge,
.plan-tag,
.deck-badge,
.chip,
.badge-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px !important;
  font: 700 10px var(--mono) !important;
  letter-spacing: 0.10em !important;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.02) !important;
  border: 1px solid var(--border-2) !important;
  color: var(--c-ink-dim) !important;
  box-shadow: var(--shadow-inset-light);
  border-radius: 0 !important;
}
.resource-badge.brand,
.token-badge.brand,
.chip--brand {
  color: var(--c-accent-bright) !important;
  border-color: var(--border-accent) !important;
  background: var(--c-accent-wash) !important;
}

/* ── 7 · icon button → library/icon-button ──────────────
 * The library icon button is a square bezel with an inset light
 * sweep on hover. ───────────────────────────────────────── */
.icon-btn,
.ql-icbtn,
.deckstudio-icon-btn {
  background: rgba(255, 255, 255, 0.02) !important;
  border: 1px solid var(--border-2) !important;
  color: var(--c-ink-dim) !important;
  box-shadow: var(--shadow-inset-light) !important;
  border-radius: 0 !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
.icon-btn:hover,
.ql-icbtn:hover,
.deckstudio-icon-btn:hover {
  color: var(--c-ink) !important;
  border-color: var(--border-accent) !important;
  background: rgba(255, 255, 255, 0.04) !important;
  box-shadow: var(--shadow-inset-light) !important;
}
.icon-btn .material-symbols-rounded,
.ql-icbtn .material-symbols-rounded { font-size: 17px !important; }

/* ── 8 · no window frame around the model response ───────
 * The app wrapped the assistant's answer in a bordered,
 * gradient-filled "quad-text-surface" panel with an animated
 * rail across the top — a window frame. The response is now
 * bare text on the canvas. ──────────────────────────────── */
.quad-text-surface,
.quad-reasoning-surface {
  border: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  padding: 0 !important;
  margin: 0 !important;
  overflow: visible !important;
}
.quad-text-surface::before,
.quad-reasoning-surface::before {
  display: none !important;
  content: none !important;
  animation: none !important;
}
.quad-mermaid-diagram {
  border: 1px solid var(--border-2) !important;
  background: rgba(255, 255, 255, 0.02) !important;
  box-shadow: none !important;
  padding: 14px;
}

/* ── 9 · reasoning as a rolling text preview ────────────
 * Not an expanding stack of steps. A single line that scrolls
 * like a ticker, with the finished trace kept in the DOM but
 * collapsed. ───────────────────────────────────────────── */
.quad-reasoning-surface {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.quad-reasoning-topline {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--c-accent) !important;
  font: 700 10px/1.2 var(--mono) !important;
  letter-spacing: 0.16em !important;
  text-transform: uppercase;
}
.quad-reasoning-pulse {
  width: 6px;
  height: 6px;
  background: var(--c-accent);
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: none;
  animation: qlPulse 1.4s var(--ease) infinite;
}
@keyframes qlPulse {
  0%, 100% { opacity: 0.35; transform: scale(0.8); }
  50%      { opacity: 1;    transform: scale(1); }
}
.quad-reasoning-status {
  margin-left: auto;
  color: var(--c-ink-muted);
  font-size: 9px;
  letter-spacing: 0.16em;
}

/* The rolling preview: one line, clipped, scrolling. */
.quad-reasoning-roll {
  position: relative;
  height: 18px;
  overflow: hidden;
  display: flex;
  align-items: center;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 14px, #000 calc(100% - 24px), transparent 100%);
}
.quad-reasoning-roll-inner {
  display: inline-flex;
  gap: 20px;
  white-space: nowrap;
  will-change: transform;
  animation: qlRoll 18s linear infinite;
  color: var(--c-ink-dim);
  font: 500 12px/1.5 var(--body);
}
.quad-reasoning-roll-inner > span { flex: 0 0 auto; }
.quad-reasoning-roll-inner > span::before {
  content: "·";
  color: var(--c-accent);
  margin-right: 20px;
}
@keyframes qlRoll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.quad-reasoning-surface.is-idle .quad-reasoning-roll-inner {
  animation-play-state: paused;
  color: var(--c-ink-muted);
}

/* The full trace stays available behind a disclosure — but the
 * disclosure is GONE. The reasoning is never expandable and never
 * expanded: the live frame shows a rolling one-line preview, and the
 * finished frame shows "Reasoned — 4.2s". The trace stays in the DOM
 * so it is not lost, but it is not shown. */
.quad-reasoning-steps {
  display: grid;
  gap: 4px;
  margin-top: 0;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 260ms var(--ease), opacity 200ms var(--ease), margin-top 260ms var(--ease);
}
.quad-reasoning-surface.is-open .quad-reasoning-steps {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  margin-top: 0;
}
.quad-reasoning-step {
  padding-left: 10px;
  border-left: 2px solid var(--border-accent);
  color: var(--c-ink-dim);
  font: 400 12px/1.5 var(--body);
}
.quad-reasoning-step.current {
  border-left-color: var(--c-accent);
  color: var(--c-ink);
}

/* ── 9b · flat reasoning header ──────────────────────────
 * The reasoning header is no longer a disclosure. It reads
 * "Reasoned — 4.2s" and nothing else: no chevron, no
 * "DONE", no second copy of the trace to open. The pulse
 * dot becomes a static copper square once the reasoning is
 * finished. ──────────────────────────────────────────── */
.quad-reasoning-flat {
  border-color: var(--border-2) !important;
  background: rgba(255, 255, 255, 0.015) !important;
  box-shadow: var(--shadow-inset-light) !important;
}
.quad-reasoning-flat::before {
  display: none !important;
}
.quad-reasoning-head {
  cursor: default !important;
  pointer-events: none;
}
.quad-reasoning-pulse--done {
  animation: none !important;
  opacity: 1;
  transform: scale(1);
  border-radius: 0;
  width: 6px;
  height: 6px;
  background: var(--c-accent);
  box-shadow: none;
}
.quad-reasoning-status--flat {
  color: var(--c-ink-dim) !important;
  font: 700 9px var(--mono) !important;
  letter-spacing: 0.14em !important;
  margin-left: auto;
}
/* The flat header is a STATUS LINE, not a disclosure. The full
 * trace stays collapsed — permanently. It is not expandable, and it
 * is not expanded by default either: the reasoning is summarised by
 * the header ("Reasoned — 4.2s") and nothing else. The trace is
 * kept in the DOM so it is never lost, but it is never shown. */
.quad-reasoning-flat .quad-reasoning-steps,
.quad-reasoning-flat .thinking-steps {
  max-height: 0 !important;
  opacity: 0 !important;
  overflow: hidden !important;
  margin-top: 0 !important;
  padding: 0 !important;
  border: 0 !important;
}
/* The live clock — the elapsed reasoning time. */
.quad-reasoning-clock {
  color: var(--c-ink-dim) !important;
  font: 700 9px var(--mono) !important;
  letter-spacing: 0.1em !important;
  font-variant-numeric: tabular-nums;
}

/* ── 10 · fluid text streaming ──────────────────────────
 * Tokens fade + rise in as they land, rather than the whole
 * block popping. Each text node is wrapped in a span that
 * animates once. ────────────────────────────────────────── */
.ql-stream-token {
  display: inline;
  animation: qlTokenIn 320ms var(--ease) both;
}
@keyframes qlTokenIn {
  from { opacity: 0; filter: blur(2px); }
  to   { opacity: 1; filter: blur(0); }
}

.typing-cursor {
  display: inline-block;
  width: 7px;
  height: 1em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: var(--c-accent);
  color: transparent !important;
  text-shadow: none !important;
  font-size: 1em;
  line-height: 1;
  animation: qlCaret 900ms steps(2, start) infinite;
}
@keyframes qlCaret {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0; }
}

/* The old cursor was a yellow '█' with a glow. */
.typing-cursor::after { content: none !important; }

/* ── 11 · focused chat view ─────────────────────────────
 * On send, the thread collapses into a focused view: the user message
 * pinned to the top right (clamped to 4 lines), the assistant response
 * anchored to the bottom, growing upward. Older messages in the
 * thread are hidden so the exchange reads as a single unit.
 *
 * The focus moves ONE EXCHANGE AT A TIME. There is no separate thread
 * view to switch to: pulling up past the top of the answer steps the
 * focus back one exchange, pulling down steps it forward. The travel
 * is animated with the spring curve, so the view overshoots and
 * settles instead of snapping.
 *
 * For the transition the outgoing exchange is lifted out of the flow
 * (it keeps its focused classes, so it does not change appearance)
 * while the incoming one springs into its place. That is why the
 * list is `position: relative` — the lifted element is positioned
 * against the list's padding box.
 * ───────────────────────────────────────────────────────── */
.chat-list.is-focused {
  justify-content: flex-start;
  overflow-anchor: none;
  /* The lifted (outgoing) exchange is absolutely positioned against
   * this box. */
  position: relative;
}

/* ── Gutter arrows — desktop only ──
 *
 * On desktop the focused exchange sits between two side gutters.
 * Each gutter carries an arrow that lets the user step one exchange
 * back / forward without touching the answer itself. The gutters are
 * hidden on mobile (horizontal swiping is used instead).
 */
.chat-list.is-focused .focus-gutter {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 80px;
  display: grid;
  place-items: center;
  z-index: 3;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 150ms, background 150ms;
  user-select: none;
  -webkit-user-select: none;
}
.chat-list.is-focused .focus-gutter:hover {
  opacity: 0.9;
  background: rgba(255,255,255,0.04);
}
.chat-list.is-focused .focus-gutter.left { left: 0; }
.chat-list.is-focused .focus-gutter.right { right: 0; }
.chat-list.is-focused .focus-gutter svg {
  width: 20px; height: 20px; fill: none; stroke: var(--c-ink); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.8;
}
.chat-list.is-focused .focus-gutter.left svg { transform: rotate(90deg); }
.chat-list.is-focused .focus-gutter.right svg { transform: rotate(-90deg); }

/* Hide gutters on mobile — horizontal swiping is used instead. */
@media (max-width: 768px) {
  .chat-list.is-focused .focus-gutter { display: none !important; }
}

/* Everything before the focused exchange is out of the way. */
.chat-list.is-focused > .msg:not(.is-focused-user):not(.is-focused-bot) {
  display: none !important;
}

/* The focused pair is animated with the Web Animations API, so the
 * browser needs to know it will be transformed. */
.msg.is-focused-user,
.msg.is-focused-bot {
  will-change: transform, opacity;
}

/* While an exchange is being lifted out of the flow it must not
 * intercept clicks. */
.msg[data-focus-lift] {
  pointer-events: none;
}

.msg.is-focused-user {
  order: -1;
  align-self: flex-end;
  max-width: min(72%, 720px);
  margin-bottom: auto;
  flex: 0 0 auto;
  cursor: pointer;
  transition: background 150ms, border-color 150ms;
}
/* The clamp is a max-height, not `-webkit-line-clamp`.
 *
 * Chromium blockifies `display: -webkit-box` to `flow-root` in several
 * situations (it is the legacy flexbox layout, and the blockification
 * rules bite as soon as the element is a flex/grid item or has certain
 * overflow values). The blockified box ignores `-webkit-line-clamp`, so
 * the text never truncates.
 *
 * A max-height in `lh` units gives the same 4-line result, works in
 * every engine, and the JS measures the unclamped height the same way
 * (it lifts the clamp with `max-height: none`). */
/* The clamp is a plain block with a fixed height.
 *
 * `display: -webkit-box` is the legacy flexbox, and Chromium
 * blockifies it to `flow-root` in most contexts — a blockified box
 * ignores `-webkit-line-clamp`, and with `overflow: hidden` it can
 * collapse to zero height. A plain block with a height set from the
 * real line height behaves identically in every engine. */
.msg.is-focused-user .msg-clamp {
  display: block;
  /* The exact height is set by QUAD.Focus in JS, because it has to
   * track the element's real line height (the webfont's metrics differ
   * from the fallback's). This is the pre-webfont default.
   *
   * The height only applies to a message that OVERFLOWS — a short one
   * must not get a four-line box with empty space under it. QUAD.Focus
   * sets the inline style to `auto` / `none` for the short case, and
   * this rule is the fallback for the pre-webfont moment. */
  height: calc(var(--clamp-lh, 24px) * 4);
  max-height: calc(var(--clamp-lh, 24px) * 4);
  overflow: hidden;
  word-break: break-word;
  overscroll-behavior: contain;
}
/* A message that fits has no clamp at all. */
.msg.is-focused-user:not(.is-clamped) .msg-clamp {
  height: auto;
  max-height: none;
}
.msg.is-focused-user.is-expanded .msg-clamp {
  overflow: auto;
}
.msg.is-focused-user .msg-expand-hint {
  display: none;
  margin-top: 4px;
  font: 600 9px var(--mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-accent);
  opacity: 0.75;
}
/* The hint shows only while the message is clamped AND collapsed. */
.msg.is-focused-user.is-clamped:not(.is-expanded) .msg-expand-hint { display: block; }

.msg.is-focused-bot {
  order: 1;
  align-self: stretch;
  width: 100%;
  max-width: none;
  /* `margin-top: auto` pushes the answer to the bottom of the column
   * while the user's message stays at the top. */
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  border-left: 0 !important;
  margin-left: 0 !important;
  box-shadow: none !important;
}

/* An exchange can hold several bot elements — the answer, the MaGE
 * inline block, an artefact card. They belong to the SAME exchange, so
 * they stack in it: only the FIRST one takes the auto margin that
 * pushes the group to the bottom, and the rest follow it directly.
 * Without this the second element starts a new row of its own and
 * the exchange comes apart. */
.chat-list.is-focused .msg.is-focused-bot ~ .msg.is-focused-bot {
  margin-top: 0;
  padding-top: 12px;
  border-top: none;
}

/* The MaGE block and the artefact card are part of the answer, not a
 * separate message — they get no border of their own. */
.chat-list.is-focused .msg.is-focused-bot.mage-inline,
.chat-list.is-focused .msg.is-focused-bot:has(.ql-file-bar),
.chat-list.is-focused .msg.is-focused-bot:has(.chat-card-preview) {
  padding-top: 12px;
  border-top: none;
  margin-top: 0;
}

/* The focused view needs the user message to stay visible even
 * when the response is long. */
.chat-list.is-focused {
  display: flex;
  flex-direction: column;
}

/* In the focused view the response is its own scroll region so a long
 * answer scrolls without dragging the thread. */
.chat-list.is-focused .msg.is-focused-bot .response-content,
.chat-list.is-focused .msg.is-focused-bot .streaming-content,
.chat-list.is-focused .msg.is-focused-bot .response-streaming {
  max-height: none;
  overflow-y: visible;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-3) transparent;
  padding-right: 4px;
}
.chat-list.is-focused .msg.is-focused-bot .response-content::-webkit-scrollbar,
.chat-list.is-focused .msg.is-focused-bot .streaming-content::-webkit-scrollbar,
.chat-list.is-focused .msg.is-focused-bot .response-streaming::-webkit-scrollbar { width: 6px; }
.chat-list.is-focused .msg.is-focused-bot .response-content::-webkit-scrollbar-thumb,
.chat-list.is-focused .msg.is-focused-bot .streaming-content::-webkit-scrollbar-thumb,
.chat-list.is-focused .msg.is-focused-bot .response-streaming::-webkit-scrollbar-thumb {
  background: var(--border-2);
}
.chat-list.is-focused .msg.is-focused-bot .response-content::-webkit-scrollbar-thumb:hover,
.chat-list.is-focused .msg.is-focused-bot .streaming-content::-webkit-scrollbar-thumb:hover,
.chat-list.is-focused .msg.is-focused-bot .response-streaming::-webkit-scrollbar-thumb:hover {
  background: var(--border-accent);
}

.chat-view .home-chat-input-wrap,
.chat-view .composer-dock-wrap {
  position: absolute !important;
  bottom: 12px !important;
  left: 50% !important;
  transform: translateX(-50%) !important;
  width: min(920px, calc(100% - 24px)) !important;
  max-width: calc(100% - 24px) !important;
  z-index: 6 !important;
  pointer-events: none !important;
  padding: 0 !important;
  margin: 0 !important;
}
.chat-view .home-chat-input-wrap .chat-input-bar,
.chat-view .composer-dock-wrap .quad-composer,
.chat-view .composer-dock-wrap .composer-attachments,
.home-view .home-chat-input-wrap .chat-input-bar,
.home-view .composer-dock-wrap .quad-composer,
.home-view .composer-dock-wrap .composer-attachments,
.composer-dock-wrap > * {
  pointer-events: auto !important;
}
.chat-input-bar, .quad-composer.composer-dock {
  align-items: flex-end !important;
  gap: 10px !important;
  background: transparent !important;
  isolation: auto !important;
  transform: none !important;
  filter: none !important;
  contain: none !important;
}
.composer-input-shell, .composer-attachments, .composer-sq, .composer-input {
  transform: none !important;
  filter: none !important;
  will-change: auto !important;
  opacity: 1 !important;
}
.composer-input-shell {
  flex: 1 1 auto !important;
  min-width: 0 !important;
  align-self: stretch !important;
  display: flex !important;
  align-items: flex-end !important;
}
.composer-input,
#chat-input.composer-input,
#home-quick-chat.composer-input,
textarea.composer-input {
  display: block !important;
  width: 100% !important;
  min-height: 46px !important;
  max-height: 180px !important;
  height: auto !important;
  padding: 13px 16px !important;
  line-height: 20px !important;
  resize: none !important;
  overflow-y: auto !important;
  background: rgba(14,14,18,0.55) !important;
  border: 1px solid rgba(204,144,80,0.22) !important;
  border-radius: 0 !important;
  color: var(--c-ink, var(--text, #ece6db)) !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
  box-shadow: 0 8px 28px rgba(0,0,0,.28) !important;
  outline: none !important;
}
.composer-input:focus, #chat-input.composer-input:focus, #home-quick-chat.composer-input:focus {
  border-color: rgba(255,255,255,0.18) !important;
  background: rgba(18,18,24,0.62) !important;
  box-shadow: 0 8px 28px rgba(0,0,0,.28) !important;
}
.chat-input-bar .composer-sq.snd,
.home-quick-chat-wrap .snd.composer-sq,
.home-quick-chat-wrap .composer-sq.icbtn.more-btn,
#home-more-btn.icbtn.composer-sq,
#more-btn.icbtn.composer-sq {
  width: 46px !important;
  height: 46px !important;
  min-width: 46px !important;
  flex: 0 0 46px !important;
  align-self: flex-end !important;
  background: rgba(14,14,18,0.55) !important;
  border: 1px solid rgba(204,144,80,0.22) !important;
  border-radius: 0 !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
  box-shadow: 0 8px 28px rgba(0,0,0,.28) !important;
  color: var(--c-ink, var(--text, #ece6db)) !important;
}
.composer-sq,
.composer-sq.icbtn,
.composer-sq.more-btn,
.composer-sq.snd,
#more-btn.composer-sq,
#home-more-btn.composer-sq,
#send-btn.composer-sq,
#home-send-btn.composer-sq {
  width: 46px !important;
  height: 46px !important;
  min-width: 46px !important;
  flex: 0 0 46px !important;
  align-self: flex-end !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  border-radius: 0 !important;
  background: rgba(14,14,18,0.55) !important;
  border: 1px solid rgba(204,144,80,0.22) !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
  box-shadow: 0 8px 28px rgba(0,0,0,.28) !important;
  color: var(--c-ink, var(--text, #ece6db)) !important;
  padding: 0 !important;
  transform: none;
}
.composer-sq:hover, .composer-sq.icbtn:hover, .composer-sq.more-btn:hover, .composer-sq.snd:hover { border-color: rgba(204,144,80,0.42) !important; }
.composer-sq.more-btn.active { color: var(--accent, #cc9050) !important; }
.chat-list {
  padding: 16px 20px calc(150px + env(safe-area-inset-bottom, 0px)) !important;
  gap: var(--chat-gap) !important;
  scroll-padding-bottom: 190px;
}
@media (min-width: 769px) {
  .chat-list {
    padding-left: clamp(28px, 7vw, 150px) !important;
    padding-right: clamp(28px, 7vw, 150px) !important;
  }
}

.msg {
  padding: var(--chat-pad-y) var(--chat-pad-x) !important;
  max-width: min(76%, 720px);
}
/* The focused answer spans the full width. */
.chat-list.is-focused .msg.is-focused-bot {
  max-width: none;
  width: 100%;
}
.msg.user { padding: 8px 12px !important; }
.msg.bot {
  padding: 0 0 18px 14px !important;
  border-left: 1px solid var(--border-2) !important;
  box-shadow: none !important;
}
.msg.bot .message-actions { margin-bottom: 4px; }

.response-content,
.streaming-content,
.response-streaming {
  line-height: 1.6 !important;
}
.response-content p,
.streaming-content p,
.response-streaming p { margin: 0 0 var(--chat-block-gap) !important; }
.response-content h1, .response-content h2, .response-content h3, .response-content h4,
.streaming-content h1, .streaming-content h2, .streaming-content h3, .streaming-content h4,
.response-streaming h1, .response-streaming h2, .response-streaming h3, .response-streaming h4 {
  margin: 16px 0 6px !important;
}

/* ── 13 · library underline / emphasis ─────────────────── */
.response-content u,
.response-content ins {
  text-decoration: underline solid var(--c-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.response-content mark {
  background: var(--c-accent-wash);
  color: var(--c-ink);
  padding: 0 2px;
}

/* ── 14 · home screen quote → Space Grotesk ─────────────
 * The headline is the loudest type on the page. Space Grotesk's
 * squared terminals and tight apertures stay crisp at 60px, where
 * Geist's softer curves start to blur.
 * ───────────────────────────────────────────────────────── */
.quad-headline-text {
  font-family: var(--display) !important;
  font-weight: 700 !important;
  font-variation-settings: normal;
  letter-spacing: -0.035em !important;
  line-height: 1.04 !important;
  color: var(--c-ink) !important;
  text-wrap: balance;
}
/* The gradient rule under the quote becomes a static copper hair. */
.quad-headline-text::after {
  background: linear-gradient(90deg, var(--c-accent), rgba(204, 144, 80, 0.25)) !important;
  background-size: 100% 100% !important;
  animation: none !important;
  box-shadow: none !important;
  height: 2px !important;
  width: 72px !important;
  margin-top: 18px !important;
}
/* The quote's emphasis must not use gradient text. */
.quad-headline-text strong {
  background: none !important;
  -webkit-background-clip: initial !important;
  background-clip: initial !important;
  -webkit-text-fill-color: currentColor !important;
  color: var(--c-ink) !important;
  font-weight: 700 !important;
}
.quad-headline-text em {
  color: var(--c-accent) !important;
  font-style: italic;
}

/* ── 15 · lighter reading weight ───────────────────────
 * The app read at 500 weight. 400 is closer to how the reference
 * chat UIs set body copy, and with the negative tracking it keeps
 * the strokes sharp rather than thick.
 * ───────────────────────────────────────────────────────── */
.response-content,
.streaming-content,
.response-streaming,
.ql-body {
  font-weight: 400 !important;
  font-variation-settings: "wght" 400;
}

.msg.bot,
.msg.user {
  font-weight: 400 !important;
}

.chat-input-bar textarea,
#chat-input,
.home-chat-input-wrap textarea,
.composer-input {
  font-weight: 400 !important;
  letter-spacing: -0.008em !important;
}

/* Micro-labels stay mono but lose a little weight so they read as
 * annotations rather than shouting. */
.ql-lbl,
.stat-lab,
.ql-title,
.side-tab-label,
.config-group-label {
  font-weight: 600 !important;
}

/* ── 16 · toggle → library/toggle ───────────────────────
 * The app used a rounded iOS-style switch (10px radius, a filled
 * track). The library switch is a square glass bezel with an internal
 * LED that lights when the state is on.
 * ───────────────────────────────────────────────────────── */
.tgl {
  -webkit-appearance: none;
  appearance: none;
  width: 48px;
  height: 24px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border-2);
  position: relative;
  cursor: pointer;
  touch-action: none;
  box-shadow: var(--shadow-inset-light);
  transition: background 150ms, border-color 150ms;
  outline: none;
  flex-shrink: 0;
  border-radius: 0;
}
.tgl::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 3px;
  width: 16px;
  height: 16px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.02) 100%), rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.20);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.30), 0 1px 2px rgba(0, 0, 0, 0.4);
  transition: transform 220ms var(--ease), background 150ms, box-shadow 150ms;
}
.tgl[aria-checked="true"],
.tgl:checked {
  background: var(--c-accent-wash);
  border-color: var(--c-accent);
}
.tgl[aria-checked="true"]::after,
.tgl:checked::after {
  transform: translateX(24px);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0.04) 100%), rgba(255, 200, 150, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.50), inset 0 0 6px rgba(255, 255, 255, 0.7),
              0 0 8px rgba(255, 255, 255, 0.45), 0 0 16px rgba(204, 144, 80, 0.32);
}
.tgl[aria-checked="true"]::before,
.tgl:checked::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 4px rgba(255, 255, 255, 0.8);
  transform: translate(8px, -50%);
  animation: qlLedOn 1.6s ease-in-out infinite;
}
@keyframes qlLedOn {
  0%, 100% { opacity: 0.6; }
  50%      { opacity: 1; }
}

/* The app's own `.switch` / `.slider` become the library switch. */
.switch {
  position: relative;
  display: inline-block;
  width: 48px;
  height: 24px;
  flex-shrink: 0;
  vertical-align: middle;
}
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border-2);
  transition: background 150ms, border-color 150ms;
  border-radius: 0;
  box-shadow: var(--shadow-inset-light);
}
.switch .slider::before {
  content: "";
  position: absolute;
  height: 16px;
  width: 16px;
  left: 3px;
  top: 3px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.02) 100%), rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.20);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.30), 0 1px 2px rgba(0, 0, 0, 0.4);
  transition: transform 220ms var(--ease), background 150ms, box-shadow 150ms;
  border-radius: 0;
}
.switch input:checked + .slider {
  background: var(--c-accent-wash);
  border-color: var(--c-accent);
}
.switch input:checked + .slider::before {
  transform: translateX(24px);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0.04) 100%), rgba(255, 200, 150, 0.18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.50), inset 0 0 6px rgba(255, 255, 255, 0.7),
              0 0 8px rgba(255, 255, 255, 0.45), 0 0 16px rgba(204, 144, 80, 0.32);
}

/* ── 17 · checkbox → library/toggle (hardware bezel) ──── */
.chk {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font: 400 14px var(--body);
  color: var(--c-ink-dim);
}
.chk input { position: absolute; opacity: 0; pointer-events: none; }
.chk-box {
  width: 20px;
  height: 20px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border-2);
  position: relative;
  transition: background 150ms, border-color 150ms, box-shadow 150ms;
  box-shadow: var(--shadow-inset-deep);
  flex-shrink: 0;
}
.chk:hover .chk-box { border-color: var(--border-3); }
.chk input:checked + .chk-box {
  background: var(--c-accent);
  border-color: var(--c-accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.30), 0 0 0 1px var(--c-accent), 0 0 12px var(--c-accent-glow);
}
.chk-box::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 2px;
  width: 6px;
  height: 10px;
  border-right: 2px solid #000;
  border-bottom: 2px solid #000;
  transform: rotate(45deg) scale(0);
  transform-origin: center;
  transition: transform 220ms var(--spring);
}
.chk input:checked + .chk-box::after { transform: rotate(45deg) scale(1); }

/* ── 18 · LED dot → library/toggle ────────────────────── */
.led {
  display: inline-grid;
  place-items: center;
  cursor: pointer;
}
.led input { position: absolute; opacity: 0; }
.led-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: rgba(80, 40, 15, 0.6);
  border: 1px solid rgba(80, 40, 15, 0.8);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transition: background 220ms, box-shadow 220ms;
}
.led input:checked ~ .led-dot,
.led input:checked + .led-dot {
  background: var(--c-accent-led);
  border-color: rgba(255, 138, 61, 0.4);
  box-shadow: 0 0 8px var(--c-accent-led), 0 0 16px rgba(255, 138, 61, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

/* ── 19 · icon → library/icons ─────────────────────────
 * The library draws its own icons as 1.5px-stroke SVGs on a 24×24
 * viewBox. The app uses Material Symbols, which is heavier. The
 * Material set stays (it is a large icon vocabulary and swapping it
 * wholesale would break every call site), but the optical weight is
 * matched to the library: thinner, dimmer, and square-capped.
 * ───────────────────────────────────────────────────────── */
.material-symbols-rounded {
  font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 20;
  font-size: 18px;
  width: 18px;
  height: 18px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--c-ink-dim);
}
.material-symbols-rounded:hover { color: var(--c-ink); }

/* The library icon is 22px at 1.5px stroke. */
.ic {
  width: 22px;
  height: 22px;
  color: var(--c-ink-dim);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
}

/* ── 20 · dropdown → library/menu-item ───────────────────
 * The app's menu was a "window-like spec panel": a warm gradient,
 * uppercase 11px rows with a hairline separator, 0.8px tracking.
 * The library menu is a flat list — 12px body weight, a 4px
 * right-slide on hover, no separators, copper for the active row.
 * ───────────────────────────────────────────────────────── */
.menu-dropdown,
.dropdown-menu,
.edit-model-dropdown {
  min-width: 200px !important;
  padding: 4px !important;
  background: rgba(10, 10, 14, 0.94) !important;
  background-image: none !important;
  border: 1px solid var(--border-2) !important;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6) !important;
  border-radius: 0 !important;
  backdrop-filter: blur(20px) !important;
  -webkit-backdrop-filter: blur(20px) !important;
}

.menu-item,
.dropdown-item,
.edit-model-dropdown-item {
  display: flex !important;
  align-items: center !important;
  gap: 10px !important;
  padding: 7px 12px !important;
  font: 500 12px var(--body) !important;
  text-transform: none !important;
  letter-spacing: normal !important;
  font-weight: 500 !important;
  color: var(--c-ink) !important;
  border-bottom: 0 !important;
  border-radius: 0 !important;
  transition: transform 220ms var(--ease), background 150ms, color 150ms !important;
  cursor: pointer;
}
.menu-item:hover,
.dropdown-item:hover,
.edit-model-dropdown-item:hover {
  background: var(--c-accent-wash) !important;
  color: var(--c-ink) !important;
  transform: translateX(4px);
  padding-left: 12px !important;
}
.menu-item.danger,
.dropdown-item.danger {
  color: var(--c-bad) !important;
}
.menu-item.danger:hover,
.dropdown-item.danger:hover {
  background: rgba(224, 82, 82, 0.10) !important;
  color: var(--c-bad) !important;
}

/* The library menu carries a right-aligned mono `kbd`. */
.menu-item kbd,
.dropdown-item kbd {
  margin-left: auto;
  font: 700 10px var(--mono);
  color: var(--c-ink-muted);
  border: 1px solid var(--border-2);
  padding: 1px 5px;
  background: rgba(255, 255, 255, 0.02);
  transition: color 150ms, transform 220ms var(--ease);
}
.menu-item:hover kbd,
.dropdown-item:hover kbd {
  color: var(--c-accent);
  transform: translateX(2px);
}

/* ── 21 · bare checkbox → library/toggle bezel ─────────
 * The settings rows use a plain `<input type="checkbox">` with no
 * wrapper. Rather than rewrite every call site in app.js, the input
 * itself is given the library's hardware bezel. The native control
 * is hidden and the visual is drawn with ::before / ::after.
 * ───────────────────────────────────────────────────────── */
input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--border-2);
  position: relative;
  cursor: pointer;
  transition: background 150ms, border-color 150ms, box-shadow 150ms;
  box-shadow: var(--shadow-inset-deep);
  border-radius: 0;
  vertical-align: middle;
  margin: 0;
}
input[type="checkbox"]:hover:not(:disabled) { border-color: var(--border-3); }
input[type="checkbox"]:checked {
  background: var(--c-accent);
  border-color: var(--c-accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.30), 0 0 0 1px var(--c-accent), 0 0 12px var(--c-accent-glow);
}
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 2px;
  width: 6px;
  height: 10px;
  border-right: 2px solid #000;
  border-bottom: 2px solid #000;
  transform: rotate(45deg) scale(1);
  transform-origin: center;
  animation: qlChk 220ms var(--spring);
}
@keyframes qlChk {
  from { transform: rotate(45deg) scale(0); }
  to   { transform: rotate(45deg) scale(1); }
}
input[type="checkbox"]:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}
input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

/* The settings toggle row lays the label and the control apart. */
.settings-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.settings-toggle-row > div > div:first-child {
  font: 600 12px var(--body) !important;
  color: var(--c-ink) !important;
}
.settings-toggle-row > div > div:last-child {
  font: 400 10px var(--body) !important;
  color: var(--c-ink-muted) !important;
}
.settings-toggle-row.disabled { opacity: 0.4; pointer-events: none; }


/* Composer frosted glass: high-specificity final overrides (beats .icbtn/.snd bases; library-port loads after so index inline fix also applied) */
.chat-input-bar .composer-sq.icbtn,
.chat-input-bar .composer-sq.more-btn,
.chat-input-bar .composer-sq.snd,
.home-chat-input-wrap .composer-sq.icbtn,
.home-chat-input-wrap .composer-sq.more-btn,
.home-chat-input-wrap .composer-sq.snd,
.quad-composer .composer-sq.icbtn,
.quad-composer .composer-sq.more-btn,
.quad-composer .composer-sq.snd,
#more-btn.icbtn.more-btn.composer-sq,
#home-more-btn.icbtn.more-btn.composer-sq,
#send-btn.snd.composer-sq,
#home-send-btn.snd.composer-sq,
.composer-dock .composer-sq {
  background: rgba(14,14,18,0.55) !important;
  background-color: rgba(14,14,18,0.55) !important;
  border: 1px solid rgba(204,144,80,0.22) !important;
  box-shadow: 0 8px 28px rgba(0,0,0,.28) !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
  color: var(--c-ink, var(--text, #ece6db)) !important;
}
.composer-dock .composer-sq::before, .composer-dock .composer-sq.icbtn::before { opacity: 0 !important; background: transparent !important; }
.composer-dock .composer-input,
#chat-view .composer-input, #home-view .composer-input,
#chat-input.composer-input, #home-quick-chat.composer-input {
  background: rgba(14,14,18,0.55) !important;
  background-color: rgba(14,14,18,0.55) !important;
  border: 1px solid rgba(204,144,80,0.22) !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
}
.composer-dock .composer-attachments, #attachment-list-container.composer-attachments {
  background: rgba(14,14,18,0.55) !important;
  background-color: rgba(14,14,18,0.55) !important;
  backdrop-filter: blur(14px) saturate(140%) !important;
  -webkit-backdrop-filter: blur(14px) saturate(140%) !important;
}

/* Composer dock: frosted glass variants */
body.nothing-light .composer-input-shell .composer-input,
body.nothing-light .composer-sq.icbtn,
body.nothing-light .composer-sq.snd,
body.nothing-light .composer-sq.more-btn,
body.nothing-light .composer-attachments {
  background: rgba(248,244,238,0.62) !important;
  border-color: rgba(122,78,30,0.22) !important;
  color: #1f1a15 !important;
  box-shadow: 0 8px 24px rgba(45,31,15,.12) !important;
}
body.nothing-light .composer-input-shell .composer-input::placeholder { color: rgba(47,36,23,0.52) !important; }
