/* =============================================
   wazer.ai — chat-canvas.css
   The chat experience itself.

   Owns: chat canvas + composer base, message list
   (user / AI / model headers), file cards in messages,
   notes-view panel (when a note is open), welcome modal,
   centerpiece composer empty state (greeting + action
   cards + capability row), cost-preview, jump-to-latest,
   floating private-mode toggle, errors.
   Design tokens inherited from main.css :root.
   ============================================= */

  /* ─── canvas — chat view ─── */
  .canvas {
    grid-area: canvas;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
  }
  /* Drag-over feedback for the whole chat canvas — confirms the drop
     will be accepted instead of the browser opening the file. */
  .canvas.drag-over::after {
    content: 'Drop to attach';
    position: absolute;
    inset: 8px;
    pointer-events: none;
    display: grid;
    place-items: center;
    background: var(--accent-soft);
    border: 2px dashed var(--accent);
    border-radius: var(--radius-lg);
    color: var(--accent-deep);
    font-family: var(--font-ui);
    font-size: 18px;
    font-weight: 500;
    z-index: 50;
  }

  .chat-scroll {
    flex: 1;
    overflow-y: auto;
    padding: 24px 24px 0;
  }
  .chat-scroll::-webkit-scrollbar { width: 6px; }
  .chat-scroll::-webkit-scrollbar-thumb {
    background: var(--ink-ghost);
    border-radius: 3px;
  }

  .chat-content {
    max-width: 760px;
    margin: 0 auto;
    padding-bottom: 20px;
  }

  /* Message blocks */
  .message {
    margin-bottom: 28px;
    opacity: 0;
    animation: messageRise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
  }
  @keyframes messageRise {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }

  .message-user {
    display: flex;
    justify-content: flex-end;
  }
  .message-user .bubble {
    background: var(--surface);
    color: var(--ink);
    padding: 12px 18px;
    border-radius: var(--radius-lg);
    border-bottom-right-radius: 4px;
    border: 1px solid var(--border-strong);
    max-width: 80%;
    font-size: 14.5px;
    line-height: 1.5;
    box-shadow: var(--shadow-xs);
  }

  /* Time stamp shown above a user message (when relevant) */
  .timestamp {
    display: flex;
    justify-content: center;
    margin: 16px 0 12px;
    font-family: var(--font-mono);
    font-size: 9.5px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--ink-faint);
  }
  .timestamp .line {
    flex: 1;
    height: 1px;
    background: var(--border);
    margin: 0 12px;
    align-self: center;
    max-width: 80px;
  }

  .message-ai {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  /* Model name as a "sender" header — Newsreader medium, more presence */
  .message-ai .sender {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding-left: 4px;
  }
  .message-ai .sender .model-name {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 15px;
    color: var(--ink);
    letter-spacing: -0.01em;
  }
  .message-ai .sender .model-time {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .message-ai .content {
    color: var(--ink);
    font-size: 14.5px;
    line-height: 1.65;
    padding: 0 4px;
  }
  .message-ai .content p {
    margin-bottom: 12px;
  }
  .message-ai .content p:last-child { margin-bottom: 0; }
  .message-ai .content ul {
    padding-left: 20px;
    margin-bottom: 12px;
  }
  .message-ai .content li {
    margin-bottom: 6px;
  }
  .message-ai .content strong {
    color: var(--ink);
    font-weight: 600;
  }

  /* Cost as a small footnote AFTER the answer */
  .message-ai .cost-footnote {
    display: flex;
    justify-content: flex-end;
    padding: 0 4px;
    margin-top: 4px;
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .message-ai .cost-footnote .arrow-glyph {
    color: var(--ink-ghost);
    font-family: var(--font-display);
    font-style: italic;
    margin-right: 5px;
  }

  /* Action row under each AI message */
  .message-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px 0 0 4px;
    margin-top: 4px;
    opacity: 0;
    transition: opacity 0.15s ease;
  }
  .message-ai:hover .message-actions {
    opacity: 1;
  }
  .message-ai.show-actions .message-actions {
    opacity: 1;
  }

  .msg-action {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 10px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    font-family: var(--font-ui);
    font-size: 11.5px;
    color: var(--ink-faint);
    cursor: pointer;
    transition: all 0.15s ease;
    position: relative;
  }
  .msg-action:hover {
    background: rgba(26, 22, 18, 0.05);
    color: var(--ink-soft);
  }
  .msg-action.save-btn:hover {
    color: var(--accent);
    background: var(--accent-softer);
  }
  .msg-action.save-btn.open {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: var(--accent-soft);
  }

  /* Save-to-note dropdown */
  .save-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    width: 280px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(-4px);
    transition: all 0.18s ease;
    z-index: 60;
  }
  .save-dropdown.open {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0);
  }
  .save-dropdown-header {
    font-family: var(--font-mono);
    font-size: 9.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-faint);
    padding: 6px 10px 4px;
  }
  .save-option {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 10px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.12s ease;
    font-family: var(--font-ui);
    font-size: 13px;
    color: var(--ink);
  }
  .save-option:hover { background: rgba(26, 22, 18, 0.04); }
  .save-option.new-note {
    color: var(--accent);
    font-weight: 500;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
    padding-bottom: 10px;
  }
  .save-option .icon {
    color: var(--ink-faint);
    flex-shrink: 0;
  }
  .save-option.new-note .icon { color: var(--accent); }
  .save-meta {
    padding: 6px 10px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
    font-family: var(--font-mono);
    font-size: 9.5px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  .save-meta .free {
    color: var(--accent);
  }

  /* Save-to-note flying toast */
  .note-toast {
    position: fixed;
    pointer-events: none;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--accent);
    background: var(--surface);
    border: 1px solid var(--accent-soft);
    border-radius: var(--radius-pill);
    padding: 6px 14px;
    box-shadow: var(--shadow-md);
    opacity: 0;
    z-index: 80;
    white-space: nowrap;
    display: flex;
    align-items: center;
    gap: 7px;
  }
  .note-toast.flying {
    animation: flyToSidebar 1.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
  }
  @keyframes flyToSidebar {
    0%   { opacity: 0; transform: translate(0, 0) scale(0.95); }
    18%  { opacity: 1; transform: translate(0, -4px) scale(1); }
    80%  { opacity: 1; }
    100% { opacity: 0;
           transform: translate(var(--toast-tx, -300px), var(--toast-ty, 0px)) scale(0.7); }
  }
  .note-toast .arrow-glyph {
    color: var(--accent);
    font-family: var(--font-display);
    font-style: italic;
  }

  /* ─── composer (sticky bottom in chat view) ─── */
  .composer-wrap {
    flex-shrink: 0;
    width: 100%;
    padding: 12px 24px 20px;
    background: linear-gradient(180deg, transparent 0%, var(--bg-2) 30%);
  }
  /* Subtle "AI can make mistakes" line sitting under the composer.
     Mono + ink-faint + small so it reads as a footnote, not a banner. */
  .composer-disclaimer {
    margin: 8px 0 0;
    text-align: center;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
    letter-spacing: 0.02em;
    line-height: 1.5;
  }
  .composer-inner {
    max-width: 760px;
    margin: 0 auto;
  }

  .composer {
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md);
    transition: all 0.22s ease;
    overflow: visible;
    position: relative;
  }
  .composer:focus-within {
    border-color: var(--accent);
    box-shadow: var(--shadow-lg), var(--shadow-focus);
  }
  .composer textarea {
    width: 100%;
    min-height: 64px;
    max-height: 200px;
    padding: 18px 22px 10px 22px;
    border: none;
    background: transparent;
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.5;
    color: var(--ink);
    resize: none;
    outline: none;
    border-radius: var(--radius-xl);
  }
  .composer textarea::placeholder { color: var(--ink-faint); }
  .composer-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 6px 12px 10px 18px;
  }
  .composer-actions-left {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .composer-actions-right {
    display: flex;
    align-items: center;
    gap: 10px;
    position: relative;
    /* Allow the group to shrink so the model picker truncates instead of
       shoving the cost estimate + send button off the right edge on narrow
       phones. The chain needs min-width:0 at every level to propagate. */
    min-width: 0;
    flex: 0 1 auto;
  }
  /* Send + icon buttons never shrink — they must always stay visible. */
  .composer-actions-right > .send-btn,
  .composer-actions-right > .icon-btn { flex-shrink: 0; }
  /* The cost estimate compresses before anything gets pushed off-screen. */
  .composer-actions-right .cost-preview-block { min-width: 0; flex-shrink: 1; }
  .icon-btn {
    width: 32px; height: 32px;
    border-radius: 6px;
    display: grid;
    place-items: center;
    background: transparent;
    border: none;
    color: var(--ink-faint);
    cursor: pointer;
    transition: all 0.15s ease;
  }
  .icon-btn:hover {
    background: rgba(26, 22, 18, 0.05);
    color: var(--ink-soft);
  }

  /* Camera capture button — only useful where there's a real camera, so it's
     surfaced on touch devices and hidden on desktop (the paperclip already
     covers image upload there). On touch it inherits its sibling button's
     display from .icon-btn / .centerpiece-attach. ID selectors so this beats
     the later-defined .centerpiece-attach display rule without !important. */
  @media (hover: hover) and (pointer: fine) {
    #camera-btn, #centerpiece-camera-btn { display: none; }
  }

  /* Voice input states — red pulse while recording, soft animation
     while the audio uploads + transcribes. Applies to both the
     bottom-composer mic (#mic-btn) and the centerpiece mic
     (#centerpiece-mic-btn). */
  #mic-btn.is-recording,
  #centerpiece-mic-btn.is-recording {
    color: var(--error, #A8453F);
    background: rgba(168, 69, 63, 0.10);
    animation: micPulse 1.4s ease-in-out infinite;
  }
  #mic-btn.is-processing,
  #centerpiece-mic-btn.is-processing {
    color: var(--ink-faint);
    background: rgba(26, 22, 18, 0.05);
    opacity: 0.7;
    pointer-events: none;
  }
  @keyframes micPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(168, 69, 63, 0.30); }
    50%      { box-shadow: 0 0 0 6px rgba(168, 69, 63, 0); }
  }
  .model-picker {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: 12.5px;
    color: var(--ink-soft);
    cursor: pointer;
    transition: all 0.15s ease;
    user-select: none;
    /* Let the picker shrink so a long model name (e.g. "Grok Imagine Video")
       ellipsizes instead of wrapping and shoving the cost + send off-screen. */
    min-width: 0;
  }
  .model-picker:hover {
    background: rgba(26, 22, 18, 0.04);
    color: var(--ink);
  }
  .model-picker .auto-label {
    font-weight: 600;
    color: var(--ink);
    /* One line, truncate if squeezed — never wrap onto multiple rows. */
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .model-picker .auto-desc {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }
  .model-picker .chev { color: var(--ink-faint); }

  .cost-preview {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
    user-select: none;
    /* Keep the estimate intact — the picker absorbs any horizontal squeeze. */
    flex-shrink: 0;
    white-space: nowrap;
  }
  .cost-preview .amount {
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 12.5px;
    color: var(--ink-soft);
  }

  .send-btn {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: white;
    border: none;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background 0.18s ease, transform 0.18s ease;
    flex-shrink: 0;
  }
  .send-btn:hover {
    background: var(--accent-deep);
    transform: translateY(-1px);
  }
  .send-btn .send-svg {
    transition: transform 0.18s ease;
  }
  .send-btn:hover .send-svg {
    transform: translate(1px, -1px);
  }
  .send-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .send-btn:disabled:hover {
    background: var(--accent);
    transform: none;
  }
  .send-btn:disabled:hover .send-svg {
    transform: none;
  }
  /* Show send icon by default, stop icon while streaming */
  .send-btn .stop-svg { display: none; }
  .send-btn.is-stopping .send-svg { display: none; }
  .send-btn.is-stopping .stop-svg { display: block; }
  .send-btn.is-stopping:hover .stop-svg {
    transform: none;
  }

  /* ─────────────────────────────────────────────────────────
     Project View — centered panel shown when a project is active
     ───────────────────────────────────────────────────────── */

  .project-view {
    max-width: 680px;
    margin: 0 auto;
    padding: 48px 24px;
    display: flex;
    flex-direction: column;
    gap: 32px;
  }
  .project-view[hidden] {
    display: none;
  }

  .project-view-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
  }

  .project-view-title-group {
    min-width: 0;
  }

  .project-view-name {
    font-family: var(--font-display);
    font-size: 1.6rem;
    font-weight: 500;
    color: var(--ink);
    margin: 0;
    line-height: 1.3;
  }

  .project-view-desc {
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--ink-soft);
    margin: 4px 0 0;
    line-height: 1.4;
  }

  .project-view-desc:empty {
    display: none;
  }

  .project-view-header-actions {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-shrink: 0;
  }

  /* Primary CTA inside the project header — "+ New chat" scoped to this
     project. Filled-accent so it reads as the main action of the view,
     not just another secondary control. */
  .project-view-new-chat {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border: none;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 500;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background 0.12s ease, transform 0.06s ease;
  }
  .project-view-new-chat:hover {
    background: var(--accent-deep);
  }
  .project-view-new-chat:active {
    transform: translateY(1px);
  }
  .project-view-new-chat .plus {
    font-size: 16px;
    line-height: 1;
    font-weight: 400;
  }

  .project-view-close {
    width: 28px;
    height: 28px;
    display: grid;
    place-items: center;
    border: none;
    background: none;
    color: var(--ink-faint);
    font-size: 18px;
    cursor: pointer;
    border-radius: var(--radius-sm);
    transition: color 0.15s ease, background 0.15s ease;
    flex-shrink: 0;
  }
  .project-view-close:hover {
    color: var(--ink-soft);
    background: var(--accent-soft);
  }

  .project-view-section {
    display: flex;
    flex-direction: column;
  }

  .project-view-section-heading {
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--ink-faint);
    margin: 0 0 8px;
  }
  /* Count chip after the section name — keeps the header informative
     without making the label compete with the content below it. */
  .project-view-section-count {
    margin-left: 8px;
    font-weight: 400;
    color: var(--ink-ghost);
    letter-spacing: 0;
  }
  .project-view-section-count:empty { display: none; }

  .project-view-cards {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  .project-view-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    position: relative;
  }
  .project-view-card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-xs);
  }
  /* ⋯ menu button — hover-only so cards stay clean by default. Uses
     opacity (not display) so it keeps its 28px slot in the flex layout
     even when invisible — otherwise the title would shift left/right
     as you hover, causing a layout jump. */
  .project-view-card-menu {
    display: grid;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    border: none;
    background: transparent;
    color: var(--ink-faint);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    place-items: center;
    transition: color 0.15s ease, background 0.15s ease, opacity 0.15s ease;
    flex-shrink: 0;
    opacity: 0;
    pointer-events: none;
  }
  .project-view-card:hover .project-view-card-menu,
  .project-view-card-menu.open {
    opacity: 1;
    pointer-events: auto;
  }
  .project-view-card-menu:hover {
    color: var(--ink);
    background: var(--accent-soft);
  }

  /* Selection checkbox — sits on the left of each card. Hidden by default
     for a clean look; appears on card-hover, on focus, when the card is
     selected, or when ANY selection exists in the view (so the user can
     pick more without hovering each card individually). */
  .project-view-card-select {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    cursor: pointer;
    accent-color: var(--accent);
    margin: 0;
    opacity: 0;
    transition: opacity 0.15s ease;
  }
  .project-view-card:hover .project-view-card-select,
  .project-view-card-select:focus,
  .project-view-card.is-selected .project-view-card-select,
  .project-view.has-selection .project-view-card-select {
    opacity: 1;
  }
  .project-view-card.is-selected {
    border-color: var(--accent);
    background: var(--accent-softer);
  }

  /* Bulk-action bar — sticks to the bottom of the project-view, styled
     as a soft surface pill matching the rest of the UI rather than a
     hard contrasting black bar. */
  .project-view-bulkbar {
    position: sticky;
    bottom: 16px;
    align-self: center;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 8px 12px 8px 18px;
    background: var(--surface);
    color: var(--ink);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    box-shadow: var(--shadow-lg);
    font-family: var(--font-ui);
    font-size: 13px;
    z-index: 5;
  }
  .project-view-bulkbar[hidden] { display: none; }
  .project-view-bulkbar-count {
    font-weight: 500;
    color: var(--ink);
  }
  .project-view-bulkbar-actions {
    display: flex;
    gap: 4px;
  }
  .project-view-bulkbar-clear,
  .project-view-bulkbar-delete {
    border: none;
    font-family: var(--font-ui);
    font-size: 13px;
    padding: 6px 14px;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
  }
  .project-view-bulkbar-clear,
  .project-view-bulkbar-move {
    background: transparent;
    color: var(--ink-soft);
  }
  .project-view-bulkbar-clear:hover,
  .project-view-bulkbar-move:hover {
    background: var(--bg-2);
    color: var(--ink);
  }
  /* Move dropdown — anchored above the Move button, opens on click. */
  .project-view-bulkbar-move-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
  }
  .project-view-bulkbar-move-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    min-width: 200px;
    max-height: 280px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 4px;
    z-index: 10;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .project-view-bulkbar-move-menu[hidden] { display: none; }
  .project-view-bulkbar-move-item {
    background: transparent;
    border: none;
    text-align: left;
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 13px;
    cursor: pointer;
    transition: background 0.12s ease;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .project-view-bulkbar-move-item:hover {
    background: var(--accent-soft);
  }
  .project-view-bulkbar-move-sep {
    height: 1px;
    background: var(--border);
    margin: 4px 0;
  }
  .project-view-bulkbar-move-empty {
    padding: 8px 12px;
    color: var(--ink-faint);
    font-style: italic;
    font-size: 12px;
  }
  .project-view-bulkbar-delete {
    background: var(--error-soft);
    color: var(--error-text);
  }
  .project-view-bulkbar-delete:hover {
    background: var(--error);
    color: var(--surface);
  }

  /* ───────────────────────────────────────────────────────────
     Compare Models view — full-canvas takeover. Lets the user
     pick 2-4 models, send one prompt, and see side-by-side
     responses. Phase 1: shell only (no streaming yet). */
  .compare-view {
    max-width: 1480px;
    margin: 0 auto;
    padding: 24px 32px 48px;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .compare-view[hidden] { display: none; }

  /* Hide the standard chat input while compare mode is active — compare
     has its own input and showing both is confusing. */
  .app.compare-active .chat-input-area { display: none; }

  .compare-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
  }
  .compare-title-group { min-width: 0; }
  .compare-title {
    font-family: var(--font-display);
    font-size: 1.6rem;
    font-weight: 500;
    color: var(--ink);
    margin: 0;
    line-height: 1.3;
  }
  .compare-subtitle {
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--ink-soft);
    margin: 4px 0 0;
  }
  .compare-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
  }
  .compare-share {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.15s ease, color 0.15s ease;
  }
  .compare-share:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink-soft);
  }

  /* Compare ⇄ Debate flavour switch — a quiet segmented control centred
     in the header. Same picker below; only the run semantics differ. */
  .compare-mode-toggle {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    background: var(--surface-warm);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    flex-shrink: 0;
  }
  .compare-mode-seg {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 500;
    padding: 5px 14px;
    border-radius: 7px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
  }
  .compare-mode-seg:hover {
    color: var(--ink);
  }
  .compare-mode-seg.is-active {
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-xs);
  }

  /* On narrow screens the three header children (title · flavour toggle ·
     share/close) can't share one row — the title collapses and wraps mid-word
     behind the toggle. Drop the toggle to its own full-width row underneath. */
  @media (max-width: 640px) {
    /* Timestamps on every question + answer are clutter on a narrow screen —
       the conversation order already carries the sequence. Hide both. */
    .sender-meta,
    .msg-usertime { display: none; }

    .compare-header {
      flex-wrap: wrap;
      align-items: center;
      gap: 10px 12px;
    }
    .compare-title-group { flex: 1 1 auto; }
    .compare-title { font-size: 1.35rem; }
    .compare-header-actions { order: 2; }
    .compare-mode-toggle {
      order: 3;
      flex-basis: 100%;
      justify-content: center;
    }
  }

  /* Debate foundation hint — only shown in debate flavour while the
     sequential run pipeline (Commit B) is still pending. */
  .compare-debate-hint {
    font-family: var(--font-ui);
    font-size: 13px;
    line-height: 1.45;
    color: var(--ink-soft);
    background: var(--accent-softer);
    border: 1px solid var(--accent-soft);
    border-radius: var(--radius-md);
    padding: 9px 12px;
    margin-bottom: 4px;
  }
  .compare-debate-hint[hidden] { display: none; }

  .compare-close {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .compare-close:hover {
    color: var(--ink);
    background: var(--accent-soft);
  }

  /* Model chips — clickable pills, click toggles selection. */
  .compare-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }
  .compare-chip {
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 12.5px;
    padding: 6px 12px 6px 10px;
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  }
  .compare-chip:hover {
    border-color: var(--border-strong);
  }
  .compare-chip.is-selected {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
  }
  /* Provider color dot before the chip text — makes the chip rail
     scannable by provider family at a glance instead of relying on
     model-name recognition alone. */
  .compare-chip-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .compare-chip.is-selected .compare-chip-dot {
    /* Selected chip is accent-filled — give the dot a faint ring so
       its provider color stays readable against the dark background. */
    box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.45);
  }
  .compare-chips-empty {
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
    font-style: italic;
  }
  /* Chat / Image group label — full-width inside the flex-wrap rail so
     each group's pills land on their own rows. Quiet typography so it
     reads as a section marker, not a heading. */
  .compare-chip-group-label {
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    color: var(--ink-faint);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    width: 100%;
    margin: 10px 0 2px;
  }
  .compare-chip-group-label:first-child { margin-top: 0; }

  /* Image panel body — full-bleed image fills the panel; loading
     state uses the same dotted spinner the chat image renderer uses. */
  .compare-panel-image {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 8px;
    box-sizing: border-box;
  }
  .compare-panel-image img {
    max-width: 100%;
    max-height: 360px;
    object-fit: contain;
    border-radius: var(--radius-sm);
    cursor: zoom-in;
  }
  .compare-panel-image-loading {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
    padding: 40px 20px;
    justify-content: center;
  }

  /* Prompt input + cost + send button */
  .compare-prompt-row {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .compare-input {
    width: 100%;
    border: none;
    background: transparent;
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink);
    resize: vertical;
    min-height: 40px;
    outline: none;
  }
  .compare-input::placeholder { color: var(--ink-faint); }
  .compare-input-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  .compare-cost-estimate {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--ink-faint);
  }

  /* ─── Compare/Debate mobile composer ───
     On phones the chip rail (every model, grouped across many rows) can fill
     the entire viewport, pushing the prompt off the bottom of the page — and
     the textarea was a fixed ~42px on top of that. Three fixes, all phone-only:
     cap the chip rail to a bounded scroll area, pin the prompt row to the
     bottom of the viewport so it's always reachable, and give the textarea
     real room (auto-grown from compare.js). Compare and Debate share this
     surface, so both flavours get the fix. */
  @media (max-width: 640px) {
    /* Bounded, internally-scrolling chip rail — can't swallow the screen. */
    .compare-chips {
      max-height: 38vh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }
    /* Pin the prompt row to the bottom; chips + panels scroll underneath it.
       The nearest scrolling ancestor is .chat-scroll, so sticky keeps this
       flush to the viewport bottom while the taller content scrolls. */
    .compare-prompt-row {
      position: sticky;
      bottom: 0;
      z-index: 5;
      background: var(--surface);
      box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.18);
    }
    .compare-input {
      min-height: 64px;
      max-height: 40vh;
      resize: none; /* touch drag-resize is fiddly — auto-grow handles it */
    }
    /* Trim the big bottom padding so the pinned composer sits near the edge. */
    .compare-view { padding-bottom: 16px; }
  }

  /* Compare-only attach button — sits left of the cost label in the
     meta row, mirrors chat's paperclip but smaller / borderless. */
  .compare-attach-btn {
    width: 28px;
    height: 28px;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    cursor: pointer;
    display: grid;
    place-items: center;
    border-radius: var(--radius-sm);
    transition: color 0.15s ease, background 0.15s ease;
    flex-shrink: 0;
  }
  .compare-attach-btn:hover {
    color: var(--ink);
    background: var(--accent-soft);
  }

  /* Attachment strip above the textarea — file chips. Text-only for
     v1 (no thumbnails), so a compact pill list with kind label +
     filename + remove × is enough. */
  .compare-attachment-strip {
    display: none;
    flex-wrap: wrap;
    gap: 6px;
    padding: 10px 14px 0;
  }
  .compare-attachment-strip.visible { display: flex; }
  .compare-att-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 4px 4px 4px 8px;
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--ink);
    max-width: 240px;
  }
  .compare-att-chip.is-saved {
    background: var(--accent-softer);
    border-color: var(--accent-soft);
  }
  .compare-att-kind {
    font-family: var(--font-mono);
    font-size: 9.5px;
    font-weight: 600;
    color: var(--ink-faint);
    letter-spacing: 0.04em;
  }
  /* Image attachments get a small thumbnail in place of the kind label. */
  .compare-att-thumb {
    width: 24px;
    height: 24px;
    object-fit: cover;
    border-radius: 3px;
    flex-shrink: 0;
    margin: -2px 0;
  }
  .compare-att-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }
  .compare-att-remove {
    width: 18px;
    height: 18px;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    cursor: pointer;
    border-radius: 50%;
    font-size: 14px;
    line-height: 1;
    display: grid;
    place-items: center;
    flex-shrink: 0;
  }
  .compare-att-remove:hover {
    background: var(--accent-soft);
    color: var(--ink);
  }
  .compare-att-note {
    width: 100%;
    background: var(--warning-soft, rgba(184, 124, 29, 0.12));
    color: var(--warning-text, #8a5a14);
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    margin-bottom: 4px;
  }

  /* Persistent warnings row above the attachment strip — fires only
     when an attachment definitively won't reach a selected model
     (image-based PDF + text-only model, image + non-multimodal model). */
  .compare-attach-warnings {
    display: none;
    flex-direction: column;
    gap: 4px;
    padding: 10px 14px 0;
  }
  .compare-attach-warnings.visible { display: flex; }
  .compare-att-warning-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    background: rgba(184, 124, 29, 0.14);
    color: var(--warning-text, #8a5a14);
    padding: 7px 12px;
    border-left: 3px solid var(--warning, #B87C1D);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: 12.5px;
    line-height: 1.45;
  }
  .compare-att-warning-icon {
    flex-shrink: 0;
    color: var(--warning, #B87C1D);
    font-size: 14px;
    line-height: 1.3;
  }
  /* Send button reuses the chat composer's .send-btn class verbatim —
     same visual + morph behavior. The send-svg/stop-svg toggle via
     .is-stopping is inherited too. */

  /* Panels grid — count-driven layout. 2 = side by side, 3 = three cols,
     4 = 2×2. Single column fallback on narrow screens. */
  .compare-panels {
    display: grid;
    gap: 14px;
    grid-template-columns: 1fr;
  }
  .compare-panels[data-count="2"] {
    grid-template-columns: repeat(2, 1fr);
  }
  .compare-panels[data-count="3"] {
    grid-template-columns: repeat(3, 1fr);
  }
  .compare-panels[data-count="4"] {
    grid-template-columns: repeat(2, 1fr);
  }
  @media (max-width: 900px) {
    .compare-panels[data-count="2"],
    .compare-panels[data-count="3"],
    .compare-panels[data-count="4"] {
      grid-template-columns: 1fr;
    }
  }
  .compare-panels-hint {
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
    text-align: center;
    padding: 40px 20px;
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
  }
  /* .compare-panels is display:grid, so [hidden] needs an explicit
     override to actually hide it when debate flavour takes over. */
  .compare-panels[hidden] { display: none; }

  /* ───────────────────────────────────────────────────────────
     Debate flavour — speaking order + vertical conversation stream.
     Shares compare's chip rail + prompt; only the surface differs. */

  /* Speaking-order badge stamped onto each selected chip (debate only).
     Sits first in the chip, before the provider dot. */
  .compare-chip-order {
    display: inline-grid;
    place-items: center;
    width: 17px;
    height: 17px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 700;
    flex-shrink: 0;
    margin-right: 1px;
  }

  .debate-order-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: -4px;
  }
  .debate-order-bar[hidden] { display: none; }
  .debate-order-hint {
    font-family: var(--font-ui);
    font-size: 12.5px;
    color: var(--ink-faint);
  }
  .debate-order-seq {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-family: var(--font-ui);
    font-size: 12.5px;
    color: var(--ink-soft);
  }
  .debate-order-step {
    display: inline-flex;
    align-items: center;
    gap: 6px;
  }
  .debate-order-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 500;
    flex-shrink: 0;
  }
  .debate-order-sep {
    color: var(--ink-ghost);
  }
  .debate-shuffle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 11px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
  }
  .debate-shuffle:hover {
    border-color: var(--border-strong);
    color: var(--ink);
  }
  .debate-shuffle:disabled {
    opacity: 0.5;
    cursor: default;
  }

  /* The conversation stream — model-stamped bubbles flowing vertically. */
  .debate-stream {
    display: flex;
    flex-direction: column;
    gap: 18px;
    max-width: 820px;
    margin: 0 auto;
    width: 100%;
  }
  .debate-stream[hidden] { display: none; }

  .debate-msg { display: flex; flex-direction: column; }

  /* User's question — right-aligned warm bubble, like the chat composer. */
  .debate-msg-user { align-items: flex-end; }
  .debate-msg-user .debate-bubble {
    background: var(--accent-soft);
    border: 1px solid var(--accent-soft);
    border-radius: var(--radius-lg);
    padding: 10px 14px;
    max-width: 85%;
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.55;
    color: var(--ink);
    white-space: pre-wrap;
  }

  /* Model turn — left-aligned, stamped with speaking number + provider. */
  .debate-msg-model { align-items: stretch; }
  .debate-stamp {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
  }
  .debate-speaker-num {
    display: inline-grid;
    place-items: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--surface);
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    flex-shrink: 0;
  }
  .debate-stamp-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  .debate-stamp-name {
    font-family: var(--font-mono);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--ink-soft);
    letter-spacing: 0.01em;
  }
  .debate-msg-model .debate-bubble {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
  }
  .debate-body {
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.6;
    color: var(--ink);
    word-wrap: break-word;
  }
  .debate-body > :first-child { margin-top: 0; }
  .debate-body > :last-child { margin-bottom: 0; }
  .debate-body p { margin: 0 0 0.8em; }
  .debate-body ul, .debate-body ol { margin: 0.5em 0; padding-left: 1.4em; }
  .debate-body li { margin: 0.25em 0; }
  .debate-body pre {
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 13px;
  }
  .debate-body code {
    font-family: var(--font-mono);
    font-size: 0.9em;
  }
  .debate-thinking {
    color: var(--ink-faint);
    font-style: italic;
  }
  .debate-stopped { color: var(--ink-faint); font-style: italic; }
  .debate-error { color: var(--error-text); }
  .debate-cost {
    margin-top: 8px;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }

  /* Round divider — a quiet centred rule marking the start of an
     extension round. */
  .debate-round-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 6px 0;
    color: var(--ink-faint);
  }
  .debate-round-divider::before,
  .debate-round-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border-divider);
  }
  .debate-round-divider span {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  /* Per-round verdict — a horizontal block that reads as a summary, not
     another model turn. Accent-tinted, full-width within the stream. */
  .debate-summary {
    border: 1px solid var(--accent-soft);
    border-left: 3px solid var(--accent);
    background: var(--accent-softer);
    border-radius: var(--radius-md);
    padding: 14px 16px;
  }
  .debate-summary-label {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 8px;
  }
  .debate-summary-body {
    font-family: var(--font-ui);
    font-size: 14.5px;
    line-height: 1.55;
    color: var(--ink);
  }
  .debate-summary-body > :first-child { margin-top: 0; }
  .debate-summary-body > :last-child { margin-bottom: 0; }
  .debate-summary-body p { margin: 0 0 0.6em; }
  .debate-summary-body ul { margin: 0.2em 0 0.6em; padding-left: 1.2em; }
  .debate-summary-body strong { color: var(--accent-deep); }
  .debate-summary-cost {
    margin-top: 8px;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }

  /* "One more round" affordance below the latest summary. */
  .debate-next-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 4px 0 8px;
  }
  .debate-next-round {
    appearance: none;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--surface);
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 600;
    padding: 9px 20px;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
  }
  .debate-next-round:hover {
    background: var(--accent-deep);
    border-color: var(--accent-deep);
  }
  .debate-next-note {
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--ink-faint);
    text-align: center;
  }
  .debate-next-input {
    width: 100%;
    max-width: 560px;
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.5;
    padding: 10px 14px;
    min-height: 64px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--ink);
    box-sizing: border-box;
    resize: vertical;
    overflow: hidden;
  }
  .debate-next-input:focus { outline: none; border-color: var(--ink); }
  .debate-moderator-tag {
    display: block;
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.7;
    margin-bottom: 3px;
  }

  /* Placeholder panel — sits in unfilled slots so the grid always shows
     at least MIN_MODELS columns. Conveys "this is where a comparison
     will appear" before any model is picked. */
  .compare-panel-placeholder {
    background: transparent;
    border: 1px dashed var(--border-strong);
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 240px;
    color: var(--ink-faint);
  }
  .compare-panel-placeholder-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    font-family: var(--font-ui);
  }
  .compare-panel-placeholder-icon {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    display: grid;
    place-items: center;
    color: var(--ink-faint);
    font-size: 18px;
    line-height: 1;
  }
  .compare-panel-placeholder-text {
    font-size: 12.5px;
  }

  .compare-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    min-height: 240px;
    overflow: hidden;
    /* Grid items default to min-width:auto, which lets wide content
       (tables, long unbroken strings) expand the column. Lock to 0 so
       the panel always respects its grid track. */
    min-width: 0;
  }
  .compare-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-2);
  }
  .compare-panel-model {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--ink);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .compare-panel-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
  }
  .compare-panel-stop {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 11px;
    padding: 3px 8px;
    border-radius: var(--radius-sm);
    cursor: pointer;
  }
  .compare-panel-stop:hover {
    background: var(--bg-2);
  }
  .compare-panel-remove {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .compare-panel-remove:hover {
    color: var(--ink);
    background: var(--accent-soft);
  }
  .compare-panel-body {
    flex: 1;
    overflow-y: auto;
    padding: 14px;
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.65;
    color: var(--ink);
    word-wrap: break-word;
    overflow-wrap: anywhere;
  }
  .compare-panel-empty {
    color: var(--ink-faint);
    font-style: italic;
    text-align: center;
    padding: 30px 10px;
    font-size: 13px;
  }
  .compare-panel-error {
    color: var(--error-text);
    background: var(--error-soft);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 13px;
  }
  .compare-panel-stream p { margin: 0 0 12px; }
  .compare-panel-stream p:last-child { margin-bottom: 0; }
  .compare-panel-stream strong { font-weight: 600; }
  .compare-panel-stream em { font-style: italic; }
  .compare-panel-stream ul,
  .compare-panel-stream ol { margin: 0 0 12px; padding-left: 22px; }
  .compare-panel-stream code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background: var(--bg-2);
    padding: 1px 5px;
    border-radius: 3px;
  }
  .compare-panel-stream pre {
    background: var(--bg-2);
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 12.5px;
  }
  .compare-panel-stream pre code { background: transparent; padding: 0; }
  .compare-panel-stream table {
    width: 100%;
    table-layout: fixed;
    border-collapse: collapse;
    margin: 0 0 12px;
    font-size: 13px;
    word-wrap: break-word;
  }
  .compare-panel-stream th,
  .compare-panel-stream td {
    border: 1px solid var(--border);
    padding: 5px 7px;
    text-align: left;
    vertical-align: top;
  }
  .compare-panel-stream th {
    background: var(--bg-2);
    font-weight: 600;
  }
  .compare-panel-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 14px;
    border-top: 1px solid var(--border);
    background: var(--surface-warm);
    min-height: 36px;
  }
  .compare-panel-cost {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }
  .compare-panel-continue {
    background: transparent;
    border: 1px solid var(--accent);
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 12px;
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
  }
  .compare-panel-continue:hover {
    background: var(--accent);
    color: var(--surface);
  }

  .project-view-card-title {
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
  }

  .project-view-card-time {
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--ink-faint);
    white-space: nowrap;
    flex-shrink: 0;
  }

  .project-view-empty {
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--ink-ghost);
    text-align: center;
    padding: 24px 0;
  }

  /* ─────────────────────────────────────────────────────────
     Bridge styles — added on top of Mock 19b's CSS
     (19b's CSS is preserved untouched above).
     These rules make production-side classnames render with 19b's look:
     • messages.js writes .msg/.msg--user/.msg--ai/.msg-body/.msg-sender/.msg-cost-footnote
       — we re-skin those to match 19b's .message/.message-user/.message-ai/.bubble/.content
     • welcome-modal, notes-view, model-dropdown, errors, file-thumbs — added fresh.
     ───────────────────────────────────────────────────────── */

  /* ── Generic helpers used by JS ── */
  .hidden { display: none !important; }


  /* ── messages.js classes mapped to 19b's look ── */
  .msg-wrapper {
    max-width: 760px;
    margin: 0 auto;
    width: 100%;
  }

  .msg {
    margin-bottom: 28px;
    opacity: 0;
    animation: messageRise 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
    display: flex;
    flex-direction: column;
  }

  .msg--user {
    align-items: flex-end;
  }
  .msg--user .msg-body {
    background: var(--surface);
    color: var(--ink);
    padding: 12px 18px;
    border-radius: var(--radius-lg);
    border-bottom-right-radius: 4px;
    border: 1px solid var(--border-strong);
    max-width: 80%;
    font-size: 16px;
    line-height: 1.5;
    box-shadow: var(--shadow-xs);
    white-space: pre-wrap;
    word-wrap: break-word;
  }

  .msg--ai {
    align-items: stretch;
    gap: 6px;
  }

  /* Copy button row under finished AI messages — hover-visible to
     keep the reading surface quiet. Sits at the same left-padding
     as .msg-body so it aligns with the text above. */
  .msg-actions {
    display: flex;
    gap: 6px;
    padding: 2px 4px 0;
    opacity: 0;
    transition: opacity 0.15s ease;
  }
  .msg:hover .msg-actions { opacity: 1; }
  .msg-copy-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    border-radius: 5px;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .msg-copy-btn:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink-soft);
  }
  .msg-copy-btn.copied {
    color: var(--success-text, #5A7A52);
    opacity: 1;
  }
  .msg:has(.msg-copy-btn.copied) .msg-actions { opacity: 1; }

  /* User messages are right-aligned (.msg--user has align-items:
     flex-end), so the action row needs to anchor right too — otherwise
     the copy button floats off to the left, far from the bubble. */
  .msg--user .msg-actions {
    align-self: flex-end;
    padding: 2px 4px 0 0;
  }

  /* Retry-with-model button + popover. Same visual treatment as copy
     button so they read as a paired action row. The popover lists
     models with provider sub-labels so wazer's multi-model spread
     is visible at the moment of choice. */
  .msg-retry-wrap {
    position: relative;
  }
  .msg-retry-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    border-radius: 5px;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .msg-retry-btn:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink-soft);
  }
  .msg-share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    border-radius: 5px;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .msg-share-btn:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink-soft);
  }


  /* Edit button on user messages — same visual treatment as the rest
     of the action row. Clicking it swaps the bubble for an inline
     editor (.msg-edit-wrap below). */
  .msg-edit-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    border-radius: 5px;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .msg-edit-btn:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink-soft);
  }

  /* Inline edit-mode for a user message — textarea + Save/Cancel
     mounted in place of the bubble. Sits right-aligned to match the
     user-message side of the conversation. Textarea inherits the
     bubble border + paper feel so it reads as "the same message,
     now editable" rather than a separate input. */
  .msg-edit-wrap {
    align-self: flex-end;
    width: 100%;
    max-width: 600px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .msg-edit-textarea {
    width: 100%;
    box-sizing: border-box;
    padding: 12px 16px;
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 14.5px;
    line-height: 1.5;
    border: 1px solid var(--accent);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    resize: none;
    outline: none;
    overflow-y: auto;
  }
  .msg-edit-textarea:focus {
    box-shadow: var(--shadow-focus);
  }
  .msg-edit-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
  }
  .msg-retry-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 50;
    min-width: 220px;
    max-height: 360px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    padding: 4px;
    flex-direction: column;
    gap: 1px;
    animation: messageRise 0.18s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  /* Use :not([hidden]) for the visibility toggle — declaring
     display: flex unconditionally would override the hidden attribute
     and render the empty menu as a floating box. */
  .msg-retry-menu:not([hidden]) {
    display: flex;
  }
  .msg-retry-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 10px;
    border: none;
    background: transparent;
    text-align: left;
    cursor: pointer;
    border-radius: 5px;
    font-family: var(--font-ui);
    font-size: 13px;
    color: var(--ink);
    transition: background 0.12s ease;
  }
  .msg-retry-item:hover {
    background: rgba(26, 22, 18, 0.05);
  }
  .msg-retry-item.is-same .msg-retry-item-label {
    font-weight: 500;
  }
  .msg-retry-item-model {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    flex-shrink: 0;
  }
  .msg-retry-sep {
    font-family: var(--font-mono);
    font-size: 9px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 10px 10px 4px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
  }

  .msg-sender {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding-left: 4px;
  }
  .msg-sender .sender-name {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 15px;
    color: var(--ink);
    letter-spacing: -0.01em;
  }
  .msg-sender .sender-meta {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .msg--ai .msg-body {
    color: var(--ink);
    font-size: 16px;
    line-height: 1.65;
    padding: 0 4px;
    word-wrap: break-word;
  }

  /* "Thinking…" indicator shown while the bubble is otherwise empty
     — bridges the silent wait on reasoning models that don't stream
     anything until the answer is essentially done. */
  .msg-thinking {
    margin: 0 4px;
    padding: 4px 0;
    font-size: 13px;
    color: var(--ink-faint);
    display: flex;
    align-items: center;
    gap: 8px;
  }

  /* Slow-stream watchdog banner — appears inside the assistant message
     after ~5 min of stream silence so the user knows the run may be
     stuck. Calm amber so it reads as information, not error. */
  .msg-slow-notice {
    margin: 8px 4px 0;
    padding: 10px 14px;
    background: rgba(184, 124, 29, 0.08);
    border: 1px solid rgba(184, 124, 29, 0.22);
    border-radius: var(--radius-md);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink);
  }
  .msg-slow-notice strong {
    color: var(--warning, #B87C1D);
    font-weight: 500;
  }

  /* Reasoning summary block — OpenAI Responses reasoning models (gpt-5.x,
     o-series). Shown above the body. Open by default while streaming,
     collapsed once the actual answer starts. */
  .msg-reasoning {
    margin: 0 4px 12px;
    padding: 10px 14px;
    border-left: 2px solid var(--border);
    background: var(--bg-2);
    border-radius: 4px;
    font-size: 13px;
    color: var(--ink-soft);
  }
  .msg-reasoning-toggle {
    cursor: pointer;
    font-family: var(--font-ui);
    font-size: 12px;
    color: var(--ink-faint);
    list-style: none;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .msg-reasoning-toggle::-webkit-details-marker { display: none; }
  .msg-reasoning-toggle::before {
    content: '›';
    transition: transform 0.15s ease;
    color: var(--ink-faint);
  }
  .msg-reasoning[open] .msg-reasoning-toggle::before {
    transform: rotate(90deg);
  }
  .msg-reasoning.is-thinking .msg-reasoning-toggle {
    color: var(--accent);
  }
  .msg-reasoning-body {
    margin-top: 8px;
    font-style: italic;
    white-space: pre-wrap;
    word-wrap: break-word;
    line-height: 1.6;
  }
  .thinking-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    animation: thinking-pulse 1.2s ease-in-out infinite;
  }
  @keyframes thinking-pulse {
    0%, 100% { opacity: 0.3; transform: scale(0.85); }
    50%      { opacity: 1;   transform: scale(1.1); }
  }
  .msg--ai .msg-body p { margin: 0 0 12px; }
  .msg--ai .msg-body p:last-child { margin-bottom: 0; }
  .msg--ai .msg-body strong { font-weight: 600; color: var(--ink); }
  .msg--ai .msg-body em { font-style: italic; }
  .msg--ai .msg-body ul,
  .msg--ai .msg-body ol { margin: 0 0 12px; padding-left: 24px; }
  .msg--ai .msg-body li { margin: 4px 0; }
  .msg--ai .msg-body code {
    font-family: var(--font-mono);
    font-size: 13px;
    background: rgba(26, 22, 18, 0.05);
    padding: 1px 6px;
    border-radius: 4px;
  }
  .msg--ai .msg-body pre {
    background: rgba(26, 22, 18, 0.05);
    padding: 12px 14px;
    border-radius: 8px;
    overflow-x: auto;
    margin: 0 0 12px;
  }
  .msg--ai .msg-body pre code {
    background: transparent;
    padding: 0;
    font-size: 13px;
  }

  /* Per-codeblock copy button — hover-visible, top-right of each <pre>
     wrapper. Lets users grab a specific snippet without scrolling
     above the block or copying the whole message. */
  .codeblock-wrap {
    position: relative;
    margin: 0 0 12px;
  }
  .codeblock-wrap pre {
    margin: 0;
  }
  .codeblock-copy-btn {
    position: absolute;
    top: 6px;
    right: 6px;
    padding: 3px 9px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: var(--font-ui);
    font-size: 10px;
    font-weight: 500;
    color: var(--ink-faint);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s ease, color 0.15s ease, background 0.15s ease;
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .codeblock-wrap:hover .codeblock-copy-btn { opacity: 0.85; }
  .codeblock-copy-btn:hover {
    opacity: 1 !important;
    color: var(--ink);
    background: var(--surface-warm);
  }
  .codeblock-copy-btn.copied {
    color: var(--success-text, #5A7A52);
    opacity: 1;
    border-color: var(--success-text, #5A7A52);
  }
  .msg--ai .msg-body h1,
  .msg--ai .msg-body h2,
  .msg--ai .msg-body h3 {
    font-family: var(--font-ui);
    font-weight: 600;
    margin: 14px 0 8px;
    color: var(--ink);
  }
  .msg--ai .msg-body h1 { font-size: 20px; }
  .msg--ai .msg-body h2 { font-size: 18px; }
  .msg--ai .msg-body h3 { font-size: 16.5px; }
  .msg--ai .msg-body a { color: var(--accent); text-decoration: underline; }
  .msg--ai .msg-body blockquote {
    border-left: 3px solid var(--border-strong);
    padding-left: 12px;
    margin: 0 0 12px;
    color: var(--ink-soft);
  }

  /* Inline "Continue →" button on length-truncated AI replies. Lives
     inside the warning footnote so it reads as part of the
     "response was truncated" explanation. Quiet styling so it doesn't
     compete with the actual reply above. */
  .msg-continue-btn {
    display: inline-block;
    padding: 2px 8px;
    margin-left: 6px;
    border: 1px solid var(--accent-soft);
    background: transparent;
    color: var(--accent);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 500;
    font-style: normal;
    text-transform: none;
    letter-spacing: 0;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  }
  .msg-continue-btn:hover {
    background: var(--accent);
    color: var(--surface);
    border-color: var(--accent);
  }

  .msg-cost-footnote {
    display: flex;
    justify-content: flex-end;
    padding: 0 4px;
    margin-top: 4px;
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-faint);
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
  .msg-cost-footnote .arrow-glyph {
    color: var(--ink-ghost);
    font-family: var(--font-display);
    font-style: italic;
    margin-right: 5px;
  }

  /* Streaming cursor — appended while AI is typing */
  .msg-body.streaming::after {
    content: '▊';
    display: inline-block;
    animation: blink 1s steps(2) infinite;
    color: var(--ink-faint);
    margin-left: 2px;
  }
  @keyframes blink { 50% { opacity: 0; } }

  /* User-message attachments */
  .msg-attachments-meta {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
    margin-top: 6px;
    align-self: flex-end;
    max-width: 80%;
  }
  .msg-attachments {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 6px;
    align-self: flex-end;
    max-width: 80%;
  }
  .msg-attachment {
    width: 120px;
    height: 120px;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--surface);
    flex-shrink: 0;
  }

  /* Audio attachments — break the 120x120 grid since an inline
     audio player needs full row width for its controls. Sits as
     its own row at the bottom of the bubble, file name on top. */
  .msg-attachment.msg-attachment-audio {
    width: 100%;
    height: auto;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: var(--surface-warm);
  }
  .msg-attachment-audio-name {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-soft);
    letter-spacing: 0.04em;
    word-break: break-all;
  }
  .msg-attachment-audio audio {
    width: 100%;
    height: 36px;
  }
  /* PDF/DOCX/text thumbs render at their natural size with filename pill */
  .msg-attachment:has(.file-thumb) {
    width: auto;
    height: auto;
    border: none;
    background: transparent;
    overflow: visible;
  }
  .msg-attachment img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .file-thumb {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 13px;
  }
  .file-thumb-icon {
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    padding: 4px 6px;
    background: #b91c1c;
    color: #fff;
    border-radius: 4px;
    letter-spacing: 0.04em;
  }
  .file-thumb-name {
    color: var(--ink-soft);
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* AI-generated file cards (rendered under assistant messages) */
  .msg-files {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 10px;
    align-self: flex-start;
    max-width: 380px;
  }
  /* Wider container when the file is a generated image — files are
     download cards (small), images are real previews (bigger). */
  .msg-files:has(.msg-image) {
    max-width: 560px;
  }

  /* Generated image inline render. Sits in .msg-files as a sibling to
     any file cards (image gen never has both today, but the shape is
     forward-compatible with tool-call image gen alongside files). */
  .msg-image {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .msg-image img {
    width: 100%;
    max-width: 560px;
    max-height: 640px;
    object-fit: contain;
    display: block;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    background: var(--bg-2);
    cursor: zoom-in;
  }
  /* F7 — a generated image fades in once its pixels decode (JS adds
     .is-loaded on the img's load event) instead of popping in. */
  .msg-image img.msg-image-fade {
    opacity: 0;
    transition: opacity 0.45s ease;
  }
  .msg-image img.msg-image-fade.is-loaded { opacity: 1; }
  @media (prefers-reduced-motion: reduce) {
    .msg-image img.msg-image-fade { opacity: 1; transition: none; }
  }
  .msg-image.is-building {
    width: 320px;
    height: 240px;
    background: var(--bg-2);
    border: 1px dashed var(--border);
    border-radius: var(--radius-md);
    align-items: center;
    justify-content: center;
  }
  .msg-image-placeholder {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
  }
  .spinner-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--accent);
    animation: spinnerPulse 1.2s ease-in-out infinite;
  }
  @keyframes spinnerPulse {
    0%, 100% { opacity: 0.3; transform: scale(0.7); }
    50%      { opacity: 1;   transform: scale(1); }
  }
  .msg-image-actions {
    display: flex;
    gap: 6px;
  }
  .msg-image-download {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--ink-soft);
    padding: 4px 12px;
    border-radius: var(--radius-sm);
    font-family: var(--font-ui);
    font-size: 12px;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
  }
  .msg-image-download:hover {
    border-color: var(--border-strong);
    color: var(--ink);
  }

  /* Generated voice / TTS inline render. Audio player inside a soft
     card with filename + download button — same affordance shape as
     .msg-image but optimised for the height of a native audio control. */
  .msg-audio {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 14px;
    background: var(--surface-warm);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    max-width: 480px;
  }
  .msg-audio.is-building {
    align-items: center;
    justify-content: center;
    min-height: 64px;
  }
  .msg-audio-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  .msg-audio-name {
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--ink-soft);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1;
  }
  .msg-audio-download {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--ink-soft);
    padding: 4px 12px;
    border-radius: var(--radius-sm);
    font-family: var(--font-ui);
    font-size: 12px;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
    flex-shrink: 0;
  }
  .msg-audio-download:hover {
    border-color: var(--border-strong);
    color: var(--ink);
  }
  .msg-audio-player {
    width: 100%;
    height: 38px;
  }
  .msg-audio-placeholder {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
  }

  /* Generated video — mirrors .msg-audio, with a <video> player. */
  .msg-video {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 14px;
    background: var(--surface-warm);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    max-width: 560px;
  }
  .msg-video.is-building {
    align-items: center;
    justify-content: center;
    min-height: 96px;
  }
  .msg-video-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  .msg-video-name {
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--ink-soft);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1;
  }
  .msg-video-download {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--ink-soft);
    padding: 4px 12px;
    border-radius: var(--radius-sm);
    font-family: var(--font-ui);
    font-size: 12px;
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
    flex-shrink: 0;
  }
  .msg-video-download:hover {
    border-color: var(--border-strong);
    color: var(--ink);
  }
  /* Aspect-cast frame: sized to the video's true ratio in JS (buildVideoFrame),
     so portrait/landscape/square all sit snug with no letterbox. Neutral warm
     backing (never a black void) + soft fade-in once sized. */
  .msg-video-frame {
    width: 100%;
    max-width: 520px;
    aspect-ratio: 16 / 9;        /* default until metadata sets the true ratio */
    align-self: center;
    margin: 0 auto;
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--bg-3);
    box-shadow: var(--shadow-sm);
    opacity: 0;
    transition: opacity 0.35s ease;
  }
  .msg-video-frame.is-ready { opacity: 1; }
  .msg-video-player {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;           /* frame == true aspect, so this never crops */
    border-radius: inherit;
    background: transparent;
  }
  @media (prefers-reduced-motion: reduce) {
    .msg-video-frame { transition: none; opacity: 1; }
  }
  .msg-video-placeholder {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--ink-faint);
    font-family: var(--font-ui);
    font-size: 13px;
  }

  .msg-file-card {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    padding: 10px 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow-xs);
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
    animation: fileCardIn 0.25s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .msg-file-card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-sm);
  }
  .msg-file-card.is-building {
    opacity: 0.8;
  }
  .msg-file-card.is-clickable {
    cursor: pointer;
  }
  /* Preview-affordance on the card itself — a sligthly stronger hover
     so users in the Saved files drawer see the click target without
     having to hunt. Subtle enough that in-chat cards still read calm. */
  .msg-file-card.is-clickable:hover {
    border-color: var(--accent-soft);
    background: var(--accent-softer);
  }
  /* Action row holding Preview + Download side by side. */
  .file-card-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
  }
  /* In the narrow Saved files drawer, badge + name + buttons on one row
     squeezed the filename to an immediate ellipsis. Drop the buttons onto
     their own full-width row beneath so the name sits above with room. */
  #artifacts-list .file-card-actions {
    flex-basis: 100%;
    justify-content: flex-end;
  }
  /* Secondary button — same shape as Download but lighter so primary
     action (Download) stays the clear default. */
  .file-card-action--secondary {
    background: transparent;
    border-color: var(--border);
    color: var(--ink-soft);
  }
  .file-card-action--secondary:hover {
    background: var(--accent-softer);
    border-color: var(--accent-soft);
    color: var(--accent);
  }
  /* Inline audio player — appended to the card on Play, removed on Hide.
     flex-basis: 100% forces it onto its own row underneath the badge /
     info / actions cluster so it has full width to render the controls. */
  .file-card-audio {
    flex-basis: 100%;
    width: 100%;
    height: 36px;
    margin-top: 2px;
  }
  /* In a file card the aspect-cast frame (.msg-video-frame) does the sizing;
     this just gives it its own full-width row with a little breathing room. */
  .file-card-video {
    flex-basis: 100%;
    margin-top: 6px;
  }
  .msg-file-card.is-error {
    border-color: rgba(185, 28, 28, 0.3);
    background: rgba(185, 28, 28, 0.03);
  }
  @keyframes fileCardIn {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .file-card-badge {
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    padding: 6px 8px;
    border-radius: 6px;
    color: #fff;
    background: var(--accent);
    flex-shrink: 0;
    min-width: 48px;
    text-align: center;
  }
  /* Format-specific badge tints */
  .file-card-badge[data-format="docx"] { background: #2557a7; }
  .file-card-badge[data-format="pdf"]  { background: #b91c1c; }
  .file-card-badge[data-format="xlsx"] { background: #1d6f42; }
  .file-card-badge[data-format="csv"]  { background: #1d6f42; }
  .file-card-badge[data-format="pptx"] { background: #c44a1c; }
  .file-card-badge[data-format="md"],
  .file-card-badge[data-format="txt"]  { background: var(--ink-soft); }
  .file-card-badge[data-format="html"] { background: #6d28d9; }
  .file-card-badge[data-format="json"] { background: var(--ink); }

  .file-card-info {
    flex: 1;
    min-width: 0;
  }
  .file-card-name {
    font-size: 14px;
    color: var(--ink);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .file-card-meta {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
    margin-top: 2px;
    letter-spacing: 0.02em;
  }
  .file-card-action {
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--border-strong);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 500;
    padding: 6px 12px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .file-card-action:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
  }
  .file-card-action:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
  .file-card-spinner {
    width: 14px;
    height: 14px;
    border: 2px solid var(--ink-ghost);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: fileSpin 0.8s linear infinite;
    flex-shrink: 0;
  }
  @keyframes fileSpin {
    to { transform: rotate(360deg); }
  }

  /* ── Composer additions ── */
  .composer textarea#chat-input { /* same look as 19b's textarea */ }

  .composer-actions-right .cost-preview.hidden { display: none; }

  /* Model dropdown — opens upward from the model picker */
  .composer-actions-right { position: relative; }
  .model-dropdown {
    position: absolute;
    bottom: calc(100% + 10px);
    right: 0;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 6px;
    min-width: 280px;
    max-height: 400px;
    overflow-y: auto;
    z-index: 50;
    display: none;
  }
  .model-dropdown.open { display: block; }
  .model-dropdown-header {
    font-family: var(--font-mono);
    font-size: 9.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-faint);
    padding: 6px 10px 4px;
  }
  .model-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 10px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.12s ease;
    background: transparent;
    border: none;
    width: 100%;
    text-align: left;
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: 13.5px;
  }
  .model-option:hover { background: rgba(26, 22, 18, 0.05); }
  /* Selected row stays shaded so the current model / video length-resolution-
     shape choice is visible on reopen. JS marks it `.active`; `.selected` is
     kept for older call sites. */
  .model-option.selected,
  .model-option.active { background: var(--accent-soft); }
  /* Voice-mode dropdown groups voices under a provider header so the
     two-tier lineup (premium vs cheap) is scannable at a glance. */
  .voice-group-header {
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    color: var(--ink-faint);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 12px 10px 4px;
  }
  .voice-group-header:first-child { padding-top: 4px; }
  .voice-group-meta {
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0.02em;
    color: var(--ink-ghost);
  }
  .model-option-cost {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }
  /* Pass2-batch-1: tier label replaces the old credit indicator. Same
     visual treatment (mono + faint) so the row layout reads identically. */
  .model-option-tier {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
    text-transform: lowercase;
    letter-spacing: 0.02em;
    flex-shrink: 0;
  }
  /* Free-tier pill — a small positive badge so the free models pop. */
  .model-option-free {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--success-text);
    background: var(--success-soft);
    border: 1px solid var(--success-soft);
    padding: 2px 7px;
    border-radius: var(--radius-pill);
  }

  /* ── "See all models" overlay ───────────────────────────────
     A centered modal over the chat (built in models-overlay.js). Floats above
     everything; never touches #chat-messages, so closing returns the user to
     the exact same conversation view. Mechanics mirror the credits drawer. */
  .models-overlay-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(26, 22, 18, 0.22);
    backdrop-filter: blur(2px);
    opacity: 0;
    transition: opacity 0.24s ease;
    z-index: 1000;
  }
  .models-overlay-backdrop.open { opacity: 1; }
  .models-overlay-backdrop.hidden { display: none; }

  .models-overlay {
    position: fixed;
    top: 50%;
    left: 50%;
    width: min(960px, calc(100vw - 48px));
    max-height: 85vh;
    transform: translate(-50%, -48%) scale(0.98);
    opacity: 0;
    background: var(--bg-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-panel);
    z-index: 1001;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: opacity 0.24s ease, transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .models-overlay.open { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  .models-overlay.hidden { display: none; }

  .models-overlay-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 22px;
    border-bottom: 1px solid var(--border-divider);
    flex-shrink: 0;
  }
  .models-overlay-title {
    font-family: var(--font-display);
    font-size: 20px;
    color: var(--ink);
    margin: 0;
  }
  .models-overlay-close {
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    border-radius: 8px;
    font-size: 22px;
    line-height: 1;
    color: var(--ink-faint);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
  }
  .models-overlay-close:hover { background: rgba(26, 22, 18, 0.05); color: var(--ink); }

  .models-overlay-body {
    overflow-y: auto;
    padding: 20px 22px 26px;
  }
  .mov-loading {
    padding: 40px 0;
    text-align: center;
    color: var(--ink-faint);
    font-family: var(--font-mono);
    font-size: 12px;
  }

  .mov-section { margin-bottom: 26px; }
  .mov-section-header { margin-bottom: 12px; }
  .mov-section-title {
    font-family: var(--font-display);
    font-size: 16px;
    color: var(--ink);
  }
  .mov-section-sub {
    font-size: 12.5px;
    color: var(--ink-faint);
    margin-top: 2px;
  }
  .mov-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
    align-items: start;
  }

  .mov-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 14px 16px;
    width: 100%;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  .mov-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-xs); }
  .mov-card--static { cursor: default; }
  .mov-card--static:hover { border-color: var(--border); box-shadow: none; }
  .mov-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
  }
  .mov-card-provider {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.06em;
    color: var(--ink-ghost);
    text-transform: uppercase;
  }
  .mov-card-name {
    font-family: var(--font-display);
    font-size: 18px;
    color: var(--ink);
    line-height: 1.2;
    margin-bottom: 6px;
  }
  .mov-card-strength {
    font-size: 13.5px;
    color: var(--ink-soft);
    line-height: 1.45;
  }
  /* Right-side tag group in the card header: speed tag + cost chip. */
  .mov-card-tags {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
  }
  .mov-speed {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.04em;
    padding: 2px 8px;
    border-radius: 999px;
    color: var(--ink-faint);
    background: rgba(26, 22, 18, 0.04);
    border: 1px solid var(--border);
    white-space: nowrap;
  }
  .mov-speed--fast { color: var(--success-text, var(--success)); border-color: rgba(90, 122, 82, 0.28); }
  .mov-speed--deliberate { color: var(--accent); border-color: var(--accent-soft); }
  /* Concrete cost + specs line on media cards (mono, quiet). */
  .mov-card-spec {
    margin-top: 8px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.02em;
    color: var(--ink-faint);
    line-height: 1.5;
  }
  .mov-chip {
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    padding: 2px 9px;
    border-radius: 999px;
    white-space: nowrap;
    color: var(--ink-faint);
    background: rgba(26, 22, 18, 0.04);
    border: 1px solid var(--border);
  }
  .mov-chip.cost--high {
    color: var(--warning-text, var(--warning));
    background: var(--warning-soft, rgba(184, 124, 29, 0.10));
    border-color: rgba(184, 124, 29, 0.25);
  }
  .mov-chip.cost--highest {
    color: var(--error-text, var(--error));
    background: var(--error-soft, rgba(168, 69, 63, 0.10));
    border-color: rgba(168, 69, 63, 0.28);
    font-weight: 500;
  }
  .mov-card-detail {
    display: none;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border-divider);
  }
  .mov-card.is-open .mov-card-detail { display: block; }
  .mov-detail-role {
    font-size: 13.5px;
    color: var(--ink);
    line-height: 1.55;
    margin-bottom: 16px;
  }
  .mov-detail-col { margin-bottom: 12px; }
  .mov-detail-col h4 {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 4px;
  }
  .mov-detail-col p {
    font-size: 13px;
    color: var(--ink-soft);
    line-height: 1.5;
    margin: 0;
  }
  .mov-detail-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border-divider);
  }
  .mov-badge {
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.03em;
    color: var(--ink-soft);
    padding: 3px 8px;
    border-radius: 999px;
    background: rgba(26, 22, 18, 0.04);
    border: 1px solid var(--border);
  }
  .mov-detail-cost {
    margin-top: 10px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.02em;
    color: var(--ink-faint);
    line-height: 1.5;
  }
  .mov-card-toggle {
    display: flex;
    align-items: center;
    gap: 5px;
    margin-top: 12px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    color: var(--ink-faint);
  }
  .mov-card-toggle svg { width: 13px; height: 13px; transition: transform 0.18s ease; }
  .mov-card.is-open .mov-card-toggle svg { transform: rotate(180deg); }
  .mov-card-toggle .toggle-less { display: none; }
  .mov-card.is-open .toggle-more { display: none; }
  .mov-card.is-open .toggle-less { display: inline; }

  @media (max-width: 600px) {
    .models-overlay { width: calc(100vw - 24px); max-height: 90vh; }
    .mov-grid { grid-template-columns: 1fr; }
  }


  /* ── Notes-view (replaces messages when a note is open) ── */
  .notes-view {
    display: none;
    flex: 1;
    overflow-y: auto;
    padding: 32px 24px 48px;
  }
  .notes-view.active {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  /* Document-card styling: the editor sits as a white card centered on
     the canvas, matching the visual language of file-preview overlays
     without being a modal. Stays a stable editing surface — no "modal
     feels transient" worry. */
  .notes-wrapper {
    position: relative;
    max-width: 720px;
    width: 100%;
    margin: 0 auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: 56px 64px;
    box-sizing: border-box;
    animation: notes-pop-in 0.24s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  /* Compound selector (.notes-wrapper .notes-close) raises specificity
     above [data-tooltip] { position: relative; } which would otherwise
     override our absolute position and drop the × into normal flow
     next to the title. data-tooltip was added in the tooltip cleanup
     pass; this guard keeps absolute positioning. */
  .notes-wrapper .notes-close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: color 0.15s ease, background 0.15s ease;
  }
  .notes-close:hover {
    color: var(--ink);
    background: var(--accent-soft);
  }
  /* Delete sits bottom-right, deliberately far from the top-right close
     button so destructive intent reads as separate from "just close
     this." Hover tints red so the intent is unambiguous without being
     red by default — quiet at rest, clear on commit. */
  .notes-wrapper .notes-delete {
    position: absolute;
    bottom: 14px;
    right: 14px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--ink-faint);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .notes-delete:hover {
    color: var(--error);
    background: rgba(168, 69, 63, 0.10);
  }
  @keyframes notes-pop-in {
    from { opacity: 0; transform: translateY(8px) scale(0.985); }
    to   { opacity: 1; transform: translateY(0)   scale(1); }
  }
  .notes-title {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 32px;
    color: var(--ink);
    letter-spacing: -0.015em;
    width: 100%;
    border: none;
    background: transparent;
    padding: 0 0 12px;
    margin-bottom: 12px;
  }
  .notes-title:focus { outline: none; }
  .notes-title::placeholder { color: var(--ink-faint); }
  /* F1 — notes formatting toolbar */
  .notes-toolbar {
    display: flex;
    align-items: center;
    gap: 1px;
    padding: 2px 0 8px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--border-divider);
  }
  /* Uniform square buttons so the row reads as one even cluster, not a
     fladdrig mix of letters + glyphs of different widths. */
  .notes-tool {
    width: 30px;
    height: 30px;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 6px;
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s ease, color 0.12s ease;
  }
  .notes-tool:hover { background: var(--accent-softer); color: var(--ink); }
  .notes-tool em { font-style: italic; font-weight: 600; }
  .notes-tool svg { width: 16px; height: 16px; }
  /* Highlight indicator — a small yellow underline-swatch under the H, not a
     full box that reads as "always on". */
  .notes-tool-hl {
    position: relative;
    padding: 0 1px 2px;
    box-shadow: inset 0 -5px 0 #fbe7a1;
  }
  .notes-tool-sep {
    width: 1px;
    height: 16px;
    background: var(--border-divider);
    margin: 0 4px;
    flex-shrink: 0;
  }
  .notes-body {
    width: 100%;
    min-height: 50vh;
    border: none;
    background: transparent;
    font-family: var(--font-ui);
    font-size: 15px;
    line-height: 1.7;
    color: var(--ink);
    outline: none;
  }
  .notes-body:focus { outline: none; }
  /* contenteditable placeholder — shows while the editor has no content */
  .notes-body:empty:before {
    content: attr(data-placeholder);
    color: var(--ink-faint);
    pointer-events: none;
  }
  .notes-body mark,
  .notes-body span[style*="background"] {
    color: var(--ink);
    border-radius: 2px;
  }
  .notes-body ul,
  .notes-body ol { margin: 8px 0; padding-left: 28px; }
  .notes-body li { margin: 3px 0; }
  /* contenteditable wraps list-item content in <div>/<p> on some browsers,
     which knocks the markers out of vertical alignment — flatten that. */
  .notes-body li > div,
  .notes-body li > p { margin: 0; display: inline; }
  .notes-body p { margin: 0 0 10px; }
  .notes-body p:last-child { margin-bottom: 0; }

  /* ── Errors ── */
  /* Lives directly above the composer (between #autorefill-notice and
     .composer-wrap). Width-matched to .composer-inner (760px) so it
     reads as part of the input cluster, not as a stray banner. */
  .chat-error {
    max-width: 760px;
    margin: 0 auto 10px;
    padding: 10px 14px;
    background: #fef2f2;
    border: 1px solid #fecaca;
    border-radius: var(--radius-md);
    font-size: 13px;
    color: #991b1b;
    display: flex;
    align-items: flex-start;
    gap: 12px;
  }
  .chat-error-text { flex: 1; min-width: 0; line-height: 1.5; }
  .chat-error-close {
    background: transparent;
    border: none;
    color: currentColor;
    opacity: 0.7;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 4px;
    flex-shrink: 0;
  }
  .chat-error-close:hover { opacity: 1; background: rgba(0, 0, 0, 0.06); }
  .chat-error--soft {
    background: var(--accent-softer);
    border: 1px solid var(--accent-soft);
    color: var(--ink);
    padding: 14px 18px;
    display: flex;
    align-items: center;
    gap: 16px;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    animation: chat-error-soft-in 220ms ease-out;
  }
  .chat-error--soft .chat-error-cta {
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    padding: 6px 14px;
    border-radius: var(--radius-md);
    background: var(--surface);
    border: 1px solid var(--accent-soft);
    transition: all 0.15s ease;
  }
  .chat-error--soft .chat-error-cta:hover {
    background: var(--accent);
    color: var(--surface);
    border-color: var(--accent);
  }
  .chat-error--soft .chat-error-close {
    background: transparent;
    border: none;
    color: var(--ink-faint);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 4px;
  }
  .chat-error--soft .chat-error-close:hover {
    color: var(--ink);
    background: rgba(26, 22, 18, 0.06);
  }
  @keyframes chat-error-soft-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .chat-error--soft .chat-error-text { flex: 1; font-size: 13px; line-height: 1.5; }
  .chat-error--soft .chat-error-text strong { color: var(--ink); font-weight: 500; }


  /* ── Auto-refill notice (Fas 3 + 4) ─────────────────────────
     Slim chip that sits above the composer (between messages and input).
     Three variants with intentional color semantics:
       --info     blocking pre-send refill ("Topping up…") — pale accent
                  blue tint, refresh icon spinning. "Wait, we're working."
       --success  refilled, auto-fades after 8s — pale sage-green tint
                  with check icon. Green reads universally as "payment
                  completed"; sage hue (not bright kelly) keeps it on the
                  refined side of wazer's palette.
       --warn     paused / card declined — warm amber, persistent with ×
     Spinning icon class only on info; success/warn get a static icon. */
  .autorefill-notice {
    max-width: 720px;
    margin: 0 auto 12px;
    padding: 11px 18px;
    border-radius: 10px;
    font-size: 13px;
    line-height: 1.55;
    display: flex;
    align-items: center;
    gap: 12px;
    animation: autorefill-notice-in 220ms ease-out;
  }
  .autorefill-notice.hidden { display: none; }
  .autorefill-notice--info {
    background: rgba(30, 58, 95, 0.08);
    border: 1px solid rgba(30, 58, 95, 0.22);
    color: var(--ink-soft);
  }
  .autorefill-notice--info .autorefill-notice-icon { color: var(--accent, #1E3A5F); }
  .autorefill-notice--success {
    background: rgba(15, 122, 77, 0.10);
    border: 1px solid rgba(15, 122, 77, 0.30);
    color: var(--ink);
  }
  .autorefill-notice--success .autorefill-notice-icon { color: #0F7A4D; }
  .autorefill-notice--warn {
    background: var(--warning-soft);
    border: 1px solid var(--warning-soft);
    color: var(--warning-text);
  }
  .autorefill-notice--warn .autorefill-notice-icon { color: var(--warning); }
  .autorefill-notice-icon {
    display: inline-flex;
    flex-shrink: 0;
    width: 16px;
    height: 16px;
  }
  .autorefill-notice-icon.is-spinning {
    animation: autorefill-spin 1100ms linear infinite;
  }
  .autorefill-notice-text { flex: 1; }
  .autorefill-notice-text strong {
    font-weight: 600;
    color: var(--ink);
  }
  .autorefill-notice-meta {
    font-family: var(--font-mono, ui-monospace);
    font-size: 12px;
    color: var(--ink-soft);
    margin-left: 2px;
  }
  .autorefill-notice-faint { color: var(--ink-faint); margin-left: 4px; }
  .autorefill-notice-link {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    margin-left: 4px;
  }
  .autorefill-notice-link:hover { text-decoration: none; }
  .autorefill-notice-dots {
    display: inline-block;
    margin-left: 2px;
  }
  .autorefill-notice-close {
    appearance: none;
    background: none;
    border: none;
    color: inherit;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
    opacity: 0.6;
  }
  .autorefill-notice-close:hover { opacity: 1; }
  @keyframes autorefill-notice-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @keyframes autorefill-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
  }


  /* ── Welcome modal (first-visit) ── */
  .modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .modal-overlay.hidden { display: none !important; }
  .modal {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 28px;
    width: 100%;
    max-width: 480px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.18);
    max-height: calc(100vh - 40px);
    overflow-y: auto;
  }
  .modal h2 {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 26px;
    color: var(--ink);
    letter-spacing: -0.015em;
    margin: 0 0 8px;
  }
  .modal p {
    color: var(--ink-soft);
    margin: 0 0 16px;
    font-size: 15px;
    line-height: 1.55;
  }
  .modal-credits {
    background: var(--accent-soft);
    border: 1px solid var(--accent-soft);
    border-radius: 10px;
    padding: 12px 14px;
    margin: 12px 0;
    font-size: 14px;
    color: var(--ink);
  }
  .modal-credits strong {
    color: var(--accent);
    font-weight: 600;
  }
  .modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 20px;
  }
  .btn {
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 500;
    padding: 10px 18px;
    border-radius: 8px;
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: all 0.18s ease;
  }
  .btn-primary {
    background: var(--accent);
    color: #fff;
  }
  .btn-primary:hover {
    background: var(--accent-deep);
    transform: translateY(-1px);
  }
  .btn-lg { padding: 13px 24px; font-size: 15px; border-radius: 10px; }
  .btn-block { width: 100%; }

  /* Project-edit modal */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: 300;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .modal-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 28px;
    width: 100%;
    max-width: 480px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.18);
    max-height: calc(100vh - 40px);
    overflow-y: auto;
  }
  .modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
  }
  .modal-title {
    font-family: var(--font-display);
    font-size: 22px;
    color: var(--ink);
    letter-spacing: -0.01em;
  }
  .modal-close {
    background: transparent;
    border: none;
    cursor: pointer;
    color: var(--ink-faint);
    font-size: 20px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
  }
  .modal-close:hover {
    background: rgba(26, 22, 18, 0.05);
    color: var(--ink);
  }
  .modal-textarea {
    padding: 12px 14px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    font-size: 14px;
    font-family: inherit;
    resize: vertical;
    min-height: 80px;
    color: var(--ink);
    width: 100%;
  }
  .modal-textarea:focus {
    outline: none;
    border-color: var(--accent);
  }

  /* Make 19b's chat-input textarea behave with #chat-input id (used by send.js) */
  .composer textarea[id="chat-input"]::placeholder { color: var(--ink-faint); }

  /* Attachment strip — preserves space when files are queued */
  .attachment-strip {
    display: none;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 22px 0;
  }
  .attachment-strip.visible { display: flex; }

  /* Pre-send compatibility warning — shown when selected model can't
     read currently-attached files. Send button gets .compat-blocked
     which dims it and blocks the click handler. */
  .attach-compat-warning {
    margin: 10px 22px 0;
    padding: 7px 12px;
    background: rgba(184, 124, 29, 0.14);
    color: var(--warning-text, #8a5a14);
    border-left: 3px solid var(--warning, #B87C1D);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-family: var(--font-ui);
    font-size: 12.5px;
    line-height: 1.45;
  }
  .send-btn.compat-blocked {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .send-btn.compat-blocked:hover {
    background: var(--accent);
    transform: none;
  }

  /* Mobile hamburger that opens the sidebar drawer — hidden on desktop.
     Sized to a 40px tap target (the bare SVG was only 20×20). */
  .mobile-sidebar-toggle {
    display: none;
    width: 40px; height: 40px;
    margin-left: -8px;
    place-items: center;
    border: none;
    background: transparent;
    color: var(--ink-soft);
    border-radius: 8px;
    cursor: pointer;
  }
  .mobile-sidebar-toggle:active { background: var(--accent-soft); }

  /* Mobile */
  @media (max-width: 768px) {
    .app {
      grid-template-columns: 1fr;
      grid-template-areas:
        "topbar"
        "canvas";
    }
    /* The sidebar becomes an off-canvas drawer that slides in over the content,
       rather than being hidden — otherwise history / projects / notes / new
       chat are unreachable on mobile. */
    .sidebar {
      position: fixed;
      top: 0; left: 0; bottom: 0;
      width: min(86vw, 320px);
      z-index: 200;
      transform: translateX(-100%);
      transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.2, 1);
      background: var(--bg-1);
      box-shadow: var(--shadow-lg);
      will-change: transform;
    }
    .app.sidebar-open .sidebar { transform: translateX(0); }
    /* Dim backdrop behind the open drawer; tap to close. */
    .sidebar-backdrop {
      position: fixed; inset: 0;
      background: rgba(26, 22, 18, 0.4);
      z-index: 199;
      opacity: 0; pointer-events: none;
      transition: opacity 0.25s ease;
    }
    .app.sidebar-open .sidebar-backdrop { opacity: 1; pointer-events: auto; }
    .mobile-sidebar-toggle { display: grid; }
    /* Desktop collapse toggles are meaningless on mobile (the sidebar is a
       drawer, not a grid column) — hide the floating + in-drawer collapse btns. */
    .sidebar-toggle-floating,
    #sidebar-toggle-inside { display: none !important; }
    /* Credits-pill + avatar are fixed to the viewport's top-right on mobile
       (lifted out of the topbar flow), so a long title/number slid underneath
       them. Cap topbar-left's width to reserve that right strip — via max-width,
       NOT padding, so the topbar never grows past the viewport (padding widened
       it and zoomed the whole page out). #chat-title then ellipsis-truncates. */
    .topbar-left { max-width: calc(100% - 210px); }
    .breadcrumb .crumb.current { max-width: none; }
    .topbar-action span:not(.count) { display: none; }
  }



  /* ═════════════════════════════════════════════════════════
     EMPTY-STATE DESIGN REFRESH
     Centerpiece composer + 4 action cards + mono capability row.
     Replaces the simple greeting-only empty state.
     ═════════════════════════════════════════════════════════ */

  /* Tagline svävar fritt högt upp — absolute positioning så composer
     position inte påverkas. Subtil whisper-storlek, dämpad till
     --ink-faint så den knappt registreras men sätter ton.
     Magasin-stil fine print under nameplaten. */
  .chat-empty .empty-tagline {
    position: absolute;
    top: 6vh;
    left: 0;
    right: 0;
    margin: 0;
    font-family: var(--font-display);
    font-weight: 400;
    font-size: clamp(14px, 1.3vw, 16px);
    line-height: 1.4;
    letter-spacing: 0;
    color: var(--ink-faint);
    text-align: center;
    animation: messageRise 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
    /* Empty-state.js flips opacity every 20s to rotate to a new pool
       entry; this transition is what makes it feel calm rather than
       a blink. 1.2s matches the FADE_MS constant on the JS side. */
    transition: opacity 1.2s ease;
  }

  /* Composer placement preserved at the spot it held when the tagline
     was big and in-flow (~20vh + tagline-height + 56px margin).
     Tagline is now absolute-positioned, so we bake that height back
     into padding-top to keep composer exactly where it was.
     position:relative anchors the absolute tagline. */
  .chat-empty {
    padding: 28vh 24px 0 !important;
    transform: none !important;
    position: relative;
  }

  /* ── Centerpiece composer ─────────────────────────────── */
  .centerpiece-composer {
    width: 100%;
    max-width: 840px;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 18px;
    box-shadow:
      0 1px 2px rgba(26, 22, 18, 0.04),
      0 8px 28px rgba(26, 22, 18, 0.06);
    transition: all 0.22s ease;
    overflow: visible;
    text-align: left;
  }
  .centerpiece-composer:focus-within {
    border-color: var(--accent);
    box-shadow:
      0 1px 2px rgba(26, 22, 18, 0.04),
      0 12px 40px rgba(26, 22, 18, 0.08),
      0 0 0 3px var(--accent-soft);
  }
  .centerpiece-composer.mode-active {
    border-color: var(--accent);
    box-shadow:
      0 1px 2px rgba(26, 22, 18, 0.04),
      0 8px 28px rgba(26, 22, 18, 0.06),
      0 0 0 3px var(--accent-soft);
  }

  .mode-banner {
    display: none;
    align-items: center;
    gap: 8px;
    padding: 10px 18px 6px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 4px;
    font-family: var(--font-mono);
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--accent);
  }
  .mode-banner.active { display: flex; }
  .mode-banner .mode-icon {
    display: inline-grid;
    place-items: center;
    width: 14px;
    height: 14px;
    color: var(--accent);
  }
  .mode-banner .mode-icon svg { width: 14px; height: 14px; }
  .mode-banner .mode-label { flex: 1; }
  .mode-banner .mode-soon {
    font-family: var(--font-mono);
    font-size: 9.5px;
    color: var(--ink-faint);
    text-transform: none;
    letter-spacing: 0.04em;
    font-style: italic;
    margin-right: 6px;
  }
  .mode-banner .mode-close {
    width: 22px;
    height: 22px;
    border-radius: 5px;
    background: transparent;
    border: none;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
    transition: all 0.15s ease;
  }
  .mode-banner .mode-close:hover {
    background: rgba(26, 22, 18, 0.05);
    color: var(--ink);
  }

  .centerpiece-input {
    width: 100%;
    min-height: 64px;
    max-height: 280px;
    padding: 18px 26px 6px;
    border: none;
    background: transparent;
    font-family: var(--font-ui);
    font-size: 17px;
    line-height: 1.5;
    color: var(--ink);
    resize: none;
    outline: none;
  }
  .centerpiece-input::placeholder { color: var(--ink-faint); }

  /* Transcribe-mode picker — takes the slot the textarea normally
     occupies inside the centerpiece composer. Hidden by default;
     empty-state.js toggles it in/out as the user enters Transcribe
     mode. */
  .transcribe-block {
    padding: 22px 26px 4px;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  /* Restore hidden-attribute semantics — without this, our display:flex
     above wins over the UA stylesheet's [hidden] { display: none } and
     the picker stays visible even outside Transcribe mode. */
  .transcribe-block[hidden] { display: none; }
  .transcribe-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    padding: 22px 18px;
    background: var(--surface-warm);
    border: 1.5px dashed var(--border-strong);
    border-radius: 12px;
    color: var(--ink-soft);
    cursor: pointer;
    outline: none;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
  }
  .transcribe-dropzone:hover,
  .transcribe-dropzone:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-softer);
  }
  .transcribe-dropzone.is-drag-active {
    border-style: solid;
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
  }
  .transcribe-dropzone[hidden] { display: none; }
  .transcribe-dropzone-icon {
    stroke: currentColor;
    opacity: 0.85;
  }
  .transcribe-dropzone-label {
    font-family: var(--font-ui);
    font-size: 13.5px;
    font-weight: 500;
    color: var(--ink);
  }
  .transcribe-dropzone:hover .transcribe-dropzone-label,
  .transcribe-dropzone:focus-visible .transcribe-dropzone-label,
  .transcribe-dropzone.is-drag-active .transcribe-dropzone-label {
    color: var(--accent);
  }
  .transcribe-dropzone-hint {
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--ink-faint);
    letter-spacing: 0.02em;
    text-align: center;
  }
  .transcribe-file-info {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 8px 8px 8px 14px;
    background: var(--surface-warm);
    border: 1px solid var(--border);
    border-radius: 10px;
    align-self: flex-start;
    max-width: 100%;
  }
  .transcribe-file-info[hidden] { display: none; }
  .transcribe-file-name {
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 360px;
  }
  .transcribe-file-size {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-faint);
  }
  .transcribe-file-clear {
    width: 22px;
    height: 22px;
    border-radius: 5px;
    background: transparent;
    border: none;
    color: var(--ink-faint);
    cursor: pointer;
    font-size: 13px;
    line-height: 1;
  }
  .transcribe-file-clear:hover {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink);
  }
  .centerpiece-actions {
    display: flex;
    align-items: center;
    padding: 8px 14px 12px;
  }

  .centerpiece-attach {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    display: grid;
    place-items: center;
    background: transparent;
    border: none;
    color: var(--ink-faint);
    cursor: pointer;
    transition: all 0.15s ease;
  }
  .centerpiece-attach:hover {
    background: rgba(26, 22, 18, 0.05);
    color: var(--ink-soft);
  }

  /* Mid group — model picker + (optional) cost estimate. Sits between
     paperclip (left) and send (right) so the actions-row reads as three
     evenly-spaced tyngdpunkter instead of paperclip-alone vs send-side.
     Spacing between model + cost lives as margin-left on the cost
     block (not parent gap) so we can animate it collapsing to 0 when
     the composer is empty. */
  .centerpiece-mid {
    display: flex;
    align-items: center;
    /* Push mid + send to the right so the icon-buttons on the left
       (attach + mic) stay grouped together instead of getting spread
       out by justify-content: space-between. */
    margin-left: auto;
    /* Allow the model + cost group to shrink so a long model name truncates
       instead of wrapping + clipping the cost estimate on narrow phones. */
    min-width: 0;
  }
  .centerpiece-mid .cost-preview-block {
    margin-left: 12px;
  }

  .centerpiece-model {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid transparent;
    border-radius: 100px;
    font-family: var(--font-ui);
    font-size: 13px;
    color: var(--ink-soft);
    cursor: pointer;
    transition: all 0.15s ease;
    user-select: none;
    /* The model picker is the important, tappable element — it never shrinks.
       The cost estimate yields space instead (see .centerpiece-cost). */
    flex-shrink: 0;
    white-space: nowrap;
  }
  .centerpiece-model .chev { flex-shrink: 0; }
  .centerpiece-model:hover {
    background: rgba(26, 22, 18, 0.04);
    color: var(--ink);
  }
  .centerpiece-model strong {
    font-weight: 600;
    color: var(--ink);
    /* Show the name in full; only an unusually long one truncates (last resort
       so it can never push the cost/send off-screen). */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 150px;
  }
  .centerpiece-model .model-sub {
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--ink-faint);
    margin-left: -2px;
  }
  .centerpiece-model .chev { color: var(--ink-faint); }

  .centerpiece-cost {
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--ink-faint);
    user-select: none;
    /* Yields space before the model picker does — shrinks/clips if the row
       gets tight (e.g. large system font) so the model name stays intact. */
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
  }
  .centerpiece-cost strong {
    font-weight: 500;
    color: var(--ink-soft);
  }

  .centerpiece-send {
    width: 36px;
    height: 36px;
    padding: 0;
    flex-shrink: 0;
    border-radius: var(--radius-md);
    background: var(--accent);
    color: white;
    border: none;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background 0.18s ease, transform 0.18s ease;
    flex-shrink: 0;
    /* Breathing room between the credit estimate and the send button. */
    margin-left: 14px;
  }
  .centerpiece-send:hover {
    background: var(--accent-deep);
    transform: translateY(-1px);
  }
  .centerpiece-send .send-svg {
    transition: transform 0.18s ease;
  }
  .centerpiece-send:hover .send-svg {
    transform: translate(1px, -1px);
  }
  .centerpiece-send:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }
  .centerpiece-send:disabled:hover {
    background: var(--accent);
    transform: none;
  }
  .centerpiece-send:disabled:hover .send-svg {
    transform: none;
  }
  .centerpiece-attach { flex-shrink: 0; }

  /* Narrow phones: free up enough room that the model name shows in full (it's
     the important, tappable element) by making everything else compact — the
     estimate shrinks to just "~0.7" (no "Est."/"credits" labels, like the
     bottom composer) and the Auto sub-label is dropped. */
  @media (max-width: 640px) {
    .centerpiece-actions { padding: 8px 10px 12px; }
    .centerpiece-attach { width: 32px; height: 32px; }
    .centerpiece-model { padding: 6px 8px; }
    .centerpiece-model .model-sub { display: none; }
    .centerpiece-mid .cost-preview-block { margin-left: 10px; }
    .centerpiece-cost .cost-est-label,
    .centerpiece-cost .cost-credits-label { display: none; }
  }

  /* ── Action cards ───────────────────────────────────────── */
  .action-cards {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
    width: 100%;
    max-width: 840px;
    margin-top: 22px;
  }
  .action-card {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 8px 12px;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 10px;
    cursor: pointer;
    transition: all 0.18s ease;
    font-family: var(--font-ui);
    font-size: 12.5px;
    text-align: center;
    color: var(--ink-soft);
    position: relative;
  }
  .action-card:hover {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--ink);
  }
  .action-card.active {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-soft);
  }
  .action-card .action-icon {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 7px;
    background: rgba(26, 22, 18, 0.04);
    color: var(--ink-soft);
    transition: all 0.18s ease;
  }
  .action-card:hover .action-icon {
    background: rgba(26, 22, 18, 0.06);
    color: var(--ink);
  }
  .action-card.active .action-icon {
    background: var(--accent);
    color: var(--surface);
  }
  .action-card .action-label {
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.2;
  }

  /* ── Custom tooltips ────────────────────────────────────
     Use [data-tooltip="..."] on a button to opt into a styled
     tooltip instead of the OS default `title=...` chip. Title is
     reserved for elements that haven't been migrated; aria-label
     should carry the accessibility text in either case. */
  [data-tooltip] { position: relative; }
  [data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%) translateY(2px);
    background: var(--ink);
    color: var(--surface);
    padding: 5px 9px;
    border-radius: 5px;
    font-family: var(--font-mono);
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: 0.04em;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    /* No delay on disappear (snappy hover-out), gentle delay on
       appear so the tooltip doesn't flash on incidental hovers. */
    transition: opacity 0.2s ease, transform 0.2s ease;
    z-index: 100;
  }
  [data-tooltip]:hover::after,
  [data-tooltip]:focus-visible::after {
    opacity: 0.95;
    transform: translateX(-50%) translateY(6px);
    transition-delay: 0.35s;
  }

  /* Right-aligned tooltip: anchored to the right edge of the trigger
     so it grows leftward instead of centred. Opt-in via
     data-tooltip-align="right". Use on triggers that sit near the
     right edge of the viewport (or a clipping container) where a
     centred tooltip would overflow. */
  [data-tooltip][data-tooltip-align="right"]::after {
    left: auto;
    right: 0;
    transform: translateY(2px);
  }
  [data-tooltip][data-tooltip-align="right"]:hover::after,
  [data-tooltip][data-tooltip-align="right"]:focus-visible::after {
    transform: translateY(6px);
  }

  /* Position above the trigger instead of below. Use for buttons that
     sit close to the bottom edge of the viewport (bottom composer,
     compare composer) where a downward tooltip would clip. */
  [data-tooltip][data-tooltip-position="up"]::after {
    top: auto;
    bottom: 100%;
    transform: translateX(-50%) translateY(-2px);
  }
  [data-tooltip][data-tooltip-position="up"][data-tooltip-align="right"]::after {
    transform: translateY(-2px);
  }
  [data-tooltip][data-tooltip-position="up"]:hover::after,
  [data-tooltip][data-tooltip-position="up"]:focus-visible::after {
    transform: translateX(-50%) translateY(-6px);
  }
  [data-tooltip][data-tooltip-position="up"][data-tooltip-align="right"]:hover::after,
  [data-tooltip][data-tooltip-position="up"][data-tooltip-align="right"]:focus-visible::after {
    transform: translateY(-6px);
  }
  /* Touch devices have no hover, so tapping a button latches its :hover state
     and the tooltip sticks open (e.g. "Take a photo" lingering after capture).
     Disable the CSS tooltip entirely where there's no real hover. */
  @media (hover: none) {
    [data-tooltip]::after { content: none !important; display: none !important; }
  }
  /* Credits pill sits in the topbar; the floating buttons below it
     have z-index 20, so the tooltip needs its own stacking context to
     render above them when it drops down. */
  .credits-pill[data-tooltip] {
    position: relative;
    z-index: 25;
  }

  /* Cost-preview-block tooltip — the explanation text is too long to
     ride the default white-space: nowrap (which collapses it into a
     single line that overflows the viewport). Allow wrapping + cap
     the width so the tooltip renders as a small explanatory card.
     The whole "Est. N credits"-block is the hover target, so users
     don't need a separate ⓘ-icon to know help is available. */
  .cost-preview-block[data-tooltip] {
    cursor: help;
  }
  .cost-preview-block[data-tooltip]::after {
    white-space: normal;
    width: 220px;
    text-align: left;
    font-family: var(--font-ui);
    font-size: 11.5px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.4;
  }

  /* ── Wide secondary action (Compare) ───────────────────── */
  /* Sits below the trio of primary action cards. Full-width and
     visually quieter than the cards because Compare is a meta-feature
     (run across models) rather than a top-level capability. */
  /* Compare + Debate share one row, aligned to the capability-card grid. */
  .action-wide-row {
    display: flex;
    gap: 10px;
    width: 100%;
    max-width: 840px;
    margin: 10px auto 0;
  }
  .action-wide {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    padding: 8px 14px;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--ink-soft);
    font-family: var(--font-ui);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
  }
  .action-wide:hover {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--ink);
  }
  .action-wide-icon {
    display: inline-grid;
    place-items: center;
    color: var(--ink-faint);
    transition: color 0.15s ease;
  }
  .action-wide:hover .action-wide-icon { color: var(--ink); }
  .action-wide-label { flex: 1; text-align: center; }
  .action-wide-arrow {
    color: var(--ink-ghost);
    transition: color 0.15s ease, transform 0.18s ease;
  }
  .action-wide:hover .action-wide-arrow {
    color: var(--ink-soft);
    transform: translateX(2px);
  }

  /* ── Capability mono row ────────────────────────────────── */
  .capability-row {
    margin-top: 20px;
    font-family: var(--font-mono);
    font-size: 10.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--ink-faint);
    user-select: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
    max-width: 720px;
    line-height: 1.6;
  }
  .capability-row .dot {
    color: var(--ink-ghost);
    opacity: 0.7;
  }

  @media (max-width: 768px) {
    .action-cards { grid-template-columns: repeat(2, 1fr); }
    .action-wide-row { flex-direction: column; }
  }

  /* Hide bottom composer while empty state is visible — centerpiece owns the screen.
     When messages.js renders messages, .chat-empty gets `.hidden` and bottom composer reappears.
     Requires :has() (Chrome/Safari/Firefox 2023+). */
  body:has(#empty-state:not(.hidden)) .composer-wrap {
    display: none;
  }


  /* ============================================================
     pass2-batch-1 styles — additive, appended at end of file
     ============================================================ */

  /* ── Action card "Coming soon" badge ──────────────────────── */
  /* Small chip in top-right of each non-shipped action card.
     Used until the corresponding feature lands. Hidden when the
     card is in active mode (user clicked it) so it doesn't
     duplicate the "arrives soon" notice in the mode banner. */
  .action-card.has-coming-soon {
    padding-right: 88px;  /* leave room for the badge so it doesn't overlap label */
  }
  .action-card .action-coming-soon {
    position: absolute;
    top: 50%;
    right: 12px;
    transform: translateY(-50%);
    font-family: var(--font-mono);
    font-size: 9.5px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--ink-faint);
    background: rgba(26, 22, 18, 0.04);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 2px 6px;
    line-height: 1.2;
    white-space: nowrap;
    pointer-events: none;
    transition: all 0.18s ease;
  }
  .action-card:hover .action-coming-soon {
    color: var(--ink-soft);
    border-color: var(--border-strong);
  }
  .action-card.active .action-coming-soon {
    /* When user activates the mode, hide the chip — banner shows full notice */
    display: none;
  }


  /* ── Capability row: deliberately non-interactive ─────────── */
  /* Was rendering as if clickable. Now it's clearly text. */
  .capability-row {
    cursor: default;
    user-select: none;
    opacity: 0.7;
  }
  .capability-row span {
    cursor: default;
  }


  /* ── Cost preview block: Est. label + (i) tooltip ─────────── */
  /* Replaces the older "~ N credits" treatment. The "Est." prefix
     makes it self-documenting; the (i) icon offers more detail on
     hover/tap; the tooltip is a CSS-only popover. */
  .cost-preview-block {
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    font-family: var(--font-mono);
    color: var(--ink-soft);
    position: relative;
    overflow: hidden;
    max-width: 240px;
    transition: opacity 0.2s ease, max-width 0.28s ease, margin-left 0.28s ease;
  }
  /* When the composer is empty, collapse the cost-estimate block
     completely (no layout space) so the model picker sits at the
     true centre of the action row. As soon as the user types the
     block expands and pushes the model picker gently to the left. */
  .cost-preview-block.is-empty {
    opacity: 0;
    max-width: 0;
    margin-left: 0 !important;
    pointer-events: none;
  }
  .cost-preview-block .cost-est-label {
    font-size: 11px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-faint);
    margin-right: 1px;
  }
  .cost-preview-block strong,
  .cost-preview-block .amount {
    font-weight: 500;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
  }
  .cost-preview-block .cost-credits-label {
    color: var(--ink-soft);
  }

  /* Long-chat cost warning. Triggers when the prompt+history+model
     combination crosses a threshold that turns a single turn into a
     painful spend. Color-shift on the amount draws the eye; tooltip
     explains and suggests a fix (cheaper model / fresh chat). */
  .cost-preview-block.is-warning .amount,
  .cost-preview-block.is-warning strong {
    color: var(--warning);
  }
  .cost-preview-block.is-warning .cost-est-label,
  .cost-preview-block.is-warning .cost-credits-label {
    color: var(--warning);
  }
  .cost-preview-block.is-danger .amount,
  .cost-preview-block.is-danger strong {
    color: var(--error);
  }
  .cost-preview-block.is-danger .cost-est-label,
  .cost-preview-block.is-danger .cost-credits-label {
    color: var(--error);
  }

  /* Composer-level banner — sits inside .composer-wrap above the
     textarea, same slot as #autorefill-notice. Appears only when the
     cost-estimate crosses the danger threshold. Warning level just
     uses the amber pill; the banner is reserved for "this is really
     getting expensive" so it stays meaningful. */
  .long-chat-banner {
    max-width: 760px;
    margin: 0 auto 10px;
    padding: 10px 14px;
    background: rgba(168, 69, 63, 0.06);
    border: 1px solid rgba(168, 69, 63, 0.18);
    border-radius: var(--radius-md);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink);
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .long-chat-banner.hidden { display: none; }
  .long-chat-banner-text { flex: 1; }
  .long-chat-banner-text strong { color: var(--error); font-weight: 500; }

  /* (i) info button + tooltip */
  .cost-info {
    /* Visually a button, but doesn't behave like an interactive
       send/cancel — purely an informational affordance. */
    appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    margin-left: 4px;
    cursor: help;
    color: var(--ink-faint);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    transition: color 0.18s ease, background 0.18s ease;
    position: relative;
    align-self: center;
  }
  .cost-info:hover,
  .cost-info:focus {
    color: var(--ink);
    background: rgba(26, 22, 18, 0.05);
    outline: none;
  }
  .cost-info-tooltip {
    position: absolute;
    bottom: calc(100% + 8px);
    right: -4px;
    width: 240px;
    padding: 8px 11px;
    background: var(--ink);
    color: var(--surface);
    font-family: var(--font-ui);
    font-size: 12px;
    line-height: 1.4;
    font-weight: 400;
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s ease, visibility 0s linear 0.18s;
    pointer-events: none;
    z-index: 100;
    text-transform: none;
    letter-spacing: normal;
  }
  .cost-info:hover .cost-info-tooltip,
  .cost-info:focus .cost-info-tooltip,
  .cost-info.first-time-pulse .cost-info-tooltip {
    opacity: 1;
    visibility: visible;
    transition: opacity 0.18s ease, visibility 0s;
  }
  /* Small caret pointing down from the tooltip toward the icon */
  .cost-info-tooltip::after {
    content: "";
    position: absolute;
    top: 100%;
    right: 8px;
    border: 5px solid transparent;
    border-top-color: var(--ink);
  }

  /* First-time pulse on (i) — subtly draws attention until first interaction.
     JS adds .first-time-pulse on first session, removes on click. */
  @keyframes cost-info-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(30, 58, 95, 0.4); }
    50%      { box-shadow: 0 0 0 6px rgba(30, 58, 95, 0); }
  }
  .cost-info.first-time-pulse {
    color: var(--accent, #1E3A5F);
    animation: cost-info-pulse 1.8s ease-in-out infinite;
  }



  /* ── Jump-to-latest button (smart auto-scroll) ────────────── */
  /* Shown when user has scrolled away from the bottom during/after
     streaming. Click → scroll to bottom and resume auto-follow. */
  .jump-to-bottom {
    position: absolute;
    bottom: 130px;
    left: 50%;
    transform: translateX(-50%);
    width: 34px;
    height: 34px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    color: var(--ink-soft);
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: all 0.18s ease;
    z-index: 10;
    opacity: 1;
  }
  .jump-to-bottom:hover {
    border-color: var(--accent);
    color: var(--accent);
    transform: translateX(-50%) translateY(-1px);
    box-shadow: var(--shadow-md);
  }
  .jump-to-bottom.hidden {
    opacity: 0;
    pointer-events: none;
    transform: translateX(-50%) translateY(8px);
  }
  .jump-to-bottom svg {
    flex-shrink: 0;
  }


  /* ── Floating private-mode toggle (Group B-6) ─────────────
     A small circular pill that hangs just below the topbar's bottom edge,
     right-aligned with the avatar. Always visible, always clickable.
     Uses the same #incognitoBtn id as the old sidebar version so
     incognito.js's event delegation continues to work without changes. */
  .private-toggle-floating,
  .notes-toggle-floating,
  .artifacts-toggle-floating {
    position: fixed;
    top: 64px;             /* topbar 56px + 8px gap */
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--floating-disc-bg);
    border: 0.5px solid var(--border-strong);
    color: var(--floating-disc-icon);
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: all 0.18s ease;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 20;
    padding: 0;
  }
  .private-toggle-floating   { right: 18px; }   /* matches avatar's right edge */
  .notes-toggle-floating     { right: 54px; }   /* 18 + 28 + 8 = left of private */
  .artifacts-toggle-floating { right: 90px; }   /* one more 28+8 step left */
  .notes-toggle-floating:hover,
  .artifacts-toggle-floating:hover {
    border-color: var(--accent);
    color: var(--accent);
    transform: translateY(-1px);
  }
  .notes-toggle-floating.active,
  .artifacts-toggle-floating.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
  }
  .private-toggle-floating:hover {
    border-color: var(--accent);
    color: var(--accent);
    transform: translateY(-1px);
  }
  .private-toggle-floating.active {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
  }
  .private-toggle-floating.active:hover {
    background: var(--accent-deep);
    border-color: var(--accent-deep);
  }
  .private-toggle-floating.is-locked {
    opacity: 0.45;
    cursor: not-allowed;
  }
  .private-toggle-floating.is-locked:hover {
    transform: none;
    border-color: var(--border-strong);
    color: var(--ink-faint);
  }
  .private-toggle-floating.locked-flash {
    animation: privateToggleShake 0.4s ease-in-out;
  }
  @keyframes privateToggleShake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-3px); }
    40% { transform: translateX(3px); }
    60% { transform: translateX(-2px); }
    80% { transform: translateX(2px); }
  }

  /* ── Touch tap targets ───────────────────────────────────
     On touch devices, bump the small chrome controls to the ~44px tap-target
     guideline (desktop pointer sizing is left untouched). The icons keep their
     size — only the hit area grows (place-items: center keeps them centred).
     The floating discs are respaced so their larger hit areas don't overlap.
     Placed after the disc rules so these class overrides win the cascade. */
  @media (pointer: coarse) {
    .private-toggle-floating,
    .notes-toggle-floating,
    .artifacts-toggle-floating {
      width: 44px; height: 44px;
      /* Drop the blur on touch — backdrop-filter over the scrolling chat
         repaints every frame and flickers/janks. Use a solid disc instead. */
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
      background: var(--surface);
    }
    .private-toggle-floating   { right: 14px; }
    .notes-toggle-floating     { right: 66px; }
    .artifacts-toggle-floating { right: 118px; }
    .icon-btn,
    .centerpiece-attach,
    .send-btn,
    .centerpiece-send { width: 44px; height: 44px; flex-shrink: 0; }
    /* Trim the row's side padding to win back the width the bigger buttons need,
       so the model label shows in full. The wrapper stays flexible (min-width: 0)
       as a safety so a very long model name clips instead of pushing the row. */
    .centerpiece-actions { padding-left: 8px; padding-right: 8px; }
    .centerpiece-mid { flex: 1 1 auto; min-width: 0; overflow: hidden; }
    .centerpiece-model { min-width: 0; }

    /* Floating discs fade out once the user scrolls into a conversation — on a
       narrow screen they otherwise sit over the message bubbles. They return at
       the top, where a little message clearance keeps the first bubble from
       starting under them. */
    body.chat-scrolled .private-toggle-floating,
    body.chat-scrolled .notes-toggle-floating,
    body.chat-scrolled .artifacts-toggle-floating {
      opacity: 0;
      pointer-events: none;
      transform: translateY(-6px);
    }
    #chat-messages { padding-top: 52px; }

    /* The model sub-label ("wazer picks the strongest model" / "wazer picks…")
       has no room on a phone — it wrapped and jammed between Auto and Send. Drop
       it on touch in BOTH composers; the model name alone is enough and the cost
       preview is shown separately. */
    #model-cost { display: none; }
    .centerpiece-model .model-sub { display: none; }

    /* A touch more air on the empty-state action grid for phones — taller cards
       (also friendlier tap targets) and a bit more breathing room around them. */
    .action-cards { gap: 12px; margin-top: 28px; }
    .action-card { padding: 14px 12px; }

    /* Use the phone's width better. The composers + message column were inset by
       two stacked layers of 24px side padding (chat-scroll + empty-state), so
       they only filled ~75%. Trim them: ~16px from the screen edge instead. */
    .chat-scroll { padding-left: 16px; padding-right: 16px; }
    /* .chat-empty sets its side padding with !important, so match it. */
    #empty-state { padding-left: 0 !important; padding-right: 0 !important; }
    .composer-wrap { padding-left: 16px; padding-right: 16px; }

    /* Compact composer on phones. The 64px textarea made the composer ~190px
       tall, which dominates the screen once the keyboard is up — squeezing the
       answer into a sliver. A shorter input leaves room for the conversation;
       it still grows up to max-height as you type. */
    .composer textarea { min-height: 44px; padding-top: 12px; }
  }

  /* Keyboard up = short viewport. Reclaim vertical space for the conversation:
     hide the disclaimer and make the composer ultra-compact, so a streamed
     answer isn't squeezed into a sliver. (After the pointer:coarse block so
     these win when both media queries match.) */
  @media (max-height: 480px) {
    .composer-disclaimer { display: none; }
    .composer textarea { min-height: 36px; padding-top: 10px; padding-bottom: 6px; }
    .composer-wrap { padding-top: 6px; padding-bottom: 8px; }
  }

  /* ============================================================
     F4 — "Customize Auto" model picker panel + dropdown entry
     ============================================================ */
  .auto-models-backdrop {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .auto-models-panel {
    width: 100%;
    max-width: 420px;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-panel);
    overflow: hidden;
    transform: translateY(8px);
    transition: transform 0.22s ease;
  }
  .auto-models-backdrop.open .auto-models-panel { transform: none; }
  .auto-models-head {
    padding: 18px 20px 12px;
    border-bottom: 1px solid var(--border-divider);
  }
  .auto-models-head h2 {
    margin: 0 0 4px;
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 18px;
    color: var(--ink);
  }
  .auto-models-head p {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--ink-soft);
  }
  .auto-models-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px;
  }
  .auto-models-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: 8px;
    cursor: pointer;
  }
  .auto-models-row:hover { background: rgba(26, 22, 18, 0.04); }
  .auto-models-row input {
    accent-color: var(--accent);
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    cursor: pointer;
  }
  .auto-models-name {
    flex: 1;
    font-size: 13.5px;
    color: var(--ink);
  }
  .auto-models-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 16px;
    border-top: 1px solid var(--border-divider);
  }
  .auto-models-actions-right { display: flex; gap: 8px; }
  .auto-models-actions button {
    font-family: var(--font-ui);
    font-size: 13px;
    padding: 8px 14px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--ink);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
  }
  .auto-models-reset {
    border-color: transparent;
    background: transparent;
    color: var(--ink-faint);
    padding-left: 4px;
  }
  .auto-models-reset:hover { color: var(--accent); }
  .auto-models-cancel:hover { background: var(--bg-2); }
  .auto-models-save {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
  }
  .auto-models-save:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
  /* ============================================================
     I2 — Project workspace (the project home, with the real centerpiece
     composer relocated in + editable name/context + chats & notes)
     ============================================================ */
  .project-workspace { width: 100%; max-width: 760px; margin: 0 auto; padding: 40px 20px 90px; }
  .project-workspace [contenteditable] { cursor: text; border-radius: 5px; transition: background .12s ease, box-shadow .12s ease; }
  .project-workspace [contenteditable]:hover { background: var(--accent-softer); }
  .project-workspace [contenteditable]:focus { outline: none; background: var(--surface); box-shadow: 0 0 0 2px var(--accent-soft); }
  .project-workspace [contenteditable]:empty:before { content: attr(data-placeholder); color: var(--ink-faint); }

  .pw-head { margin-bottom: 18px; }
  .pw-eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 6px; }
  .pw-name { font-family: var(--font-display); font-weight: 500; font-size: 30px; line-height: 1.15; margin: 0; padding: 2px 6px; }
  /* Big, calm "Start a new chat" button — the project home's primary action.
     Every mode (image/video/compare/debate) lives in the composer it opens. */
  .pw-new-chat {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    width: 100%; padding: 20px; margin-bottom: 28px;
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-xs);
    font-family: var(--font-ui); font-size: 16px; font-weight: 500; color: var(--ink);
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease, transform .12s ease;
  }
  .pw-new-chat:hover {
    border-color: var(--accent); background: var(--accent-softer);
    box-shadow: var(--shadow-sm);
  }
  .pw-new-chat:active { transform: translateY(1px); }
  /* The project home has no inline composer — the button is the only entry, so
     hide the bottom composer while the workspace is shown (it would otherwise
     reappear since the empty-state isn't mounted here). */
  body:has(.project-workspace) .composer-wrap { display: none; }
  /* The picker overlays (all projects / chats / notes) have no composer. */
  body:has(.project-view:not([hidden])) .composer-wrap { display: none; }

  /* All-projects picker — a card grid instead of a bare name list */
  .project-view-cards.proj-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px;
  }
  .proj-card {
    display: flex; flex-direction: column; gap: 8px;
    padding: 18px; border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--surface); cursor: pointer; text-align: left; min-width: 0;
    transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
  }
  .proj-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
  .proj-card-icon { color: var(--accent); display: inline-flex; }
  .proj-card-icon svg { width: 22px; height: 22px; }
  .proj-card-name { font-family: var(--font-ui); font-size: 15px; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .proj-card-meta { font-family: var(--font-mono); font-size: 11px; color: var(--ink-faint); }
  .proj-card-new { border-style: dashed; justify-content: center; box-shadow: none; }
  .proj-card-new .proj-card-icon { color: var(--ink-faint); }
  .proj-card-new .proj-card-name { color: var(--ink-soft); }
  .proj-card-new:hover { border-color: var(--accent); transform: none; }
  .proj-card-new:hover .proj-card-icon, .proj-card-new:hover .proj-card-name { color: var(--accent); }
  .pw-new-chat-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 50%;
    background: var(--accent); color: #fff;
  }
  .pw-new-chat-icon svg { width: 17px; height: 17px; }

  /* Project context — collapsed by default, directly under the composer */
  .pw-context { margin: 8px 0 30px; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface-warm); overflow: hidden; }
  .pw-context-toggle {
    display: flex; align-items: center; justify-content: space-between; width: 100%;
    padding: 11px 14px; background: none; border: none; cursor: pointer;
    font-family: var(--font-ui); font-size: 13px; color: var(--ink-soft); text-align: left;
  }
  .pw-context-toggle .chev { transition: transform .2s ease; flex-shrink: 0; }
  .pw-context.open .pw-context-toggle .chev { transform: rotate(180deg); }
  .pw-context-body { padding: 0 14px 14px; display: none; }
  .pw-context.open .pw-context-body { display: block; }
  .pw-context-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); margin: 12px 0 4px; }
  .pw-context-text { font-size: 13.5px; line-height: 1.6; color: var(--ink-soft); padding: 6px 8px; min-height: 1.6em; white-space: pre-wrap; }

  /* Chats + notes, side by side */
  /* Segmented Chats / Notes / Files — one panel at a time, airy + full width */
  .pw-segments {
    display: flex; gap: 2px;
    margin: 18px 0;
    border-bottom: 1px solid var(--border-divider);
  }
  .pw-seg {
    display: inline-flex; align-items: center; gap: 7px;
    padding: 9px 14px 12px; border: none; background: none; cursor: pointer;
    font-family: var(--font-ui); font-size: 14px; color: var(--ink-faint);
    border-bottom: 2px solid transparent; margin-bottom: -1px;
    transition: color .12s ease, border-color .12s ease;
  }
  .pw-seg:hover { color: var(--ink-soft); }
  .pw-seg.active { color: var(--ink); border-bottom-color: var(--accent); font-weight: 500; }
  .pw-seg-count {
    font-family: var(--font-mono); font-size: 11px; line-height: 1;
    color: var(--ink-faint); background: var(--bg-2);
    border-radius: 8px; padding: 3px 7px;
  }
  .pw-seg.active .pw-seg-count { background: var(--accent-soft); color: var(--accent); }
  .pw-panel.hidden { display: none; }
  .pw-panel-head { display: flex; justify-content: flex-end; margin-bottom: 10px; min-height: 20px; }
  .pw-add { font-family: var(--font-ui); font-size: 13px; color: var(--accent); background: none; border: none; cursor: pointer; padding: 2px 4px; }
  .pw-add:hover { color: var(--accent-deep); }
  .pw-list { display: flex; flex-direction: column; gap: 8px; }
  .pw-row {
    display: flex; align-items: center; gap: 11px; width: 100%;
    padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius-md);
    background: var(--surface); cursor: pointer; text-align: left;
    font-family: var(--font-ui); font-size: 14px; color: var(--ink);
    transition: border-color .12s ease, background .12s ease;
  }
  .pw-row:hover { border-color: var(--border-strong); background: var(--surface-warm); }
  .pw-row-icon { color: var(--ink-faint); display: inline-flex; flex-shrink: 0; }
  .pw-row-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pw-row-menu {
    flex-shrink: 0; border: none; background: none; cursor: pointer;
    color: var(--ink-faint); font-size: 16px; line-height: 1; padding: 2px 4px;
    border-radius: 5px; opacity: 0; transition: opacity .12s ease, background .12s ease, color .12s ease;
  }
  .pw-row:hover .pw-row-menu, .pw-row-menu.open { opacity: 1; }
  .pw-row-menu:hover { background: var(--accent-soft); color: var(--ink); }
  .pw-empty { font-size: 13px; color: var(--ink-faint); margin: 6px 2px; padding: 8px 0; }
  /* Files panel — the project's generated artifacts in a responsive card grid */
  .pw-files-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
  /* As in the Saved files drawer, drop the file-card buttons to their own row
     so the filename gets full width. */
  .project-workspace .file-card-actions { flex-basis: 100%; justify-content: flex-end; }
  @media (max-width: 640px) {
    .pw-files-grid { grid-template-columns: 1fr; }
    .project-workspace { padding-top: 24px; }
    .pw-seg { padding: 8px 11px 11px; }
  }

  /* The "Customize Auto…" entry beneath the Auto option in the model dropdown */
  .model-option.auto-customize { cursor: pointer; }
  .model-option.auto-customize .model-option-name {
    color: var(--accent);
    font-size: 12px;
  }
  .model-option.auto-customize:hover { background: var(--accent-softer); }



