  :root {
    --dc-blue: #4F46E5;
    /* Shades/tints below are all derived from --dc-blue via color-mix (read
       live at paint time), tuned to match the original fixed hex values
       exactly when --dc-blue is the default. A brand-color swatch pick
       therefore relights every hover/border/shadow/sidebar-bg that uses
       them too, instead of leaving 60+ spots stuck on the original blue. */
    --dc-blue-dark: color-mix(in srgb, var(--dc-blue) 70%, black);
    --dc-blue-deeper: color-mix(in srgb, var(--dc-blue) 45%, black);
    --dc-blue-light: color-mix(in srgb, var(--dc-blue) 10%, white);
    --dc-blue-mid: color-mix(in srgb, var(--dc-blue) 85%, white);
    --dc-blue-tint: color-mix(in srgb, var(--dc-blue) 65%, white);
    --dc-blue-tint-light: color-mix(in srgb, var(--dc-blue) 45%, white);
    /* --dc-accent is NOT a second brand hue. Every one of its ~55 uses means
       "the brand colour, but readable against this theme's ground" — that is a
       lightness job, not a hue job. It used to be a fixed cyan (#00A8E8), which
       is why a purple brand pick still left blue text scattered over the UI:
       the swatch only relit --dc-blue and this stayed cyan forever. Deriving it
       means one swatch pick carries every highlight, chip, icon and slider with
       it. Light mode deepens the brand so it holds contrast on white panels;
       dark mode lifts it off the violet ground (~5.2:1 on --bg-panel). */
    --dc-accent: color-mix(in srgb, var(--dc-blue) 92%, black);
    --dc-accent-light: color-mix(in srgb, var(--dc-blue) 10%, white);
    /* Rounding out the brand palette to match the DEVCON 17 mark's four-dot
       lockup (gold/orange/violet/green) so gradients can echo the logo
       instead of staying blue-only. Gold/orange/green are fixed — they are the
       logo's own dots and never read blue. Violet was the one dot that did
       (#7C5CFF is blue-dominant), so it now tracks the brand as a lighter
       companion; at the default brand it lands within a hair of the old hex. */
    --dc-violet: color-mix(in srgb, var(--dc-blue) 72%, white);
    --dc-gold: #FFC93C;
    --dc-orange: #FF8A3D;
    --dc-green: #5FBF6B;
    /* Light mode sits a few steps below paper-white so the white panels/
       bubbles laid on top of it read as raised surfaces instead of melting
       into the page — the old #F7F9FC was bright enough to glare and left
       cards with almost no edge. */
    /* The neutral scale used to be a blue-gray family (#EBEFF9/#D0DDF0/
       #8AA0B8/#4A6280/#0D1B2E). Against a purple brand every border, caption
       and page edge read faintly blue — the "cold cast" half of the problem.
       These are the violet-neutral counterparts, the same family the dark
       theme already uses, and each one is equal or better on contrast. */
    --bg-primary: #EDEBF7;
    --bg-panel: #FFFFFF;
    /* Was referenced in 5+ places (guidebook tab pills, settings fields) but
       never actually defined in either theme, so those surfaces silently
       resolved to transparent. */
    --bg-secondary: #F3F1FA;
    /* The sidebar/rail is a dark brand slab in both themes; a vertical
       blue→violet gradient (rather than a flat fill) ties it to the poster
       art and to the body glow behind it. */
    --bg-sidebar:
      linear-gradient(168deg,
        color-mix(in srgb, var(--dc-blue) 60%, black) 0%,
        color-mix(in srgb, var(--dc-blue) 42%, black) 52%,
        color-mix(in srgb, var(--dc-violet) 34%, black) 100%);
    --text-primary: #171232;
    --text-secondary: #5B5480;
    --text-muted: #948EB4;
    --border: #D7D2E8;
    --border-light: #EAE7F4;
    --user-bubble: var(--dc-blue);
    --ai-bubble: #FFFFFF;
    /* Failure states used to be red end to end — surface, border, headings,
       step numbers and buttons — which reads as alarm rather than as the
       explanation it actually is. Red is now reserved for the one glyph that
       has to say "something failed"; everything else sits on the normal
       surface scale. The tints are derived so a theme swap carries them. */
    --danger: #C2453F;
    --danger-tint: color-mix(in srgb, var(--danger) 12%, transparent);
    --danger-edge: color-mix(in srgb, var(--danger) 26%, transparent);
    --font-main: 'Plus Jakarta Sans', sans-serif;
    --font-mono: 'JetBrains Mono', monospace;
    --radius-sm: 8px;
    --radius-md: 14px;
    --radius-lg: 20px;
    --sidebar-w: 260px;
    --header-h: 92px;
    --shadow-card: 0 2px 12px color-mix(in srgb, var(--dc-blue) 8%, transparent);
    --shadow-input: 0 2px 20px color-mix(in srgb, var(--dc-blue) 12%, transparent);

    /* Sidebar ink — the panel is dark in BOTH themes, so its whites live in
       their own scale rather than following --text-*. They were mixed down
       far enough (section labels at 0.22, items at 0.55) that the nav read
       as permanently disabled; these are the legible floor. */
    --sb-text: rgba(255,255,255,0.78);
    --sb-text-strong: #FFFFFF;
    --sb-text-dim: rgba(255,255,255,0.55);
    --sb-text-faint: rgba(255,255,255,0.42);
    --sb-border: rgba(255,255,255,0.10);
    --sb-hover-bg: rgba(255,255,255,0.10);
    /* The sidebar and icon rail are dark in BOTH themes, so anything tinted on
       them needs the lifted accent even while the rest of the page is light —
       --dc-accent's light-mode value would disappear against the slab. */
    --sb-accent: color-mix(in srgb, var(--dc-blue) 55%, white);

    /* Ambient .bg-scene ink. Light mode can't use white dust (invisible on
       a near-white page) or gold (too pale), so both swap to saturated
       brand hues; dark mode keeps the original white/gold embers. */
    --scene-dust: var(--dc-violet);
    --scene-gold: var(--dc-orange);
  }

  /* DEVCON 17 dark theme — deep indigo/violet void (poster background) with
     the brand blue kept for interactive elements; gold is a sparing accent
     for special highlights, echoing the poster's "17" star. */
  [data-theme="dark"] {
    --bg-primary: #17132E;
    --bg-panel: #221C46;
    --bg-sidebar: linear-gradient(168deg, #1A1438 0%, #120E26 55%, #0D0A1F 100%);
    --scene-dust: #FFFFFF;
    --scene-gold: var(--dc-gold);
    --text-primary: #ECE9FA;
    --text-secondary: #9A93C2;
    --text-muted: #5C5484;
    --border: #362E63;
    --border-light: #261F4A;
    --user-bubble: var(--dc-blue);
    --ai-bubble: #221C46;
    --dc-blue-light: color-mix(in srgb, var(--dc-blue) 25%, var(--bg-panel));
    --dc-accent: color-mix(in srgb, var(--dc-blue) 62%, white);
    --dc-accent-light: color-mix(in srgb, var(--dc-blue) 10%, var(--bg-panel));
    --bg-secondary: #1B1539;
    /* Lifted well off the deep violet ground — the light-mode red goes muddy
       against it, and the tints need more weight to stay visible. */
    /* Lifted off the deep violet ground, but not washed out: the previous
       #F08A85 sat at the same hue and simply lost saturation, which lands on
       salmon and reads orange at glyph size. Saturation is what makes it read
       as red here, not hue. */
    --danger: #F0595B;
    --danger-tint: color-mix(in srgb, var(--danger) 15%, transparent);
    --danger-edge: color-mix(in srgb, var(--danger) 30%, transparent);
  }

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

  /* Soft violet/blue/gold glow over the void — same atmosphere as the DEVCON
     17 poster, kept subtle so text and cards stay the primary focus. */
  [data-theme="dark"] body {
    background:
      radial-gradient(1100px 750px at 12% -8%, color-mix(in srgb, var(--dc-violet) 22%, transparent), transparent 60%),
      radial-gradient(950px 700px at 92% 8%, color-mix(in srgb, var(--dc-accent) 16%, transparent), transparent 55%),
      radial-gradient(850px 650px at 100% 55%, rgba(255,138,61,0.10), transparent 55%),
      radial-gradient(900px 800px at 45% 115%, rgba(95,191,107,0.08), transparent 60%),
      radial-gradient(700px 600px at 0% 100%, rgba(255,201,60,0.07), transparent 55%),
      var(--bg-primary);
  }

  /* Light mode gets the same layered-glow treatment. The stops used to sit
     at 4–8% over a near-white base, which washed out to a flat glare; at
     roughly double the strength — plus a violet→blue diagonal wash under
     everything — the logo's gold/orange/violet/green actually read as
     colour while white panels and near-black text stay crisp on top. */
  body {
    font-family: var(--font-main);
    background:
      radial-gradient(1100px 750px at 12% -8%, color-mix(in srgb, var(--dc-violet) 20%, transparent), transparent 62%),
      radial-gradient(950px 700px at 92% 8%, color-mix(in srgb, var(--dc-accent) 16%, transparent), transparent 58%),
      radial-gradient(850px 650px at 100% 55%, color-mix(in srgb, var(--dc-orange) 12%, transparent), transparent 58%),
      radial-gradient(900px 800px at 45% 115%, color-mix(in srgb, var(--dc-green) 11%, transparent), transparent 60%),
      radial-gradient(700px 600px at 0% 100%, color-mix(in srgb, var(--dc-gold) 12%, transparent), transparent 58%),
      linear-gradient(150deg,
        color-mix(in srgb, var(--dc-violet) 9%, transparent) 0%,
        transparent 45%,
        color-mix(in srgb, var(--dc-blue) 8%, transparent) 100%),
      var(--bg-primary);
    color: var(--text-primary);
    height: 100dvh;
    display: flex;
    overflow: hidden;
    transition: background 0.3s, color 0.3s;
  }

  /* Ambient scene layer — a scattered field of gently-fading particles/
     sparkles and a small constellation node-graph, echoing the smart-
     barangay hero art. Fixed behind every panel via negative z-index so
     it never competes with real content. Movement is intentionally
     minimal — a slow opacity drift only, no scale/translate. */
  .bg-scene {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
    pointer-events: none;
  }
  /* Light mode needs MORE opacity than dark, not less: hairlines and 2px
     dots on a pale page disappear well before they would on the void. */
  .bg-scene .bg-constellation { opacity: 0.5; stroke-width: 1.4; }
  .bg-scene .bg-particles circle,
  .bg-scene .bg-sparkle {
    animation: bg-twinkle 9s ease-in-out infinite;
  }
  .bg-scene .bg-particles circle:nth-child(2n) { animation-delay: -3s; animation-duration: 11s; }
  .bg-scene .bg-particles circle:nth-child(3n) { animation-delay: -6s; animation-duration: 8s; }
  .bg-scene .bg-particles circle:nth-child(5n) { animation-delay: -1.5s; animation-duration: 13s; }
  .bg-scene .bg-sparkle:nth-of-type(2n) { animation-delay: -4.5s; animation-duration: 12s; }
  .bg-scene .bg-sparkle:nth-of-type(3n) { animation-delay: -7s; animation-duration: 10s; }
  [data-theme="dark"] .bg-scene .bg-constellation { opacity: 0.4; stroke-width: 1; }
  [data-theme="dark"] .bg-scene .bg-particles circle,
  [data-theme="dark"] .bg-scene .bg-sparkle { animation-name: bg-twinkle-dark; }
  /* A gentle fade plus a few px of drift — enough to feel alive without
     reading as motion that competes with real content. */
  @keyframes bg-twinkle {
    0%, 100% { opacity: 0.5; transform: translate(0, 0); }
    50% { opacity: 0.85; transform: translate(2px, -5px); }
  }
  @keyframes bg-twinkle-dark {
    0%, 100% { opacity: 0.4; transform: translate(0, 0); }
    50% { opacity: 0.75; transform: translate(2px, -5px); }
  }
  @media (prefers-reduced-motion: reduce) {
    .bg-scene .bg-particles circle, .bg-scene .bg-sparkle { animation: none; opacity: 0.7; }
    [data-theme="dark"] .bg-scene .bg-particles circle,
    [data-theme="dark"] .bg-scene .bg-sparkle { opacity: 0.6; }
  }

  /* SIDEBAR — floats as a rounded card next to the icon rail on desktop
     (mobile keeps the old full-bleed square-cornered drawer, see below) */
  .sidebar {
    width: var(--sidebar-w);
    background: var(--bg-sidebar);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    border: 1px solid var(--sb-border);
    border-left: none;
    border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
    margin: 10px 10px 10px 0;
    overflow: hidden;
    transition: transform 0.3s cubic-bezier(0.4,0,0.2,1), width 0.25s ease, margin 0.25s ease;
    z-index: 100;
  }
  /* Desktop collapse: the icon-rail is the persistent nav, so the sidebar
     panel collapses away entirely instead of becoming a second icon rail */
  .sidebar.collapsed {
    width: 0;
    min-width: 0;
    margin: 10px 0;
    border: none;
    overflow: hidden;
  }
  .sidebar.collapsed .brand-text,
  .sidebar.collapsed .sidebar-collapse-btn,
  .sidebar.collapsed .sidebar-section,
  .sidebar.collapsed .history-list,
  .sidebar.collapsed .nc-label,
  .sidebar.collapsed .sidebar-wifi-status,
  .sidebar.collapsed .sidebar-wifi-ssid { display: none; }
  .sidebar.collapsed .sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
  .sidebar.collapsed .new-chat-btn {
    justify-content: center;
    gap: 0;
    padding-left: 0;
    padding-right: 0;
  }
  .sidebar.collapsed .sidebar-footer { padding: 10px 0; }
  .sidebar.collapsed .sidebar-wifi { margin: 0 auto; padding: 7px; width: 40px; }
  .sidebar.collapsed .sidebar-wifi-top { justify-content: center; margin-bottom: 0; gap: 0; }

  .sidebar-brand {
    padding: 18px 16px 14px;
    display: flex;
    align-items: center;
    gap: 10px;
    border-bottom: 1px solid var(--sb-border);
  }

  /* Every DEVCON avatar/CTA that uses the brand gradient — kept in one
     place so light mode's 2-stop blue→accent and dark mode's 3-stop
     blue→violet→gold both apply everywhere consistently, not just here. */
  .avatar.ai, .modal-logo, .dc-brand-gradient {
    background: linear-gradient(135deg, var(--dc-blue) 0%, var(--dc-violet) 60%, var(--dc-gold) 130%);
  }
  [data-theme="dark"] .avatar.ai,
  [data-theme="dark"] .modal-logo,
  [data-theme="dark"] .dc-brand-gradient {
    background: linear-gradient(135deg, var(--dc-blue) 0%, var(--dc-violet) 65%, var(--dc-gold) 130%);
  }

  .brand-text { flex: 1; overflow: hidden; display: flex; align-items: center; }
  .brand-wordmark { height: 22px; width: auto; max-width: 100%; object-fit: contain; }

  .new-chat-btn {
    margin: 12px 12px 8px;
    padding: 9px 14px;
    background: color-mix(in srgb, var(--dc-blue) 25%, transparent);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 40%, transparent);
    border-radius: var(--radius-sm);
    color: var(--dc-blue-tint);
    font-size: 12.5px;
    font-weight: 600;
    font-family: var(--font-main);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 7px;
    transition: all 0.2s;
    letter-spacing: 0.2px;
  }
  .new-chat-btn:hover {
    background: color-mix(in srgb, var(--dc-blue) 40%, transparent);
    color: var(--dc-blue-tint-light);
  }
  .new-chat-btn svg { flex-shrink: 0; }

  .sidebar-section {
    padding: 8px 12px 4px;
    font-size: 9.5px;
    font-weight: 700;
    color: var(--sb-text-faint);
    letter-spacing: 1.2px;
    text-transform: uppercase;
  }

  .history-list {
    flex: 1;
    overflow-y: auto;
    padding: 4px 8px 8px;
  }
  .history-list::-webkit-scrollbar { width: 3px; }
  .history-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.18); border-radius: 2px; }

  .history-item {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--sb-text);
    font-size: 12.5px;
    font-weight: 400;
    transition: all 0.15s;
    margin-bottom: 2px;
    overflow: hidden;
    user-select: none;
  }
  .history-item:hover { background: var(--sb-hover-bg); color: var(--sb-text-strong); }
  .history-item.active { background: color-mix(in srgb, var(--dc-blue) 20%, transparent); color: var(--dc-blue-tint); }
  .history-item svg { flex-shrink: 0; opacity: 0.75; }
  .history-item-title {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 12.5px;
    line-height: 1.3;
  }
  .history-item-badge {
    flex-shrink: 0;
    font-size: 9.5px;
    font-weight: 700;
    background: rgba(255,255,255,0.14);
    color: var(--sb-text-dim);
    border-radius: 10px;
    padding: 1px 6px;
    min-width: 18px;
    text-align: center;
  }
  .history-item.active .history-item-badge {
    background: color-mix(in srgb, var(--dc-blue) 35%, transparent);
    color: color-mix(in srgb, var(--dc-blue-tint) 80%, transparent);
  }
  .history-item-delete {
    flex-shrink: 0;
    display: none;
    background: none;
    border: none;
    padding: 2px 4px;
    cursor: pointer;
    color: var(--sb-text-dim);
    border-radius: 4px;
    line-height: 1;
    transition: color 0.15s, background 0.15s;
  }
  .history-item:hover .history-item-delete { display: flex; align-items: center; }
  .history-item-delete:hover { color: #ff6b6b; background: rgba(255,107,107,0.12); }

  .sidebar-footer {
    padding: 10px 12px;
    border-top: 1px solid var(--sb-border);
  }

  .model-badge {
    background: color-mix(in srgb, var(--sb-accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--sb-accent) 25%, transparent);
    border-radius: 20px;
    padding: 5px 10px 5px 8px;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--sb-accent);
    font-weight: 500;
  }
  .model-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--sb-accent);
    animation: pulse 2s infinite;
  }
  @keyframes pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.5; transform: scale(0.8); }
  }

  /* MAIN */
  .main {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
    position: relative;
  }

  /* HEADER — only shown once a conversation is active; the welcome/main
     screen stays clean with no header bar (and thus no Back button either,
     since it lives inside it). */
  /* Separated by a rule, not by a fill. Any opaque or tinted bar is a flat
     colour laid over the body's radial-gradient glow, so it reads as a block
     cut into the background with a hard edge against the sidebar. A hairline
     marks the boundary just as clearly and lets the glow run straight
     through. Nothing scrolls behind the header either — .chat-area is its
     flex sibling, not underneath it — so it needs no fill to stay legible. */
  .header {
    /* min-height, not height: while the title is being renamed the h1 wraps
       onto extra lines and the bar grows downward with it. */
    min-height: var(--header-h);
    background: transparent;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    padding: 0 28px;
    gap: 18px;
    flex-shrink: 0;
  }
  .main.welcome-mode .header { display: none; }

  .back-btn {
    display: flex;
    align-items: center;
    gap: 3px;
    border: none;
    background: none;
    cursor: pointer;
    padding: 7px 11px 7px 7px;
    border-radius: 8px;
    color: var(--text-secondary);
    font-family: var(--font-main);
    font-size: 13px;
    font-weight: 600;
    transition: background 0.15s, color 0.15s;
    flex-shrink: 0;
  }
  .back-btn:hover { background: var(--border-light); color: var(--text-primary); }

  .header-title {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .header-title h1 {
    flex: 1 1 auto;
    min-width: 0;              /* lets the flex item shrink so text can wrap */
    font-size: 21px;
    font-weight: 800;
    letter-spacing: -0.3px;
    color: var(--text-primary);
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Click-to-rename: subtle hover hint, clear focus ring while editing */
  .header-title h1[contenteditable="true"] {
    cursor: text;
    border-radius: 6px;
    padding: 2px 7px;
    margin: -2px -7px;
    outline: none;
    transition: background 0.15s;
  }
  .header-title h1[contenteditable="true"]:hover { background: var(--border-light); }
  /* While editing, a long name wraps downward inside the box instead of
     running off the header as one line. overflow-wrap: anywhere is what
     breaks an unbroken run of characters (a pasted URL, a name typed with
     no spaces); white-space: normal alone would leave it as one long word.
     Past ~3 lines the box scrolls rather than swallowing the screen. */
  .header-title h1[contenteditable="true"]:focus {
    background: var(--bg-primary);
    box-shadow: 0 0 0 2px var(--dc-blue);
    white-space: normal;
    overflow-wrap: anywhere;
    text-overflow: clip;
    overflow-y: auto;
    max-height: calc(3.6em + 8px);
  }

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

  .lang-toggle-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: 50%;
    color: var(--text-muted);
    user-select: none;
    flex-shrink: 0;
    transition: border-color 0.15s;
  }
  .lang-toggle-btn:hover { border-color: var(--dc-blue); }
  @media (max-width: 640px) { .lang-toggle-btn { display: none; } }

  .icon-btn {
    width: 34px; height: 34px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    transition: all 0.15s;
  }
  .icon-btn:hover { background: var(--border-light); color: var(--text-primary); }

  .status-chip {
    display: flex;
    align-items: center;
    gap: 5px;
    background: #EDFAF3;
    border: 1px solid #B2E6C8;
    border-radius: 20px;
    padding: 4px 10px 4px 7px;
    font-size: 11px;
    font-weight: 600;
    color: #1A7A45;
    transition: all 0.4s;
  }
  .status-chip.disconnected {
    background: #FEF2F2;
    border-color: #FECACA;
    color: #991B1B;
  }
  [data-theme="dark"] .status-chip {
    background: rgba(26,122,69,0.15);
    border-color: rgba(26,122,69,0.3);
    color: #4AE08A;
  }
  [data-theme="dark"] .status-chip.disconnected {
    background: rgba(153,27,27,0.15);
    border-color: rgba(153,27,27,0.35);
    color: #FCA5A5;
  }
  .status-dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: #22C55E;
    transition: background 0.4s;
    animation: pulse 2s infinite;
  }
  .status-chip.disconnected .status-dot {
    background: #EF4444;
    animation: none;
  }

  /* "Checking" is a genuine third state, not a flavour of offline: right after
     you pick a model nothing is known about it yet. Amber + a spinner says
     "still probing", where the red dot said "this model is dead" — which was
     a lie for the second or two a cloud endpoint takes to answer. */
  .status-chip.checking {
    background: #FFF7ED;
    border-color: #FED7AA;
    color: #9A5B0B;
  }
  [data-theme="dark"] .status-chip.checking {
    background: rgba(217,119,6,0.15);
    border-color: rgba(217,119,6,0.35);
    color: #FBBF24;
  }
  .status-chip.checking .status-dot {
    width: 9px; height: 9px;
    background: none;
    border: 1.5px solid currentColor;
    border-top-color: transparent;
    animation: spin 0.7s linear infinite;
  }

  /* WIFI BANNER */
  .wifi-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 16px;
    font-size: 12px;
    font-weight: 500;
    background: #EDFAF3;
    border-bottom: 1px solid #B2E6C8;
    color: #1A7A45;
    transition: all 0.4s;
    flex-shrink: 0;
  }
  .wifi-banner.disconnected {
    background: #FFF7ED;
    border-color: #FED7AA;
    color: #92400E;
  }
  [data-theme="dark"] .wifi-banner {
    background: rgba(26,122,69,0.1);
    border-color: rgba(26,122,69,0.25);
    color: #4AE08A;
  }
  [data-theme="dark"] .wifi-banner.disconnected {
    background: rgba(146,64,14,0.12);
    border-color: rgba(146,64,14,0.3);
    color: #FBB97C;
  }
  .wifi-ssid {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.5px;
    background: rgba(0,0,0,0.07);
    padding: 1px 7px;
    border-radius: 5px;
  }
  [data-theme="dark"] .wifi-ssid { background: rgba(255,255,255,0.08); }
  .wifi-banner-right {
    margin-left: auto;
    font-size: 10.5px;
    opacity: 0.7;
    white-space: nowrap;
  }
  .wifi-bars {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 14px;
    flex-shrink: 0;
  }
  .wifi-bars span {
    display: block;
    width: 3px;
    border-radius: 1px;
    background: currentColor;
    transition: opacity 0.4s;
  }
  .wifi-bars span:nth-child(1) { height: 4px; }
  .wifi-bars span:nth-child(2) { height: 7px; }
  .wifi-bars span:nth-child(3) { height: 10px; }
  .wifi-bars span:nth-child(4) { height: 14px; }
  .wifi-banner.disconnected .wifi-bars span { opacity: 0.25; }
  .wifi-banner.disconnected .wifi-bars span:nth-child(1) { opacity: 0.6; }

  /* SIDEBAR WIFI CARD */
  .sidebar-wifi {
    margin: 8px 10px 4px;
    background: rgba(255,255,255,0.08);
    border: 1px solid var(--sb-border);
    border-radius: 10px;
    padding: 10px 12px;
    transition: all 0.4s;
  }
  .sidebar-wifi.connected { border-color: rgba(34,197,94,0.3); background: rgba(34,197,94,0.07); }
  .sidebar-wifi.disconnected { border-color: rgba(239,68,68,0.3); background: rgba(239,68,68,0.07); }
  .sidebar-wifi-top {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-bottom: 7px;
  }
  .sidebar-wifi-icon {
    display: flex; align-items: center; justify-content: center;
    width: 26px; height: 26px;
    border-radius: 7px;
    background: rgba(255,255,255,0.12);
    flex-shrink: 0;
  }
  /* Now that this line names whatever endpoint is live, it can be handed a
     bare host — "generativelanguage.googleapis.com · Gemini" — so it has to be
     able to cut. min-width:0 is what lets it: without it the flex item refuses
     to shrink below its text and pushes the card wider than the sidebar. */
  .sidebar-wifi-ssid {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 12px;
    font-weight: 700;
    font-family: var(--font-mono);
    color: rgba(255,255,255,0.85);
    letter-spacing: 0.3px;
  }
  .sidebar-wifi-status {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 600;
    margin-top: 2px;
  }
  .sidebar-wifi-status.ok { color: #4AE08A; }
  .sidebar-wifi-status.err { color: #FC8181; }
  .swifi-dot {
    width: 5px; height: 5px; border-radius: 50%;
    flex-shrink: 0;
  }
  .sidebar-wifi-status.ok .swifi-dot { background: #22C55E; animation: pulse 2s infinite; }
  .sidebar-wifi-status.err .swifi-dot { background: #EF4444; }
  .sidebar-wifi.checking { border-color: rgba(217,119,6,0.3); background: rgba(217,119,6,0.07); }
  .sidebar-wifi-status.checking { color: #FBBF24; }
  .sidebar-wifi-status.checking .swifi-dot {
    width: 8px; height: 8px;
    background: none;
    border: 1.5px solid currentColor;
    border-top-color: transparent;
    animation: spin 0.7s linear infinite;
  }

  /* CHAT AREA */
  .chat-area {
    flex: 1;
    overflow-y: auto;
    padding: 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .chat-area::-webkit-scrollbar { width: 4px; }
  .chat-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

  /* WELCOME */
  .welcome-screen {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 20px 20px 180px;
    gap: 20px;
  }

  .welcome-icon {
    width: 92px; height: 92px;
    object-fit: contain;
    flex-shrink: 0;
  }

  .welcome-hero { display: flex; flex-direction: column; align-items: center; gap: 6px; }

  .welcome-brief {
    font-size: 12px;
    color: var(--text-muted);
    max-width: 480px;
    line-height: 1.5;
    letter-spacing: 0.05px;
  }

  .welcome-greeting {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: 13px;
    color: var(--text-muted);
    font-weight: 500;
    letter-spacing: 0.1px;
  }
  .welcome-greeting svg { flex-shrink: 0; color: var(--dc-gold); }

  .welcome-title {
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
    letter-spacing: -0.5px;
  }
  .welcome-sub {
    font-size: 13px;
    color: var(--text-secondary);
    max-width: 340px;
    line-height: 1.65;
  }

  /* SUGGESTION CHIPS (replaces big cards on welcome screen) */
  .suggestion-chips {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    max-width: 600px;
  }
  .suggestion-chip {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 9px 16px;
    border: 1.5px solid var(--border);
    border-radius: 24px;
    background: var(--bg-panel);
    color: var(--text-secondary);
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
  }
  .suggestion-chip:hover {
    border-color: var(--dc-blue);
    color: var(--dc-blue);
    background: var(--dc-blue-light);
    transform: translateY(-1px);
    box-shadow: 0 3px 12px color-mix(in srgb, var(--dc-blue) 10%, transparent);
  }
  [data-theme="dark"] .suggestion-chip:hover {
    border-color: var(--dc-accent);
    color: var(--dc-accent);
    background: var(--dc-accent-light);
  }
  .suggestion-chip-icon { font-size: 14px; line-height: 1; }

  /* Keep old suggestion-grid for any legacy references */
  .suggestion-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 8px;
    width: 100%;
    max-width: 480px;
  }

  .suggestion-card {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 12px 13px;
    text-align: left;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: var(--shadow-card);
  }
  .suggestion-card:hover {
    border-color: var(--dc-blue);
    background: var(--dc-blue-light);
    transform: translateY(-1px);
  }
  .suggestion-card-icon {
    font-size: 18px;
    margin-bottom: 6px;
    display: block;
  }
  .suggestion-card-label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.3;
  }
  .suggestion-card-desc {
    font-size: 10.5px;
    color: var(--text-muted);
    margin-top: 2px;
    line-height: 1.3;
  }

  /* MESSAGES */
  .message-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  /* Avatars sit at the TOP of a row, level with the first line of the message
     and with the name label above them. Bottom-aligned, a long reply parked
     the AI's face down beside the *next* prompt, which read as if the person
     below had written the answer. Both bubbles already point their sharp
     corner at that top edge. */
  .message-row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
  }
  .message-row.user { flex-direction: row-reverse; }

  .avatar {
    width: 28px; height: 28px;
    border-radius: 50%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: -0.3px;
  }
  .avatar.ai {
    color: white;
  }
  /* The participant's own face: a person glyph, not the word "You". Their own
     messages are already the ones on the right in their own colour — a text
     label there was spending the only 28px it has on something the layout
     says twice over.
     Same brand palette as the AI's fallback face, deliberately: these are the
     two speakers in one conversation, and a grey chip opposite a brand-gradient
     one read as a placeholder waiting to be filled in. What separates them is
     geometry, not hue — a rounded square instead of a circle, and a radial
     highlight off the top-right corner instead of the AI's 135° diagonal
     sweep. Distinguishable at 28px and in a thumbnail; still one family. */
  .avatar.user {
    border-radius: 9px;
    background: radial-gradient(circle at 78% 14%,
      var(--dc-gold) 0%, var(--dc-violet) 32%, var(--dc-blue) 78%);
    color: #fff;
  }
  .avatar.user svg { display: block; }
  /* An uploaded picture sits inside the avatar, so the brand gradient stays
     behind anything transparent instead of being replaced by it. */
  .avatar.ai .avatar-img {
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: 50%;
    display: block;
  }

  /* The AI's name, above its avatar. Taken out of flow on purpose: the avatar
     column has to stay exactly 28px because .bubble's
     `max-width: calc(100% - 44px)` is measured against it, so a long name must
     not be able to widen it — that would shrink every bubble in the
     conversation. Absolute positioning also keeps it out of the row's flex
     layout entirely, so no wrapper element is needed around the avatar.
     padding-top opens the band the label lives in, above both the avatar and
     the bubble. */
  .message-row.has-ident {
    position: relative;
    padding-top: 18px;   /* label is ~14px tall; the rest is its gap to the bubble */
  }
  .ai-ident-name {
    position: absolute;
    top: 0;
    left: 0;
    /* Fits the 40-char name cap on desktop, and still inside the row on a
       320px screen. Past that CSS cuts it — see the .slice note in
       aiIdentMarkup() for why this isn't done in JS. */
    max-width: 240px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 10.5px;
    font-weight: 650;
    line-height: 1.3;
    color: var(--text-muted);
  }

  .bubble {
    max-width: calc(100% - 44px);
    padding: 10px 13px;
    border-radius: 16px;
    font-size: 13.5px;
    line-height: 1.65;
    word-break: break-word;
  }

  .bubble.ai {
    background: var(--ai-bubble);
    border: 1px solid var(--border-light);
    border-radius: 4px 16px 16px 16px;
    color: var(--text-primary);
    box-shadow: var(--shadow-card);
  }

  .bubble.user {
    background: var(--user-bubble);
    color: white;
    border-radius: 16px 4px 16px 16px;
    font-weight: 500;
  }

  .bubble pre {
    background: rgba(0,0,0,0.06);
    border-radius: 6px;
    padding: 8px 10px;
    margin: 8px 0;
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 11.5px;
    line-height: 1.5;
  }
  [data-theme="dark"] .bubble.ai pre { background: rgba(255,255,255,0.05); }
  .bubble.user pre { background: rgba(255,255,255,0.15); }

  .bubble code {
    font-family: var(--font-mono);
    font-size: 11.5px;
    background: color-mix(in srgb, var(--dc-blue) 8%, transparent);
    padding: 1px 5px;
    border-radius: 4px;
    color: var(--dc-blue);
  }
  .bubble.user code { background: rgba(255,255,255,0.2); color: white; }

  .bubble strong { font-weight: 600; }
  .bubble em { font-style: italic; }
  .bubble s { text-decoration: line-through; opacity: 0.7; }
  .bubble p { margin: 2px 0; line-height: 1.65; }
  .bubble ul, .bubble ol { padding-left: 18px; margin: 5px 0; }
  .bubble li { margin: 2px 0; line-height: 1.6; }
  .bubble h2 { font-size: 14.5px; font-weight: 700; margin: 12px 0 4px; color: var(--dc-blue); border-bottom: 1px solid var(--border-light); padding-bottom: 3px; }
  .bubble h3 { font-size: 13.5px; font-weight: 700; margin: 10px 0 3px; color: var(--dc-blue); }
  .bubble h4 { font-size: 13px; font-weight: 700; margin: 8px 0 3px; color: var(--text-secondary); }
  [data-theme="dark"] .bubble h2, [data-theme="dark"] .bubble h3 { color: var(--dc-accent); }
  .bubble hr { border: none; border-top: 1px solid var(--border-light); margin: 10px 0; }
  .bubble blockquote {
    border-left: 3px solid var(--dc-blue);
    margin: 6px 0;
    padding: 4px 12px;
    color: var(--text-secondary);
    background: var(--dc-blue-light);
    border-radius: 0 6px 6px 0;
    font-size: 13px;
  }
  [data-theme="dark"] .bubble blockquote { background: color-mix(in srgb, var(--dc-blue) 10%, transparent); }

  /* CODE BLOCK */
  .code-block { margin: 8px 0; border-radius: 8px; overflow: hidden; border: 1px solid var(--border-light); }
  .code-block-header {
    display: flex; align-items: center; justify-content: space-between;
    background: var(--border-light);
    border-bottom: 1px solid var(--border-light);
    padding: 2px 6px 2px 10px;
  }
  [data-theme="dark"] .code-block-header { background: rgba(255,255,255,0.06); }
  .code-lang-label {
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: 10px; font-weight: 700;
    letter-spacing: 0.5px;
    text-transform: uppercase;
  }
  .code-copy-btn {
    display: inline-flex; align-items: center; gap: 4px;
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-muted);
    font-family: var(--font-sans, inherit);
    font-size: 11px; font-weight: 600;
    padding: 3px 7px;
    border-radius: 5px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
  }
  .code-copy-btn:hover { background: color-mix(in srgb, var(--dc-blue) 8%, transparent); color: var(--dc-blue); border-color: color-mix(in srgb, var(--dc-blue) 20%, transparent); }
  [data-theme="dark"] .code-copy-btn:hover { background: rgba(255,255,255,0.08); color: var(--dc-accent); border-color: rgba(255,255,255,0.15); }
  .code-copy-btn.copied { color: #1a8754; background: rgba(26,135,84,0.1); border-color: rgba(26,135,84,0.25); }
  .code-copy-btn svg { flex-shrink: 0; }
  .code-block pre { margin: 0; border-radius: 0; border: none; white-space: pre; }

  /* TABLE */
  .table-wrap { overflow-x: auto; margin: 8px 0; border-radius: 8px; border: 1px solid var(--border-light); }
  .bubble table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
  .bubble th {
    background: var(--dc-blue-light);
    color: var(--dc-blue);
    font-weight: 700;
    text-align: left;
    padding: 7px 11px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    font-size: 12px;
  }
  .bubble td {
    padding: 6px 11px;
    border-bottom: 1px solid var(--border-light);
    vertical-align: top;
    line-height: 1.5;
  }
  .bubble tr:last-child td { border-bottom: none; }
  .bubble tbody tr:nth-child(even) td { background: rgba(0,0,0,0.02); }
  [data-theme="dark"] .bubble th { background: color-mix(in srgb, var(--dc-blue) 18%, transparent); color: var(--dc-accent); }
  [data-theme="dark"] .bubble tbody tr:nth-child(even) td { background: rgba(255,255,255,0.03); }

  /* Flex rather than a plain text line: the timestamp shares this row with the
     message's Copy / Edit / Ask again buttons, which keeps them on the 720px
     column and the 42px indent without a wrapper of their own. min-height holds
     the row at its hovered size so revealing the buttons can't shift the
     conversation above them. */
  .message-time {
    font-size: 10px;
    color: var(--text-muted);
    padding: 0 42px;
    margin-top: 2px;
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 20px;
  }
  .message-time.user { justify-content: flex-end; }

  /* MESSAGE ACTIONS — quiet until the message is hovered. The buttons are
     always in the layout, only faded, so nothing moves when they appear. */
  .msg-actions {
    display: inline-flex;
    align-items: center;
    gap: 1px;
  }
  @media (hover: hover) {
    /* The fade is on the buttons, not on the row that holds them. Opacity on a
       parent clips its children — a child cannot be more opaque than its
       parent — so fading the container would take the version switcher with it,
       and the switcher has to stay visible (see .msg-versions below). The `>`
       matters: it reaches Copy / Edit / Ask again but not the switcher's own
       arrows, which are nested one level deeper. */
    .msg-actions > .msg-action {
      opacity: 0;
      transition: opacity 0.14s ease;
    }
    /* The bubble and this row are siblings under #chat-area, so hovering the
       message reaches its actions through the adjacent-sibling combinator. */
    .message-row:hover + .message-time .msg-actions > .msg-action,
    .message-time:hover .msg-actions > .msg-action,
    .msg-actions:focus-within > .msg-action { opacity: 1; }
  }
  .msg-action {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.12s, background 0.12s;
  }
  .msg-action:hover {
    color: var(--text-primary);
    background: var(--bg-secondary);
  }
  [data-theme="dark"] .msg-action:hover { background: rgba(255,255,255,0.07); }
  .msg-action.copied { color: var(--dc-green); }
  .msg-action svg { display: block; }
  /* Editing or rewinding the thread while a reply is streaming would land that
     reply on a conversation that no longer matches what it was asked. */
  body.is-generating .msg-action {
    pointer-events: none;
    opacity: 0.3;
  }

  /* PROMPT EDITOR — takes the place of the user's bubble in the same row, so
     the edited text sits exactly where the message it replaces was. */
  .prompt-edit {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px;
    background: var(--bg-panel);
    border: 1.5px solid var(--dc-accent);
    border-radius: 20px 4px 20px 20px;
  }
  .prompt-edit-input {
    width: 100%;
    border: none;
    outline: none;
    resize: none;
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.55;
    overflow-y: auto;
  }
  .prompt-edit-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
  }
  .prompt-edit-hint {
    margin-right: auto;
    font-size: 10px;
    color: var(--text-muted);
  }
  .prompt-edit-btn {
    font-family: inherit;
    font-size: 12px;
    padding: 5px 12px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s, background 0.12s;
  }
  .prompt-edit-btn:hover { color: var(--text-primary); border-color: var(--text-muted); }
  .prompt-edit-btn.primary {
    background: var(--dc-accent);
    border-color: var(--dc-accent);
    color: #fff;
  }
  .prompt-edit-btn.primary:hover { filter: brightness(1.08); color: #fff; }

  /* VERSION SWITCHER — `‹ 2/3 ›`, on the prompt for wordings and on the answer
     for re-rolls. Unlike its neighbours this is never hidden: a count is state,
     not an offered action, and a version nobody can see they have is a version
     they lose. It carries the muted colour of the timestamp it sits beside. */
  .msg-versions {
    display: inline-flex;
    align-items: center;
    gap: 1px;
  }
  .msg-version-count {
    font-variant-numeric: tabular-nums;   /* 1/2 → 2/2 must not shift width */
    font-size: 10px;
    color: var(--text-muted);
    padding: 0 1px;
  }
  .msg-action.msg-version-step { width: 16px; height: 20px; border-radius: 4px; }
  .msg-action:disabled {
    opacity: 0.3;
    pointer-events: none;
  }

  /* THINKING INDICATOR — expandable agent trace.
     The header is a quiet button; everything below it hangs off a single
     hairline rail, so the trace reads as one thread of work rather than a
     stack of boxes. Collapse uses the 1fr→0fr grid trick so the height
     animates without measuring anything in JS. */
  @keyframes spin { to { transform: rotate(360deg); } }
  @keyframes shimmer-text { to { background-position: -200% 0; } }
  @keyframes fade-up {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .thinking-bubble {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 9px 13px 11px;
    min-width: 268px;
  }
  .thinking-top-row {
    display: flex;
    align-items: center;
    gap: 8px;
    width: calc(100% + 12px);
    margin: -3px -6px 0;
    padding: 4px 6px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    user-select: none;
    transition: background 0.1s ease;
  }
  .thinking-top-row:hover { background: var(--border-light); }
  .thinking-sparkle {
    flex-shrink: 0;
    color: var(--text-secondary);
    display: flex;
  }
  .thinking-top-chevron {
    display: flex;
    flex-shrink: 0;
    color: var(--text-muted);
    transform: rotate(180deg);   /* the trace starts expanded */
    transition: transform 0.3s ease;
  }
  .thinking-top-row.collapsed .thinking-top-chevron { transform: rotate(0deg); }
  /* Shimmer sweeps across the label instead of the old trailing "...", so the
     header stays one stable width while the model works. */
  .thinking-label {
    font-size: 13px;
    font-weight: 500;
    font-style: normal;
    white-space: nowrap;
    background-image: linear-gradient(90deg,
      var(--text-muted) 35%, var(--text-primary) 50%, var(--text-muted) 65%);
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer-text 1.4s linear infinite;
  }
  /* Work is over — the sweep stops and the label states the total. */
  .thinking-label.settled {
    background-image: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--text-secondary);
    animation: fade-in 350ms ease-out both;
  }
  .thinking-model-tag {
    font-size: 10.5px;
    font-family: var(--font-mono);
    color: var(--text-muted);
    margin-left: auto;
    padding-left: 10px;
    font-style: normal;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 45%;
  }

  .thinking-collapsible {
    display: grid;
    grid-template-rows: 1fr;
    opacity: 1;
    width: 100%;
    transition: grid-template-rows 0.4s cubic-bezier(0.23, 1, 0.32, 1),
                opacity 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  }
  .thinking-collapsible.hidden { grid-template-rows: 0fr; opacity: 0; }
  .thinking-clip { overflow: hidden; }
  /* The rail is positioned rather than measured — top/bottom insets let it
     grow with the trace for free as steps stream in. */
  .thinking-trace {
    position: relative;
    margin: 4px 0 0 5px;
    padding-left: 16px;
  }
  .thinking-trace::before {
    content: '';
    position: absolute;
    left: 0;
    top: -4px;
    bottom: 8px;
    width: 1px;
    background: var(--border);
  }

  .thinking-steps-section { margin-top: 8px; }
  .thinking-steps-section.hidden { display: none; }
  .thinking-steps-label {
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    padding-left: 6px;
    margin-bottom: 3px;
  }
  .thinking-steps-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
  }
  /* A row is icon → what happened → what it cost. The primary text truncates
     so the measurement on the right is never the thing that gets cut off. */
  .thinking-step {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 26px;
    padding: 2px 6px;
    border-radius: 6px;
    font-size: 12.5px;
    line-height: 1.35;
    color: var(--text-primary);
    font-weight: 500;
    text-decoration: none;
    animation: fade-up 320ms cubic-bezier(0.23, 1, 0.32, 1) both;
  }
  .thinking-step.step-done { color: var(--text-secondary); }
  .thinking-step.step-error { color: var(--danger); }

  /* Retrieved chunks of a single file: indented under the row naming that file,
     with a quiet tick instead of a source dot. They are parts of one thing, and
     giving each the same marker a whole source gets is what made three chunks of
     one PDF read as three documents. */
  .thinking-step.kind-chunk {
    min-height: 22px;
    padding-left: 22px;
    font-size: 12px;
    font-weight: 400;
    color: var(--text-muted);
  }
  .chunk-tick {
    display: block;
    width: 7px;
    height: 2px;
    border-radius: 1px;
    background: var(--border);
  }
  .thinking-step.kind-chunk .step-meta { color: var(--text-muted); }
  .step-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .step-meta {
    flex-shrink: 0;
    font-size: 11.5px;
    font-weight: 400;
    color: var(--text-muted);
    white-space: nowrap;
  }
  .step-icon {
    width: 14px;
    height: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--text-muted);
  }
  .thinking-step.step-error .step-icon { color: var(--danger); }
  .step-mini-spinner {
    width: 12px; height: 12px;
    border-radius: 50%;
    border: 1.5px solid var(--border);
    border-top-color: var(--text-secondary);
    animation: spin 700ms linear infinite;
    display: inline-block;
  }

  /* SEARCH ROWS — the query that was sent, then the pages it came back with.
     Sources are real links: the trace is only worth showing if the student can
     go read what the model was fed. */
  .thinking-step.kind-query { color: var(--text-secondary); font-weight: 400; }
  .thinking-step.kind-source {
    color: var(--text-primary);
    transition: background 0.15s ease;
  }
  .thinking-step.kind-source:hover { background: var(--border-light); }
  .thinking-step.kind-source:hover .step-text { text-decoration: underline; }
  /* Three near-identical rows of blue link text are unscannable — a coloured
     dot per source gives the eye something to sort on. */
  .src-dot {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    flex-shrink: 0;
  }
  .thinking-step.kind-more {
    display: block;
    padding-left: 28px;
    font-size: 12px;
    font-weight: 400;
    color: var(--text-muted);
    min-height: 0;
  }

  /* EDUCATIONAL CARD inside thinking bubble — kept, but demoted to an inset
     panel on the rail so it supports the trace instead of competing with it. */
  .thinking-edu-card {
    background: color-mix(in srgb, var(--dc-blue) 5%, transparent);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    width: 100%;
    box-sizing: border-box;
    overflow: hidden;
    transition: opacity 0.25s ease;
  }
  [data-theme="dark"] .thinking-edu-card { background: rgba(255,255,255,0.035); }
  .thinking-edu-body {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    padding: 9px 11px 8px;
  }
  .thinking-edu-icon {
    display: flex;
    flex-shrink: 0;
    margin-top: 1px;
    color: var(--text-muted);
  }
  .thinking-edu-text {
    font-size: 12px;
    color: var(--text-secondary);
    line-height: 1.6;
    font-style: normal;
  }
  .thinking-edu-footer {
    border-top: 1px solid var(--border-light);
    padding: 6px 7px 7px;
  }
  .thinking-edu-footer-label {
    display: block;
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin: 0 0 3px 4px;
  }
  .thinking-edu-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
  }
  .thinking-edu-links a {
    font-size: 11.5px;
    color: var(--text-secondary);
    text-decoration: none;
    border-radius: 6px;
    padding: 3px 6px;
    font-weight: 500;
    transition: background 0.12s ease, color 0.12s ease;
  }
  .thinking-edu-links a:hover { background: var(--border-light); color: var(--text-primary); }

  /* SETTLED TRACE — the same trace, once the answer exists. It moves inside the
     bubble and folds shut, so the record of the work sits with the work rather
     than vanishing the moment it becomes checkable. */
  .process-trace {
    width: 100%;
    margin-bottom: 8px;
  }
  .process-trace .thinking-top-row {
    width: calc(100% + 12px);
    margin: -2px -6px 0;
  }
  .process-trace .thinking-trace { margin-top: 2px; }
  .process-trace:not(.live) .thinking-steps-section.hidden { display: block; }

  /* THINK BLOCK — the model's own reasoning. Same language as the trace above
     it: sparkle, shimmer while it runs, settled label, prose on the rail. */
  .think-block { margin-bottom: 10px; }
  .think-block-header {
    display: flex;
    align-items: center;
    gap: 8px;
    width: calc(100% + 12px);
    margin: 0 -6px;
    padding: 4px 6px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    user-select: none;
    transition: background 0.1s ease;
  }
  .think-block-header:hover { background: var(--border-light); }
  .think-header-label {
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
    background-image: linear-gradient(90deg,
      var(--text-muted) 35%, var(--text-primary) 50%, var(--text-muted) 65%);
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer-text 1.4s linear infinite;
  }
  .think-header-label.settled {
    background-image: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--text-secondary);
    animation: fade-in 350ms ease-out both;
  }
  .think-block-chevron {
    display: flex;
    flex-shrink: 0;
    margin-left: auto;
    color: var(--text-muted);
    transition: transform 0.3s ease;
  }
  .think-block-header.open .think-block-chevron { transform: rotate(180deg); }
  /* The rail is a border, not a pseudo-element: this box scrolls, and an
     absolutely-positioned rail would scroll away with the text. */
  .think-block-body {
    margin: 4px 0 0 5px;
    padding: 2px 0 2px 16px;
    border-left: 1px solid var(--border);
    font-size: 11.5px;
    color: var(--text-secondary);
    white-space: pre-wrap;
    max-height: 240px;
    overflow-y: auto;
    line-height: 1.7;
  }
  .think-block-body.hidden { display: none; }

  /* MESSAGE METADATA */
  .msg-meta {
    font-size: 10px;
    color: var(--text-muted);
    padding: 3px 42px 0;
    letter-spacing: 0.1px;
    display: flex;
    gap: 0;
    align-items: center;
  }
  .msg-meta-dot {
    margin: 0 5px;
    opacity: 0.45;
  }

  /* CLICKABLE MESSAGE STATS */
  .msg-meta-wrap {
    position: relative;
    margin-left: 42px;
  }
  .msg-meta-clickable {
    cursor: pointer;
    padding: 3px 8px;
    border-radius: 6px;
    transition: background 0.15s, color 0.15s;
    width: fit-content;
  }
  .msg-meta-clickable:hover { color: var(--text-secondary); background: var(--border-light); }
  .msg-meta-chevron {
    margin-left: 5px;
    opacity: 0.5;
    transition: transform 0.2s;
    vertical-align: -1px;
  }
  .msg-meta-clickable.open .msg-meta-chevron { transform: rotate(180deg); }

  .msg-stats-popover {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 50;
    min-width: 230px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 13px 15px 11px;
    box-shadow: 0 8px 32px color-mix(in srgb, var(--dc-blue) 18%, transparent), 0 2px 8px rgba(0,0,0,0.06);
    animation: statsPop 0.16s cubic-bezier(0.4,0,0.2,1);
  }
  [data-theme="dark"] .msg-stats-popover {
    box-shadow: 0 8px 32px rgba(0,0,0,0.45);
  }
  .msg-stats-popover.hidden { display: none; }
  /* Flip above the trigger when there isn't room below (e.g. last message) */
  .msg-stats-popover.up {
    top: auto;
    bottom: calc(100% + 6px);
    animation-name: statsPopUp;
  }
  @keyframes statsPop {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @keyframes statsPopUp {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .msg-stats-title {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: 0.2px;
    margin-bottom: 10px;
  }
  .msg-stats-rows { display: flex; flex-direction: column; gap: 6px; }
  .msg-stats-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    font-size: 11.5px;
  }
  .msg-stats-k {
    color: var(--text-muted);
    font-weight: 500;
    flex-shrink: 0;
  }
  .msg-stats-v {
    color: var(--text-secondary);
    text-align: right;
  }
  .msg-stats-v b { color: var(--text-primary); font-weight: 600; }
  .msg-stats-v.mono { font-family: var(--font-mono); font-size: 11px; }
  .msg-stats-divider {
    height: 1px;
    background: var(--border-light);
    margin: 10px 0;
  }
  .msg-stats-note {
    font-size: 10px;
    color: var(--text-muted);
    margin-top: 9px;
    opacity: 0.8;
  }

  /* SOURCE CHIPS */
  .source-row {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border-light);
  }
  .source-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--dc-blue-light);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 3px 9px;
    font-size: 10.5px;
    color: var(--dc-blue);
    font-weight: 500;
    cursor: default;
  }
  [data-theme="dark"] .source-chip { color: var(--dc-accent); }

  /* INPUT — floats as a centered card in both welcome AND active-chat
     state (previously only welcome mode floated; the docked chat-mode bar
     now gets the same lift/rounding/shadow instead of sitting flush). */
  .input-area {
    padding: 10px 14px 14px;
    background: var(--bg-panel);
    border: 1.5px solid var(--border);
    flex-shrink: 0;
    position: absolute;
    left: 50%;
    width: calc(100% - 40px);
    max-width: 760px;
    transform: translateX(-50%);
    margin: 0 0 20px;
    bottom: 0;
    z-index: 10;
    border-radius: var(--radius-lg);
    box-shadow: 0 8px 40px color-mix(in srgb, var(--dc-blue) 15%, transparent), 0 2px 8px color-mix(in srgb, var(--dc-blue) 8%, transparent);
    transition: border-color 0.3s ease, box-shadow 0.4s ease;
  }
  [data-theme="dark"] .input-area {
    box-shadow: 0 8px 40px color-mix(in srgb, var(--dc-accent) 10%, transparent), 0 2px 8px rgba(0,0,0,0.2);
  }
  /* Hide footer details when floating — keep it compact like Gemini */
  .main.welcome-mode .legal-disclaimer {
    display: none;
  }

  /* Chat mode: leave room for the floating input bar (+ its bottom margin),
     and dissolve messages as they scroll in behind it.

     The fade is a mask on the scroll box, not a coloured scrim laid on top of
     it. A scrim has to commit to one flat colour, and any flat colour reads as
     a cut-out rectangle over the body's radial-gradient glow — the previous
     one painted a 150px band of --bg-primary across the bottom of .main, which
     showed up as a hard seam running down the sidebar's edge and a visibly
     mismatched block under the input card. Masking fades the content itself
     and leaves whatever is behind it completely untouched. */
  .main:not(.welcome-mode) .chat-area {
    padding-bottom: 200px;
    /* -start/-end are measured up from the bottom of the scroll box: content
       is fully opaque above -start and fully gone below -end. -top is the
       short matching fade at the header end, so messages dissolve as they
       reach the header's rule rather than sliding under it at full strength. */
    --chat-fade-top: 22px;
    --chat-fade-start: 150px;
    --chat-fade-end: 45px;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--chat-fade-top), #000 calc(100% - var(--chat-fade-start)), transparent calc(100% - var(--chat-fade-end)));
    mask-image: linear-gradient(to bottom, transparent 0, #000 var(--chat-fade-top), #000 calc(100% - var(--chat-fade-start)), transparent calc(100% - var(--chat-fade-end)));
  }

  .input-box {
    background: var(--bg-primary);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: border-color 0.2s, box-shadow 0.2s;
    box-shadow: var(--shadow-input);
  }
  .input-box:focus-within {
    border-color: var(--dc-blue);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--dc-blue) 10%, transparent), var(--shadow-input);
  }

  /* Off-screen twin of the textarea, used only to measure the draft's
     natural width (see syncComposer in app/chat.js). Absolute + invisible
     so it never affects layout or gets read out. */
  .composer-measure {
    position: absolute;
    visibility: hidden;
    pointer-events: none;
    white-space: pre;
    font-family: var(--font-main);
    font-size: 13.5px;
    line-height: 1.5;
  }

  /* Two layouts, one flex row.
     Collapsed — everything shares the line:  [ + ][ text …… ][ model ][ ↑ ]
     Expanded  — the draft takes the line:    [ text ……………………………………………… ]
                                              [ + ]        [ model ][ ↑ ]
     Sending the textarea to 100% is what wraps the controls onto their own
     line; nothing is placed by hand, so visitor mode (which drops the "+"
     and the model picker from the DOM) reflows on its own.

     Collapsed stays nowrap on purpose. A wrapping flex container breaks its
     lines from each item's *unshrunk* basis, so a bare <textarea> (which is
     ~20 characters wide by default) plus a long model name would push the
     controls onto a second line by itself — the composer must only ever
     restack when syncComposer() decides it should. */
  .composer-controls {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: 6px;
  }
  /* basis 0, not auto: the textarea takes the room that's left over rather
     than asking for its intrinsic width first. */
  .composer-controls > #message-input { flex: 1 1 0; min-width: 0; }
  .composer-controls > .model-select-btn { min-width: 0; }

  .composer-controls.expanded { flex-wrap: wrap; }
  .composer-controls.expanded > #message-input {
    order: -1;
    flex: 1 0 100%;
  }
  /* Collapsed, the stretched textarea already pushes these right; once it
     moves up a line, they need the push themselves. */
  .composer-controls.expanded > .model-select-btn {
    margin-left: auto;
    flex-shrink: 1;   /* last resort on a very narrow controls row */
  }

  #message-input {
    /* No width here on purpose: flex-basis:auto would inherit it and pin the
       textarea to a full line, so it could never share one with the controls.
       .composer-controls.expanded sets the 100% basis when it wants that. */
    min-width: 0;
    border: none;
    background: none;
    font-family: var(--font-main);
    font-size: 13.5px;
    color: var(--text-primary);
    resize: none;
    outline: none;
    min-height: 36px;
    max-height: 140px;
    overflow-y: hidden;
    overflow-x: hidden;
    overflow-wrap: anywhere;
    line-height: 1.5;
    padding: 8px 4px;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
  }
  #message-input::-webkit-scrollbar { width: 4px; }
  #message-input::-webkit-scrollbar-track { background: transparent; }
  #message-input::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
  #message-input::-webkit-scrollbar-button { display: none; }
  /* Placeholder hints stay single-line (like Gemini's "Ask Gemini") — real typed
     text still wraps/scrolls normally when it grows past one row. */
  #message-input::placeholder {
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .send-btn {
    width: 36px; height: 36px;
    background: linear-gradient(135deg, var(--dc-blue) 0%, var(--dc-violet) 100%);
    border: none;
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    transition: background 0.2s, color 0.2s, transform 0.15s, opacity 0.2s;
    flex-shrink: 0;
  }
  @media (hover: hover) {
    .send-btn:hover { background: linear-gradient(135deg, var(--dc-blue-dark) 0%, var(--dc-blue) 100%); transform: scale(1.04); }
    [data-theme="dark"] .send-btn:hover { background: linear-gradient(135deg, var(--dc-blue-dark) 0%, var(--dc-violet) 65%, var(--dc-gold) 130%); }
  }
  .send-btn:active { transform: scale(0.94); }
  [data-theme="dark"] .send-btn { background: linear-gradient(135deg, var(--dc-blue) 0%, var(--dc-violet) 65%, var(--dc-gold) 130%); }
  .send-btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
  /* Nothing to send yet — the button stays clickable (sendMessage() is the
     one guard on an empty draft) but drops out of the brand gradient so the
     composer reads as "waiting for you" rather than "ready to fire". */
  .send-btn.idle {
    background: var(--border);
    color: var(--text-muted);
  }
  @media (hover: hover) {
    .send-btn.idle:hover,
    [data-theme="dark"] .send-btn.idle:hover { background: var(--border); transform: none; }
  }
  /* Stop / cancel state while the model is generating — outranks .idle.
     A solid red block here shouts louder than anything else on screen for
     something the user does routinely; the tinted chip still reads as stop
     (same slot, same square glyph) without competing with the reply. */
  .send-btn.stop,
  .send-btn.stop.idle {
    background: var(--danger-tint);
    color: var(--danger);
    box-shadow: inset 0 0 0 1px var(--danger-edge);
  }
  @media (hover: hover) {
    .send-btn.stop:hover,
    [data-theme="dark"] .send-btn.stop:hover {
      background: color-mix(in srgb, var(--danger) 20%, transparent);
      transform: none;
    }
  }
  .cancelled-note {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px dashed var(--border);
    font-size: 12px;
    font-style: italic;
    color: var(--text-muted);
  }
  .cancelled-note svg { flex-shrink: 0; opacity: 0.8; }

  .input-tip {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 5px;
    text-align: center;
    font-size: 10px;
    color: var(--text-secondary);
    margin-top: 6px;
    letter-spacing: 0.1px;
  }
  .input-tip-dot { opacity: 0.5; }
  .input-tip-badges {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 5px;
  }
  .tip-badge {
    display: inline-flex;
    align-items: center;
    border-radius: 3px;
    padding: 1px 6px;
    font-size: 9px;
    font-weight: 800;
    color: white;
    letter-spacing: 0.3px;
    white-space: nowrap;
    line-height: 1.6;
  }

  /* MODEL SELECT BUTTON (inside the composer) — a borderless label+chevron
     that sits flush with the other composer controls instead of reading as
     a separate outlined pill. */
  .model-select-btn {
    display: flex;
    align-items: center;
    gap: 4px;
    height: 36px;
    max-width: 190px;
    padding: 0 8px;
    background: transparent;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    font-family: var(--font-main);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    transition: background 0.15s, color 0.15s, transform 0.15s, gap 0.18s ease, padding 0.18s ease;
    white-space: nowrap;
    flex-shrink: 0;
    letter-spacing: 0.1px;
  }
  #model-select-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  @media (hover: hover) {
    .model-select-btn:hover {
      background: var(--dc-blue-light);
      color: var(--dc-blue);
    }
    [data-theme="dark"] .model-select-btn:hover {
      background: var(--dc-accent-light);
      color: var(--dc-accent);
    }
  }
  .model-select-btn:active { transform: scale(0.96); }
  .model-select-btn.open {
    background: var(--dc-blue-light);
    color: var(--dc-blue);
  }
  [data-theme="dark"] .model-select-btn.open {
    background: var(--dc-accent-light);
    color: var(--dc-accent);
  }
  .model-select-btn svg { flex-shrink: 0; }
  .model-select-btn .model-select-caret { opacity: 0.6; transition: transform 0.2s, opacity 0.2s; }
  .model-select-btn:hover .model-select-caret { opacity: 1; }
  .model-select-btn.open .model-select-caret { transform: rotate(180deg); opacity: 1; }
  #model-select-icon { display: inline-flex; align-items: center; flex-shrink: 0; }
  #model-select-icon img.model-dd-icon { opacity: 1; }

  /* Collapsed pill (in-conversation only) — once there are messages on screen
     the picker is sharing its row with the draft, so it drops back to a bare
     model name and hands the width to the textarea. Hovering it, or opening
     the dropdown, restores the full logo + name + chevron.

     Welcome mode keeps the full pill: nothing is competing for the row yet,
     and that's the one moment the picker should be unmissable. Scoped to
     (hover: hover) + desktop widths so touch never gets a control that only
     un-collapses on an event it can't produce — mobile keeps its own layout,
     where the controls already have a row to themselves. */
  @media (hover: hover) and (min-width: 641px) {
    /* max-width, not display — a collapsing width is what can animate, and
       the flex items need overflow clipped because the SVGs inside them are
       fixed-size and won't shrink with their box. */
    #model-select-icon,
    .model-select-btn .model-select-caret {
      max-width: 20px;
      overflow: hidden;
      transition: max-width 0.18s ease, opacity 0.18s ease, transform 0.2s;
    }
    .main:not(.welcome-mode) .model-select-btn:not(:hover):not(:focus-visible):not(.open) {
      gap: 0;
      padding: 0 4px;
    }
    .main:not(.welcome-mode) .model-select-btn:not(:hover):not(:focus-visible):not(.open) > #model-select-icon,
    .main:not(.welcome-mode) .model-select-btn:not(:hover):not(:focus-visible):not(.open) > .model-select-caret {
      max-width: 0;
      opacity: 0;
    }
  }

  /* TOOLS MENU (Gemini "+" style) — collapses sources/thinking/web-search into one button */
  .tools-wrap { position: relative; flex-shrink: 0; }

  .tools-btn {
    width: 36px; height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    color: var(--text-muted);
    transition: background 0.15s, color 0.15s, transform 0.15s;
    position: relative;
  }
  .tools-btn:active { transform: scale(0.94); }
  /* Hover-only on devices with a real pointer — on touchscreens (no true
     hover), :hover applies on tap and then sticks until something else is
     tapped, leaving the "+" looking permanently lit. The .open highlight
     below still applies everywhere, since that's driven by a class, not
     a pseudo-state. */
  @media (hover: hover) {
    .tools-btn:hover { background: var(--dc-blue-light); color: var(--dc-blue); }
    [data-theme="dark"] .tools-btn:hover { background: var(--dc-accent-light); color: var(--dc-accent); }
  }
  .tools-btn.open {
    background: var(--dc-blue-light);
    color: var(--dc-blue);
  }
  [data-theme="dark"] .tools-btn.open {
    background: var(--dc-accent-light);
    color: var(--dc-accent);
  }
  .tools-dropdown {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 0;
    background: var(--bg-panel);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 30px rgba(0,0,0,0.18);
    padding: 6px;
    width: 200px;
    display: none;
    flex-direction: column;
    gap: 2px;
    z-index: 20;
    animation: dropdownIn 0.15s ease;
  }
  .tools-dropdown.open { display: flex; }

  .tools-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 9px;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-primary);
    text-align: left;
    width: 100%;
    transition: background 0.12s;
  }
  .tools-item:hover { background: var(--bg-primary); }
  .tools-item-ico { flex-shrink: 0; display: flex; opacity: 0.75; }
  .tools-item-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .tools-item-check { flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--dc-blue); opacity: 0; }
  .tools-item.active { background: var(--dc-blue-light); color: var(--dc-blue); }
  .tools-item.active .tools-item-ico { opacity: 1; }
  .tools-item.active .tools-item-check { opacity: 1; }
  [data-theme="dark"] .tools-item.active { background: var(--dc-accent-light); color: var(--dc-accent); }
  [data-theme="dark"] .tools-item.active .tools-item-check { color: var(--dc-accent); }

  /* Model picker — this was the mobile stand-in for the model-select pill
     back when that pill was hidden on narrow screens. The stacked composer
     gives the real pill a row of its own at every width now, so the entry
     stays hidden rather than listing the model in two places at once. */
  .tools-item-model { display: none; }
  .tools-section-label {
    font-size: 9.5px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.9px;
    padding: 2px 9px;
  }
  .tools-section-divider {
    height: 1px;
    margin: 4px 2px 6px;
    background: var(--border);
  }
  /* ── STREAMED TEXT ─────────────────────────────────────────────────
     Words carry no entry animation: text just appears as it arrives, with a
     caret marking where it is up to. Anything that fades a word in has to be
     paced, and the pacing is what read as slow and flickery.

     Layout-neutral wrappers: the split into committed vs. still-arriving text
     is a rendering concern, so it must not add a box that stops paragraph
     margins collapsing across the seam. */
  .stream-stable, .stream-tail { display: contents; }
  @keyframes pop-in {
    from { opacity: 0; transform: scale(0.9); }
    to   { opacity: 1; transform: scale(1); }
  }
  /* Deliberately not blinking: the caret is re-created on every repaint, so a
     blink would restart each tick and freeze at one phase. Its movement across
     the text is the liveness signal. */
  .stream-caret {
    display: inline-block;
    width: 2px;
    height: 0.85em;
    margin-left: 2px;
    vertical-align: -1px;
    border-radius: 2px;
    background: var(--text-primary);
    opacity: 0.75;
  }

  /* ── INLINE CITATION CHIP ──────────────────────────────────────────
     Replaces the model's bare "[1]" markers with the source it points at,
     so a claim carries its provenance in place. */
  .cite-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    height: 17px;
    margin: 0 2px 0 1px;
    padding: 0 4px 0 3px;
    border-radius: 5px;
    background: var(--border-light);
    box-shadow: inset 0 0 0 1px var(--border);
    vertical-align: middle;
    transform: translateY(-1px);
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--text-secondary);
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.15s ease, color 0.15s ease;
    animation: pop-in 250ms cubic-bezier(0.23, 1, 0.32, 1) both;
  }
  .cite-chip:hover { background: var(--dc-blue-light); color: var(--dc-blue); }
  .cite-chip img { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }

  /* ── SOURCES ROW + EXPANDABLE LIST ─────────────────────────────────
     Collapsed to one line of stacked avatars; the full list opens in place. */
  .web-sources { margin-top: 10px; }
  .src-toggle {
    display: flex;
    align-items: center;
    gap: 7px;
    margin-left: -4px;
    padding: 3px 6px;
    border: none;
    background: none;
    border-radius: 6px;
    cursor: pointer;
    font-family: var(--font-main);
    transition: background 0.15s ease;
  }
  .src-toggle:hover { background: var(--border-light); }
  .src-stack { display: flex; flex-shrink: 0; }
  .src-stack img {
    width: 15px; height: 15px;
    border-radius: 50%;
    background: var(--bg-panel);
    box-shadow: 0 0 0 1.5px var(--ai-bubble);
  }
  .src-stack img + img { margin-left: -4px; }
  .src-toggle-label { font-size: 12px; color: var(--text-secondary); }
  .src-toggle-chevron {
    display: flex;
    color: var(--text-muted);
    transition: transform 0.25s ease;
  }
  .src-toggle.open .src-toggle-chevron { transform: rotate(180deg); }

  .src-list {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows 0.3s cubic-bezier(0.23, 1, 0.32, 1),
                opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1);
  }
  .src-list.open { grid-template-rows: 1fr; opacity: 1; }
  .src-list-clip { overflow: hidden; }
  .src-list-inner {
    margin-top: 6px;
    padding: 4px;
    border-radius: 10px;
    background: var(--border-light);
    box-shadow: inset 0 0 0 1px var(--border);
    display: flex;
    flex-direction: column;
  }
  [data-theme="dark"] .src-list-inner { background: rgba(255,255,255,0.04); }
  .web-source-link {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    border-radius: 6px;
    text-decoration: none;
    color: var(--text-secondary);
    font-size: 12px;
    transition: background 0.12s, color 0.12s;
  }
  .web-source-link:hover { background: var(--bg-panel); color: var(--text-primary); }
  [data-theme="dark"] .web-source-link:hover { background: rgba(255,255,255,0.06); }
  .web-source-num {
    flex-shrink: 0;
    width: 16px; height: 16px;
    border-radius: 4px;
    overflow: hidden;
  }
  .web-source-num img { width: 100%; height: 100%; display: block; }
  .web-source-title {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .web-source-host {
    flex-shrink: 0;
    color: var(--text-muted);
    font-family: var(--font-mono);
    font-size: 10.5px;
  }

  /* ── KNOWLEDGE SOURCES (local retrieval) ───────────────────────────
     Same collapse mechanics as the web strip, but each row carries the
     retrieval score and the chunk text — a web citation can only say where
     an answer came from, while this can also say how strongly it matched. */
  .kb-source-item {
    padding: 6px;
    border-radius: 6px;
  }
  .kb-source-item + .kb-source-item { border-top: 1px solid var(--border); }
  .kb-source-head {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-secondary);
  }
  .kb-source-marker {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    color: var(--dc-accent);
    background: color-mix(in srgb, var(--dc-accent) 14%, transparent);
    border-radius: 4px;
    padding: 1px 4px;
  }
  .kb-score {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
  }
  .kb-score-bar {
    width: 34px;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    overflow: hidden;
  }
  .kb-score-bar > span {
    display: block;
    height: 100%;
    border-radius: 2px;
    background: var(--dc-accent);
  }
  .kb-score-num {
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--text-muted);
    min-width: 24px;
  }
  .kb-source-text {
    margin-top: 5px;
    margin-left: 24px;
    max-height: 74px;
    overflow-y: auto;
    font-size: 11.5px;
    line-height: 1.55;
    color: var(--text-muted);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }
  /* The web chip shows a hostname; this one shows a filename, which runs longer
     and would otherwise push the paragraph it sits in around. */
  .cite-chip.kb-chip { max-width: 150px; }
  .cite-chip.kb-chip span:not(.kb-chip-n) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .kb-chip-n {
    flex-shrink: 0;
    font-weight: 600;
    color: var(--dc-accent);
  }

  /* ── PROMPT INSPECTOR ──────────────────────────────────────────────
     The literal bytes sent to the model. Monospace and unstyled on purpose —
     this panel's whole value is that it isn't a rendering of the prompt. */
  .prompt-inspector { margin-top: 8px; }
  .prompt-toggle-icon {
    display: flex;
    flex-shrink: 0;
    color: var(--text-muted);
  }
  .prompt-toggle-meta {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--text-muted);
  }
  .prompt-inner {
    margin-top: 6px;
    border-radius: 10px;
    background: var(--border-light);
    box-shadow: inset 0 0 0 1px var(--border);
    overflow: hidden;
  }
  [data-theme="dark"] .prompt-inner { background: rgba(255,255,255,0.04); }
  /* ── Composition view ──────────────────────────────────────────────
     The part of the panel a beginner reads: a sentence, a bar, and one row
     per contributor naming the setting that produced it. */
  .prompt-breakdown { padding: 11px 12px 12px; }
  .prompt-lede {
    margin: 0 0 10px;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--text-secondary);
  }
  .prompt-lede b { color: var(--text-primary); font-weight: 700; }
  .prompt-bar {
    display: flex;
    height: 9px;
    border-radius: 5px;
    overflow: hidden;
    background: var(--border);
  }
  .prompt-bar > span { display: block; height: 100%; }
  .prompt-bar > span + span { box-shadow: inset 1px 0 0 var(--bg-panel); }
  .prompt-parts { margin-top: 10px; }
  /* Two lines per part: name and size on the first, provenance on the second.
     The wrap has to be explicit — .prompt-part-src claims a full basis to force
     itself onto its own row, which without flex-wrap would instead squeeze the
     label into a two-word column. */
  .prompt-part {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 8px;
    padding: 5px 0;
    font-size: 12px;
    line-height: 1.45;
  }
  .prompt-part + .prompt-part { border-top: 1px solid var(--border); }
  .prompt-part-dot {
    flex-shrink: 0;
    width: 8px; height: 8px;
    border-radius: 2px;
    transform: translateY(-1px);
  }
  .prompt-part-label {
    flex: 1;
    min-width: 0;
    color: var(--text-primary);
    font-weight: 600;
  }
  .prompt-part-size {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 10.5px;
    color: var(--text-secondary);
    white-space: nowrap;
  }
  .prompt-part-pct { margin-left: 6px; color: var(--text-muted); }
  /* The provenance line wraps under the label on its own row. */
  .prompt-part-src {
    flex-basis: 100%;
    margin-left: 16px;
    font-size: 11px;
    color: var(--text-muted);
  }
  .prompt-note {
    margin: 11px 0 0;
    padding-top: 9px;
    border-top: 1px solid var(--border);
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-muted);
  }

  /* ── Raw text, behind its own disclosure ───────────────────────────*/
  .prompt-raw-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 8px 12px;
    font-family: inherit;
    font-size: 11.5px;
    color: var(--text-secondary);
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
  }
  .prompt-raw-toggle:hover { color: var(--text-primary); }
  .prompt-raw-toggle .src-toggle-chevron {
    display: flex;
    transform: rotate(-90deg);
    transition: transform 0.2s;
  }
  .prompt-raw-toggle.open .src-toggle-chevron { transform: rotate(0deg); }
  .prompt-raw-body.hidden { display: none; }
  .prompt-raw-body { border-top: 1px solid var(--border); }

  .prompt-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 8px 12px;
    border-top: 1px solid var(--border);
  }
  .prompt-head-cfg { flex: 1; display: flex; gap: 4px; flex-wrap: wrap; }
  .prompt-head-cfg code {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--text-muted);
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 5px;
  }
  .prompt-copy-btn {
    flex-shrink: 0;
    font-family: inherit;
    font-size: 11px;
    color: var(--text-secondary);
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 3px 9px;
    cursor: pointer;
    transition: color 0.12s, border-color 0.12s;
  }
  .prompt-copy-btn:hover { color: var(--text-primary); }
  .prompt-copy-btn.copied { color: var(--dc-green); border-color: var(--dc-green); }
  .prompt-msg + .prompt-msg { border-top: 1px solid var(--border); }
  .prompt-msg-role {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 10px;
    font-family: var(--font-mono);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
  }
  .prompt-msg.role-system .prompt-msg-role { color: var(--dc-accent); }
  .prompt-msg-len {
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-muted);
  }
  .prompt-msg-body {
    margin: 0;
    padding: 0 10px 10px;
    max-height: 260px;
    overflow: auto;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-secondary);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }

  /* ── FOLLOW-UPS ────────────────────────────────────────────────────
     Generated after the answer lands; each row sends itself on click. */
  .followups {
    margin-top: 12px;
    animation: fade-in 300ms ease-out both;
  }
  .followups-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 1px;
  }
  .followup-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: calc(100% + 12px);
    margin: 0 -6px;
    padding: 7px 6px;
    border: none;
    border-bottom: 1px solid var(--border-light);
    background: none;
    border-radius: 7px;
    cursor: pointer;
    text-align: left;
    font-family: var(--font-main);
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text-primary);
    transition: background 0.1s ease;
    animation: fade-up 350ms cubic-bezier(0.23, 1, 0.32, 1) both;
  }
  .followup-item:hover { background: var(--border-light); }
  .followup-item svg { flex-shrink: 0; color: var(--text-muted); }

  /* MODEL DROPDOWN PANEL (search-style picker) */
  .model-dropdown {
    position: absolute;
    bottom: calc(100% + 8px);
    right: 14px;
    background: var(--bg-panel);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 30px rgba(0,0,0,0.18);
    padding: 8px;
    width: 280px;
    display: none;
    flex-direction: column;
    gap: 6px;
    z-index: 20;
    animation: dropdownIn 0.15s ease;
  }
  .model-dropdown.open { display: flex; }
  @keyframes dropdownIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .model-dd-search-row { display: flex; align-items: center; gap: 6px; }
  .model-dd-search {
    flex: 1;
    min-width: 0;
    padding: 7px 10px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--bg-primary);
    color: var(--text-primary);
    font-family: var(--font-main);
    font-size: 12.5px;
    outline: none;
    transition: border-color 0.15s;
  }
  .model-dd-search:focus { border-color: var(--dc-accent); }
  .model-dd-search::placeholder { color: var(--text-muted); }
  .model-dd-add {
    flex-shrink: 0;
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    border: 1.5px solid var(--dc-accent);
    border-radius: 8px;
    background: transparent;
    color: var(--dc-accent);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    transition: all 0.15s;
  }
  .model-dd-add:hover { background: var(--dc-accent); color: #fff; }
  .model-dd-recent-label {
    font-size: 9.5px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.9px;
    padding: 2px 4px 0;
  }
  .model-dd-list { display: flex; flex-direction: column; gap: 2px; max-height: 220px; overflow-y: auto; }
  .model-dd-empty { padding: 10px 6px; font-size: 12px; color: var(--text-muted); text-align: center; }
  .model-dd-demo-note { padding: 4px 8px 6px; font-size: 10.5px; color: var(--text-muted); text-align: left; }
  .model-dropdown-opt.demo { border: 1px dashed var(--border); opacity: 0.85; }
  .model-dropdown-opt.demo:hover { background: var(--bg-primary); opacity: 1; }
  .model-dropdown-opt.demo .model-dd-endpoint { font-style: italic; }
  .model-dropdown-opt {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 10px;
    border: none;
    background: none;
    border-radius: 8px;
    cursor: pointer;
    font-family: var(--font-main);
    font-size: 12.5px;
    color: var(--text-primary);
    font-weight: 500;
    transition: background 0.12s;
    text-align: left;
    width: 100%;
  }
  .model-dropdown-opt:hover { background: var(--bg-primary); }
  .model-dropdown-opt.active { background: var(--dc-blue-light); }
  [data-theme="dark"] .model-dropdown-opt.active { background: var(--dc-accent-light); }
  .model-dd-icon { flex-shrink: 0; opacity: 0.8; }
  img.model-dd-icon { object-fit: contain; border-radius: 4px; opacity: 1; }
  .model-dd-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  .model-dd-name { font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .model-dropdown-opt.active .model-dd-name { color: var(--dc-accent); }
  .model-dd-endpoint { font-size: 10.5px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .model-dd-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    margin-left: auto;
    background: #F59E0B;   /* default: checking / idle */
    transition: background 0.2s;
  }
  .model-dd-dot.online  { background: #22C55E; }
  .model-dd-dot.idle    { background: #F59E0B; }
  .model-dd-dot.offline { background: #EF4444; }
  .model-dd-badge {
    font-size: 9.5px;
    font-weight: 800;
    letter-spacing: 0.3px;
    padding: 2px 7px;
    border-radius: 20px;
    white-space: nowrap;
    flex-shrink: 0;
  }
  .model-dd-badge.rec   { background: rgba(34,197,94,0.14);  color: #16A34A; }
  .model-dd-badge.fast  { background: rgba(245,158,11,0.16); color: #B45309; }
  .model-dd-badge.smart { background: rgba(139,92,246,0.14); color: #7C3AED; }
  [data-theme="dark"] .model-dd-badge.rec   { color: #4ADE80; }
  [data-theme="dark"] .model-dd-badge.fast  { color: #FBBF24; }
  [data-theme="dark"] .model-dd-badge.smart { color: #A78BFA; }
  .model-dd-dot.checking {
    background: #F59E0B;
    animation: dot-pulse 1s ease-in-out infinite;
  }
  @keyframes dot-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

  /* ADD MODELS — ENDPOINT CARDS */
  .endpoint-card {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    background: var(--bg-primary);
  }
  .endpoint-source {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 11px;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    background: var(--bg-panel);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.6px;
    color: var(--text-secondary);
  }
  .endpoint-source svg { opacity: 0.7; }
  .endpoint-row { display: flex; align-items: center; gap: 8px; }
  .endpoint-row .settings-input { flex: 1; min-width: 0; }
  .endpoint-type, .endpoint-provider {
    flex-shrink: 0;
    background: var(--bg-panel);
    border: 1.5px solid var(--border);
    border-radius: 8px;
    padding: 8px 9px;
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-primary);
    cursor: pointer;
    outline: none;
  }
  .endpoint-type { width: 76px; }
  .endpoint-provider { display: flex; align-items: center; }
  .endpoint-type:focus, .endpoint-provider:focus { border-color: var(--dc-blue); }
  .endpoint-actions { display: flex; justify-content: flex-end; gap: 8px; }
  .endpoint-btn {
    padding: 8px 18px;
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
  }
  .endpoint-btn.ghost {
    background: transparent;
    border: 1.5px solid var(--border);
    color: var(--text-secondary);
  }
  .endpoint-btn.ghost:hover:not(:disabled) { border-color: var(--dc-blue); color: var(--dc-blue); }
  .endpoint-btn.solid {
    background: var(--dc-blue);
    border: 1.5px solid var(--dc-blue);
    color: #fff;
  }
  .endpoint-btn.solid:hover:not(:disabled) { filter: brightness(1.08); }
  .endpoint-btn:disabled { opacity: 0.6; cursor: default; }
  .endpoint-quickstart {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-panel);
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background 0.15s;
  }
  .endpoint-quickstart:hover { background: var(--bg-primary); }
  .endpoint-quickstart svg { transition: transform 0.2s; opacity: 0.6; }
  .endpoint-quickstart.open svg { transform: rotate(90deg); }
  .quickstart-body {
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 4px 4px 2px;
    font-size: 12px;
    color: var(--text-secondary);
    line-height: 1.5;
  }
  /* The rule above sets display:flex, which beats the UA stylesheet's
     display:none for [hidden] — without this the panel could never
     collapse and the Quickstart toggle looked dead. */
  .quickstart-body[hidden] { display: none; }
  .quickstart-body code {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 11.5px;
    color: var(--dc-blue);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
  }
  .quickstart-body strong { color: var(--text-primary); }
  .quickstart-body a { color: var(--dc-blue); font-weight: 600; text-decoration: none; }
  .quickstart-body a:hover { text-decoration: underline; }
  .qs-step { display: flex; gap: 9px; align-items: flex-start; }
  .qs-num {
    flex-shrink: 0;
    width: 19px;
    height: 19px;
    margin-top: 1px;
    border-radius: 50%;
    background: var(--dc-blue);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .qs-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
  .qs-text strong { font-size: 12.5px; }
  .qs-text code { align-self: flex-start; max-width: 100%; overflow-x: auto; }
  .qs-tip {
    margin-top: 2px;
    padding: 8px 10px;
    border-radius: 7px;
    background: var(--bg-panel);
    font-size: 11.5px;
    line-height: 1.55;
  }
  .qs-tip code {
    background: none;
    border: none;
    padding: 0;
    font-size: 11px;
  }

  /* ADDED MODELS / ENDPOINT MANAGER */
  .added-endpoints-section { margin-top: 4px; }
  .added-endpoints-head {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-primary);
  }
  .added-endpoints-head svg { opacity: 0.65; }
  .added-endpoints-sub { font-weight: 500; color: var(--text-muted); }
  .added-endpoints-desc {
    margin: 4px 0 12px;
    font-size: 11.5px;
    color: var(--text-muted);
  }
  .ep-group { margin-bottom: 14px; }
  .ep-group-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.6px;
    color: var(--text-muted);
    margin-bottom: 6px;
  }
  .ep-group-label svg { opacity: 0.6; }
  .ep-card {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px 13px;
    background: var(--bg-primary);
  }
  .ep-card-main { display: flex; align-items: center; flex-wrap: wrap; gap: 9px; }
  .ep-card-name {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
  }
  .ep-badge {
    font-size: 10.5px;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 20px;
    background: rgba(5,150,105,0.12);
    color: #059669;
    white-space: nowrap;
  }
  .ep-badge.off { background: var(--bg-panel); color: var(--text-muted); }
  .ep-manage-hint {
    flex: 1;
    min-width: 0;
    font-size: 11px;
    color: var(--text-muted);
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ep-card-actions { display: flex; align-items: center; gap: 7px; flex-shrink: 0; margin-left: auto; }
  .ep-mini-btn {
    padding: 5px 12px;
    border-radius: 7px;
    font-family: var(--font-main);
    font-size: 11.5px;
    font-weight: 600;
    cursor: pointer;
    background: transparent;
    border: 1.5px solid var(--border);
    color: var(--text-secondary);
    transition: all 0.15s;
    white-space: nowrap;
  }
  .ep-mini-btn:hover { border-color: var(--dc-blue); color: var(--dc-blue); }
  .ep-mini-btn.danger:hover { border-color: #DC2626; color: #DC2626; }
  .ep-chevron {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: none;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 6px;
  }
  .ep-chevron:hover { background: var(--bg-panel); }
  .ep-chevron svg { transition: transform 0.2s; }
  .ep-chevron.open svg { transform: rotate(180deg); }
  .ep-url-row {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    font-size: 11px;
    color: var(--text-muted);
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
  }
  .ep-url-copy {
    border: none;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px;
    display: inline-flex;
    border-radius: 4px;
  }
  .ep-url-copy:hover { color: var(--dc-blue); }
  .ep-models {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .ep-model-row {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 4px;
    font-size: 12.5px;
    color: var(--text-primary);
  }
  .ep-model-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ep-model-name.disabled { color: var(--text-muted); text-decoration: line-through; }
  .ep-toggle {
    flex-shrink: 0;
    width: 34px;
    height: 18px;
    border-radius: 20px;
    border: none;
    background: var(--border);
    position: relative;
    cursor: pointer;
    transition: background 0.15s;
  }
  .ep-toggle.on { background: var(--dc-blue); }
  .ep-toggle::after {
    content: '';
    position: absolute;
    top: 2px; left: 2px;
    width: 14px; height: 14px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.15s;
  }
  .ep-toggle.on::after { transform: translateX(16px); }
  .added-endpoints-empty {
    padding: 16px;
    border: 1px dashed var(--border);
    border-radius: 10px;
    text-align: center;
    font-size: 12px;
    color: var(--text-muted);
  }

  /* SIDEBAR OVERLAY (mobile) */
  .sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 99;
  }

  /* MOBILE */
  @media (max-width: 640px) {
    :root { --sidebar-w: 260px; }

    .sidebar {
      position: fixed;
      left: 0; top: 0; bottom: 0;
      margin: 0;
      border-radius: 0;
      border: none;
      transform: translateX(-100%);
    }
    .sidebar.open { transform: translateX(0); }

    .sidebar-overlay.visible { display: block; }

    .suggestion-grid { grid-template-columns: 1fr; max-width: 100%; }
    .welcome-title { font-size: 18px; }
    .chat-area { padding: 14px 12px; }
    /* Reclaim the side gutters — 40px of them is a lot of a phone's width. */
    .input-area { padding: 8px 10px 12px; display: flex; flex-direction: column; width: calc(100% - 20px); }
    /* The stacked composer is roughly twice as tall as the one-row desktop
       version, so the last message needs more room to clear it — and the
       scroll-in fade has to start correspondingly higher. */
    .main:not(.welcome-mode) .chat-area {
      padding-bottom: 250px;
      --chat-fade-start: 215px;
      --chat-fade-end: 60px;
    }

    /* Partner badges are the least important line here — push them below
       the legal disclaimer instead of sandwiching them right under the box. */
    .input-box { order: 1; }
    .legal-disclaimer { order: 2; }
    .input-tip { order: 3; }

    .status-chip { display: none; }

    .bubble { max-width: calc(100% - 38px); }

    .header { padding: 0 12px; gap: 6px; }
    .back-btn { font-size: 0; gap: 0; padding: 7px; }
    .header-actions { gap: 4px; }
    .icon-btn { width: 30px; height: 30px; }

    /* COMPOSER (mobile) — there's never enough room to keep the draft inline
       with the controls, so the stacked layout is permanent here: text on
       top, controls on a row beneath it. That also buys the model picker its
       place back; it used to be hidden on narrow screens and reachable only
       through the "+" menu. */
    .composer-controls { flex-wrap: wrap; }
    .composer-controls > #message-input {
      order: -1;
      flex: 1 0 100%;
    }
    .composer-controls > .model-select-btn { margin-left: auto; flex-shrink: 1; }

    /* iOS zooms the whole page in when a focused field is under 16px. */
    #message-input {
      font-size: 16px;
      padding: 8px 6px;
      max-height: 120px;
    }
    .composer-measure { font-size: 16px; }

    /* The model name is the first thing that should give up width when the
       row gets tight — the "+" and send buttons are fixed-size targets. */
    .model-select-btn { max-width: 46vw; }

    /* Clear the home-indicator / gesture bar on modern phones. */
    .input-area { margin-bottom: max(20px, env(safe-area-inset-bottom)); }

    /* Partner badges: force the label onto its own line so the 3 badges
       always wrap together as one centered group below it, instead of
       splitting lopsidedly (e.g. 2 + 1) mid-sentence with the label text. */
    .input-tip { flex-direction: column; }
    .input-tip-badges { flex-wrap: nowrap; }
  }

  /* CONTEXT NOTIFICATION */
  .context-bar {
    margin: 0 0 4px;
    padding: 8px 12px;
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 20%, transparent);
    border-radius: var(--radius-sm);
    font-size: 11.5px;
    color: var(--dc-blue);
    display: flex;
    align-items: center;
    gap: 7px;
    font-weight: 500;
  }
  [data-theme="dark"] .context-bar { color: var(--dc-accent); }

  .scroll-bottom-btn {
    position: fixed;
    bottom: 100px;
    right: 20px;
    width: 36px; height: 36px;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 50%;
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 3px 12px rgba(0,0,0,0.12);
    transition: all 0.2s;
    z-index: 50;
    color: var(--text-secondary);
  }
  .scroll-bottom-btn.visible { display: flex; }
  .scroll-bottom-btn:hover { background: var(--dc-blue); color: white; border-color: var(--dc-blue); }

  /* ERROR */
  /* ERROR CARD — an ordinary card that happens to carry a red glyph, rather
     than a red card. The recovery steps are the *fix*, not the failure, so
     they read in the normal ink scale and the primary action keeps the brand
     blue: "Open the setup guide" is constructive, and dressing it as a
     destructive button misreads what it does. */
  .error-bubble {
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    font-size: 13px;
    color: var(--text-secondary);
    margin: 4px 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    box-shadow: var(--shadow-card);
  }
  .error-bubble > svg { color: var(--danger); }
  .error-bubble-top {
    display: flex;
    align-items: flex-start;
    gap: 10px;
  }
  .error-bubble-icon {
    width: 32px; height: 32px;
    background: var(--danger-tint);
    border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    color: var(--danger);
    box-shadow: inset 0 0 0 1px var(--danger-edge);
  }
  .error-bubble-title {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 3px;
    line-height: 1.3;
  }
  .error-bubble-desc {
    font-size: 12.5px;
    color: var(--text-secondary);
    line-height: 1.6;
  }
  .error-bubble-steps {
    background: var(--border-light);
    box-shadow: inset 0 0 0 1px var(--border);
    border-radius: 10px;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  [data-theme="dark"] .error-bubble-steps { background: rgba(255,255,255,0.035); }
  .error-bubble-steps-title {
    font-size: 10.5px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin-bottom: 2px;
  }
  .error-step {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: 12px;
    color: var(--text-secondary);
    line-height: 1.5;
  }
  .error-step-num {
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--bg-panel);
    box-shadow: inset 0 0 0 1px var(--border);
    color: var(--text-secondary);
    display: flex; align-items: center; justify-content: center;
    font-size: 9px; font-weight: 700;
    flex-shrink: 0; margin-top: 1px;
  }
  .error-step code {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--text-primary);
    background: var(--bg-primary);
    box-shadow: inset 0 0 0 1px var(--border);
    padding: 1px 5px;
    border-radius: 4px;
  }
  [data-theme="dark"] .error-step code { background: rgba(0,0,0,0.28); }
  .error-bubble-dismiss {
    align-self: flex-end;
    background: none;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
    font-family: var(--font-main);
    transition: background 0.15s, color 0.15s;
  }
  .error-bubble-dismiss:hover { background: var(--border-light); color: var(--text-primary); }
  .error-bubble-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
  }
  .error-bubble-cta {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--dc-blue);
    border: 1px solid var(--dc-blue);
    border-radius: 8px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 700;
    color: #fff;
    cursor: pointer;
    font-family: var(--font-main);
    transition: background 0.15s, transform 0.05s;
  }
  .error-bubble-cta:hover { background: var(--dc-blue-dark); border-color: var(--dc-blue-dark); }
  .error-bubble-cta:active { transform: translateY(1px); }
  .error-bubble-cta svg { flex-shrink: 0; }
  /* TRUST PILLS */
  .trust-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 18%, transparent);
    border-radius: 20px;
    padding: 4px 11px 4px 8px;
    font-size: 11px;
    font-weight: 600;
    color: var(--dc-blue);
    letter-spacing: 0.1px;
    white-space: nowrap;
  }
  [data-theme="dark"] .trust-pill {
    background: color-mix(in srgb, var(--dc-blue) 15%, transparent);
    border-color: color-mix(in srgb, var(--dc-blue) 30%, transparent);
    color: var(--dc-accent);
  }

  .community-desc {
    width: 100%;
    max-width: 400px;
  }
  .community-desc-inner {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    background: var(--bg-panel);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md);
    padding: 11px 13px;
    font-size: 12px;
    color: var(--text-secondary);
    line-height: 1.65;
    box-shadow: var(--shadow-card);
    text-align: left;
  }
  .community-desc-inner svg { color: var(--dc-blue); margin-top: 1px; }
  [data-theme="dark"] .community-desc-inner svg { color: var(--dc-accent); }
  .community-desc-inner strong { color: var(--text-primary); font-weight: 600; }

  /* LEGAL DISCLAIMER — lives inside the .input-area card. It used to be a
     docked footer bar of its own, which is where the fill and the rule came
     from; against the card they just drew a stray divider between the partner
     badges and this line, over a background that already matches. */
  .legal-disclaimer {
    padding: 5px 16px 6px;
    flex-shrink: 0;
  }
  .legal-disclaimer-inner {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 9.5px;
    color: var(--text-secondary);
    line-height: 1.4;
    justify-content: center;
    flex-wrap: wrap;
    text-align: center;
  }
  .legal-about-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 9.5px;
    color: var(--dc-blue);
    font-family: var(--font-main);
    font-weight: 600;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  [data-theme="dark"] .legal-about-btn { color: var(--dc-accent); }

  /* WELCOME MODAL */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(14, 9, 32, 0.72);
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    backdrop-filter: blur(3px);
    animation: fade-in 0.2s ease;
  }
  @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

  .modal-card {
    background: var(--bg-panel);
    border-radius: 20px;
    width: 100%;
    max-width: 440px;
    max-height: 88dvh;
    overflow-y: auto;
    box-shadow: 0 24px 64px rgba(0,0,0,0.3);
    animation: slide-up 0.25s cubic-bezier(0.34,1.56,0.64,1);
    border: 1px solid var(--border);
  }
  @keyframes slide-up { from { transform: translateY(24px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
  .modal-card::-webkit-scrollbar { width: 3px; }
  .modal-card::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

  /* Onboarding copy reads too dim on the dark theme's muted/secondary
     grays — every var(--text-secondary)/var(--text-muted) use in here
     (classes and inline styles alike) resolves to near-white instead. */
  [data-theme="dark"] .onboarding-card {
    --text-secondary: var(--text-primary);
    --text-muted: var(--text-primary);
  }

  .modal-header {
    padding: 22px 22px 0;
    display: flex;
    align-items: flex-start;
    gap: 14px;
  }
  .modal-logo {
    width: 46px; height: 46px;
    border-radius: 13px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    font-weight: 800;
    color: white;
    letter-spacing: -0.5px;
    flex-shrink: 0;
    box-shadow: 0 6px 20px color-mix(in srgb, var(--dc-blue) 30%, transparent);
  }
  .modal-header-text { flex: 1; }
  .modal-header-text h2 {
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
    margin-bottom: 3px;
  }
  .modal-header-text p {
    font-size: 11.5px;
    color: var(--text-muted);
    font-weight: 400;
  }
  .modal-mabuhay {
    font-size: 12px;
    font-weight: 700;
    color: var(--dc-blue);
    letter-spacing: 0.3px;
    margin-bottom: 4px;
  }
  [data-theme="dark"] .modal-mabuhay { color: var(--text-primary); }

  .lang-toggle-soon {
    display: flex;
    align-items: center;
    gap: 4px;
    background: var(--bg-primary);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 4px 8px;
    font-size: 10.5px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: not-allowed;
    white-space: nowrap;
    user-select: none;
  }
  .soon-badge {
    background: #FEF9C3;
    border: 1px solid #FDE68A;
    color: #854D0E;
    border-radius: 4px;
    padding: 0 5px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.3px;
    text-transform: uppercase;
  }
  [data-theme="dark"] .soon-badge {
    background: rgba(133,77,14,0.2);
    border-color: rgba(133,77,14,0.4);
    color: #FCD34D;
  }
  .modal-close {
    width: 28px; height: 28px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    flex-shrink: 0;
    transition: all 0.15s;
    margin-top: -2px;
  }
  .modal-close:hover { background: var(--border-light); color: var(--text-primary); }

  .modal-tabs {
    display: flex;
    gap: 0;
    padding: 16px 22px 0;
    border-bottom: 1px solid var(--border-light);
    margin-top: 16px;
  }
  .modal-tab {
    padding: 7px 13px 9px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    font-family: var(--font-main);
    transition: all 0.15s;
    margin-bottom: -1px;
    white-space: nowrap;
  }
  .modal-tab.active {
    color: var(--dc-blue);
    border-bottom-color: var(--dc-blue);
  }
  [data-theme="dark"] .modal-tab.active { color: var(--dc-accent); border-bottom-color: var(--dc-accent); }

  .modal-body { padding: 20px 22px 22px; }
  .modal-panel { display: none; }
  .modal-panel.active { display: block; }

  .modal-section-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.8px;
    margin-bottom: 10px;
  }
  .modal-text {
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.7;
    margin-bottom: 14px;
  }
  .modal-text strong { color: var(--text-primary); font-weight: 600; }

  .modal-highlight {
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 15%, transparent);
    border-radius: 10px;
    padding: 12px 14px;
    margin-bottom: 14px;
    font-size: 12.5px;
    color: var(--dc-blue);
    font-weight: 500;
    line-height: 1.6;
    display: flex;
    gap: 9px;
    align-items: flex-start;
  }
  [data-theme="dark"] .modal-highlight { color: var(--text-primary); background: color-mix(in srgb, var(--dc-blue) 10%, transparent); border-color: color-mix(in srgb, var(--dc-blue) 20%, transparent); }
  .modal-highlight svg { flex-shrink: 0; margin-top: 2px; }

  .stack-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 8px;
    margin-bottom: 14px;
  }
  .stack-item {
    background: var(--bg-primary);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    padding: 10px 12px;
    display: flex;
    gap: 12px;
    align-items: flex-start;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s, background 0.15s;
  }
  a.stack-item:hover {
    border-color: var(--dc-blue);
    background: var(--dc-blue-light);
  }
  [data-theme="dark"] a.stack-item:hover {
    border-color: var(--dc-accent);
    background: color-mix(in srgb, var(--dc-blue) 12%, transparent);
  }
  .stack-link {
    color: var(--dc-blue);
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 700;
    font-size: inherit;
  }
  .stack-link:hover { color: var(--dc-blue-dark); }
  [data-theme="dark"] .stack-link { color: var(--dc-accent); }
  [data-theme="dark"] .stack-link:hover { color: var(--dc-blue-tint); }
  .stack-icon {
    width: 28px; height: 28px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    flex-shrink: 0;
  }
  .stack-item-text {}
  .stack-item-name {
    font-size: 12.5px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
    margin-bottom: 3px;
  }
  .stack-item-desc {
    font-size: 11.5px;
    color: var(--text-secondary);
    line-height: 1.5;
  }

  .open-source-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-light);
    font-size: 12.5px;
  }
  .open-source-row:last-child { border-bottom: none; padding-bottom: 0; }
  .open-source-row svg { flex-shrink: 0; margin-top: 2px; color: var(--dc-blue); }
  [data-theme="dark"] .open-source-row svg { color: var(--dc-accent); }
  .open-source-row strong { color: var(--text-primary); font-weight: 600; }
  .open-source-row span { color: var(--text-secondary); line-height: 1.65; }
  .modal-why-row { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-secondary); line-height: 1.65; }
  .modal-why-row strong { color: var(--text-primary); font-weight: 600; }
  .modal-why-row em { font-style: italic; }
  .modal-hour-row { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-secondary); line-height: 1.5; }
  .modal-hour-row strong { color: var(--text-primary); font-weight: 600; }

  .modal-footer-btn {
    width: 100%;
    padding: 11px;
    background: var(--dc-blue);
    color: white;
    border: none;
    border-radius: 10px;
    font-family: var(--font-main);
    font-size: 13.5px;
    font-weight: 700;
    cursor: pointer;
    margin-top: 18px;
    transition: background 0.2s;
    letter-spacing: 0.2px;
  }
  .modal-footer-btn:hover { background: var(--dc-blue-dark); }

  /* ── ONBOARDING (single column — about + why up front, rest collapsed) ── */
  .onboarding-card { max-width: 480px; }

  /* Step 1 hero — full poster, shown at its natural aspect ratio */
  .onboard-poster {
    width: 100%;
    height: auto;
    border-radius: 14px;
    border: 1px solid var(--border-light);
    margin-bottom: 14px;
    display: block;
  }

  /* Step 2 — "works with any open-source model" logo chips */
  .model-chip-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
  }
  .model-chip {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-light);
    border-radius: 10px;
    padding: 7px 11px 7px 8px;
  }
  .model-chip img { width: 20px; height: 20px; object-fit: contain; flex-shrink: 0; border-radius: 4px; }
  .model-chip-name { font-size: 12px; font-weight: 700; color: var(--text-primary); line-height: 1.3; }
  .model-chip-by { font-size: 10.5px; color: var(--text-muted); line-height: 1.3; }

  /* Step 2 — AI setup blocks */
  .onboard-setup-block {
    border: 1px solid var(--border-light);
    border-radius: 12px;
    padding: 14px;
    margin-bottom: 12px;
    background: var(--bg-secondary);
  }
  .onboard-setup-head {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 10px;
  }
  .onboard-setup-num {
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--dc-blue);
    color: #fff;
    font-size: 11.5px;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .onboard-action-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    background: var(--dc-blue-light);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 12px 14px;
    color: var(--dc-blue);
    font-family: var(--font-main);
    font-size: 13.5px;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .onboard-action-btn:hover {
    background: var(--dc-blue);
    border-color: var(--dc-blue);
    color: #fff;
  }
  .onboard-action-btn > span { flex: 1; text-align: left; }
  .onboard-action-arrow { opacity: 0.7; }

  /* Step 2 — installation requirements (collapsible) */
  .install-req { margin-bottom: 14px; }
  .install-req summary {
    cursor: pointer; list-style: none; user-select: none;
    display: flex; align-items: center; gap: 7px;
    font-size: 12.5px; font-weight: 700; color: var(--dc-blue);
    padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px;
    background: var(--bg-secondary);
  }
  .install-req summary::-webkit-details-marker { display: none; }
  .install-req summary::after { content: '▾'; margin-left: auto; font-size: 11px; opacity: 0.7; }
  .install-req[open] summary {
    border-bottom-left-radius: 0; border-bottom-right-radius: 0;
  }
  .install-req[open] summary::after { content: '▴'; }
  .install-req-body {
    border: 1px solid var(--border); border-top: none;
    border-radius: 0 0 10px 10px; padding: 10px 12px;
    background: var(--bg-secondary);
    display: flex; flex-direction: column; gap: 7px;
  }
  .install-req-row {
    display: flex; gap: 10px; font-size: 12px; line-height: 1.5;
    color: var(--text-secondary);
  }
  .install-req-row b { flex: 0 0 60px; color: var(--text-primary); }

  /* Step 2 — model recommender */
  .rec-select-row { display: flex; gap: 10px; margin-bottom: 12px; }
  .rec-select-row label {
    flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px;
    font-size: 11px; font-weight: 700; color: var(--text-secondary);
    text-transform: uppercase; letter-spacing: 0.4px;
  }
  .rec-select {
    width: 100%; background: var(--bg-secondary); border: 1px solid var(--border);
    border-radius: 10px; padding: 9px 10px; color: var(--text-primary);
    font-family: var(--font-main); font-size: 12.5px; font-weight: 600;
    outline: none; cursor: pointer;
  }
  .rec-select:invalid { color: var(--text-muted); }
  .rec-group { margin-bottom: 12px; }
  .rec-group-head {
    display: flex; align-items: center; gap: 7px; font-size: 11.5px; font-weight: 800;
    text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-secondary);
    margin: 10px 0 6px;
  }
  .rec-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
  .rec-row {
    display: flex; align-items: center; gap: 10px; padding: 8px 10px;
    border: 1px solid var(--border-light); border-radius: 10px;
    background: var(--bg-secondary); margin-bottom: 6px;
  }
  .rec-row.dim { opacity: 0.55; }
  .rec-logo { width: 20px; height: 20px; object-fit: contain; border-radius: 5px; flex-shrink: 0; }
  .rec-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  .rec-name { font-size: 12.5px; font-weight: 700; color: var(--text-primary); font-family: var(--font-mono); }
  .rec-name em { font-style: normal; font-weight: 600; font-size: 11px; color: var(--text-muted); margin-left: 6px; }
  .rec-desc { font-size: 11.5px; color: var(--text-secondary); }
  .rec-copy {
    flex-shrink: 0; background: none; border: 1px solid var(--border); border-radius: 8px;
    padding: 5px 10px; font-family: var(--font-main); font-size: 11px; font-weight: 700;
    color: var(--dc-blue); cursor: pointer; transition: background 0.15s;
    min-width: 76px; text-align: center;
  }
  .rec-copy:hover { background: var(--dc-blue-light); }
  .rec-copy.checking { opacity: 0.65; cursor: default; }
  .rec-copy.downloading {
    background: var(--dc-blue); border-color: var(--dc-blue); color: white; cursor: default;
  }
  .rec-copy.done { color: #22C55E; border-color: #22C55E; cursor: default; }
  .rec-copy.error { color: #EF4444; border-color: #EF4444; }

  /* PULL TERMINAL — mimics a terminal window; stays dark in both themes */
  .pull-terminal-card {
    max-width: 520px;
    background: #141024;
    border: 1px solid #332B57;
    border-radius: 12px;
    overflow: hidden;
    padding: 0;
  }
  .pull-terminal-titlebar {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px;
    background: #1C1636;
    border-bottom: 1px solid #332B57;
  }
  .pull-terminal-dots { display: flex; gap: 6px; flex-shrink: 0; }
  .pull-terminal-dots span { width: 10px; height: 10px; border-radius: 50%; display: block; }
  .pull-terminal-dots span:nth-child(1) { background: #ff5f56; }
  .pull-terminal-dots span:nth-child(2) { background: #ffbd2e; }
  .pull-terminal-dots span:nth-child(3) { background: #27c93f; }
  .pull-terminal-title {
    flex: 1; font-family: var(--font-mono); font-size: 12px; color: #948DBA; text-align: center;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pull-terminal-close {
    background: none; border: none; color: #948DBA; cursor: pointer; padding: 3px;
    display: flex; align-items: center; justify-content: center; border-radius: 4px; flex-shrink: 0;
  }
  .pull-terminal-close:hover { background: #332B57; color: #ECE9FA; }
  .pull-terminal-log {
    margin: 0; padding: 14px 16px;
    height: 260px; overflow-y: auto;
    font-family: var(--font-mono); font-size: 12px; line-height: 1.7;
    color: #D5D0E8; background: #141024;
    white-space: pre-wrap; word-break: break-all;
  }
  .pull-terminal-log::-webkit-scrollbar { width: 4px; }
  .pull-terminal-log::-webkit-scrollbar-thumb { background: #332B57; border-radius: 2px; }
  .pull-term-line.ok  { color: #7ee787; font-weight: 700; }
  .pull-term-line.err { color: #ff7b72; font-weight: 700; }
  .rec-cpu-note { font-size: 11.5px; color: var(--text-muted); margin: 2px 0 8px; }
  /* (i) spec help popover */
  .spec-help { display: inline-block; margin-left: auto; }
  .spec-help summary {
    list-style: none; cursor: pointer; width: 18px; height: 18px; border-radius: 50%;
    border: 1.5px solid var(--dc-blue); color: var(--dc-blue); font-size: 11px; font-weight: 800;
    font-style: italic; font-family: Georgia, serif;
    display: flex; align-items: center; justify-content: center;
  }
  .spec-help summary::-webkit-details-marker { display: none; }
  .spec-help[open] summary { background: var(--dc-blue); color: #fff; }
  .spec-help-body {
    position: absolute; right: 10px; top: 38px; z-index: 10;
    width: min(290px, calc(100% - 20px));
    background: var(--bg-panel); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: var(--shadow-card); padding: 12px; font-size: 11.5px; line-height: 1.55;
    color: var(--text-secondary); font-weight: 500; text-transform: none; letter-spacing: 0;
  }
  .spec-help-body code, .spec-help-body kbd {
    font-family: var(--font-mono); font-size: 10.5px; background: var(--bg-secondary);
    border: 1px solid var(--border-light); border-radius: 4px; padding: 1px 4px;
  }

  /* ── CAMP GUIDE — floating + dockable book panel ──────────────── */
  .guide-panel {
    position: fixed;
    top: 74px;
    right: 18px;
    z-index: 600;
    width: 384px;
    max-width: calc(100vw - 24px);
    height: 580px;
    max-height: calc(100dvh - 92px);
    display: flex;
    flex-direction: column;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: 0 24px 64px rgba(0,0,0,0.30);
    overflow: hidden;
    animation: slide-up 0.25s cubic-bezier(0.34,1.56,0.64,1);
  }
  .guide-panel[hidden] { display: none; }

  /* Docked: drop into the body flex flow as a right-side panel (VS Code style) */
  .guide-panel.docked {
    position: relative;
    top: auto; right: auto; left: auto !important;
    width: var(--guide-w, 384px);
    min-width: 240px;
    max-width: 80vw;
    height: auto;
    max-height: none;
    border-radius: 0;
    border-width: 0 0 0 1px;
    box-shadow: none;
    flex-shrink: 0;
    animation: none;
    transition: width 0.2s ease;
  }
  .gp-resizing .guide-panel.docked { transition: none; }   /* no lag while dragging */

  /* Resize splitter — draggable divider on the panel's left edge (docked only) */
  .gp-resizer { display: none; }
  .guide-panel.docked .gp-resizer {
    display: block;
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 7px;
    cursor: col-resize;
    z-index: 3;
  }
  .guide-panel.docked .gp-resizer::after {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 2px;
    background: transparent;
    transition: background 0.15s;
  }
  .guide-panel.docked .gp-resizer:hover::after,
  .gp-resizing .guide-panel.docked .gp-resizer::after { background: var(--dc-blue); }
  [data-theme="dark"] .guide-panel.docked .gp-resizer:hover::after,
  [data-theme="dark"] .gp-resizing .guide-panel.docked .gp-resizer::after { background: var(--dc-accent); }
  .gp-resizing { cursor: col-resize; user-select: none; }

  .gp-head {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 10px 10px 14px;
    border-bottom: 1px solid var(--border-light);
    flex-shrink: 0;
    cursor: move;
    user-select: none;
    touch-action: none;
  }
  .guide-panel.docked .gp-head { cursor: default; }
  .gp-grip { display: flex; color: var(--text-muted); }
  .guide-panel.docked .gp-grip { display: none; }
  .gp-head-icon { display: flex; align-items: center; color: var(--text-secondary); }
  .gp-title { font-size: 13px; font-weight: 800; color: var(--text-primary); white-space: nowrap; }
  .gp-head-spacer { flex: 1; }
  .gp-btn {
    width: 27px; height: 27px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: none;
    cursor: pointer;
    color: var(--text-muted);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    transition: all 0.15s;
  }
  .gp-btn:hover { color: var(--text-primary); background: var(--border-light); border-color: var(--border); }
  .gp-btn.active { color: var(--dc-blue); border-color: var(--dc-blue); background: var(--dc-blue-light); }
  [data-theme="dark"] .gp-btn.active { color: var(--dc-accent); }

  .gp-tabs {
    display: flex; gap: 5px;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border-light);
    overflow-x: auto;
    flex-shrink: 0;
    scrollbar-width: none;
  }
  .gp-tabs::-webkit-scrollbar { display: none; }
  .gp-tab {
    flex-shrink: 0;
    font-family: var(--font-main);
    font-size: 11px; font-weight: 700;
    color: var(--text-muted);
    background: var(--bg-secondary);
    border: 1px solid var(--border-light);
    border-radius: 999px;
    padding: 5px 11px;
    cursor: pointer;
    white-space: nowrap;
    transition: all 0.15s;
  }
  .gp-tab:hover { color: var(--text-primary); border-color: var(--border); }
  .gp-tab.active { background: var(--dc-blue); color: #fff; border-color: var(--dc-blue); }

  .gp-pages { flex: 1; overflow-y: auto; padding: 16px 16px 8px; }
  .gp-pages::-webkit-scrollbar { width: 4px; }
  .gp-pages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
  .gp-page { display: none; animation: fade-in 0.2s ease; }
  .gp-page.active { display: block; }
  .gp-page-h {
    display: flex; align-items: center; gap: 9px;
    font-size: 15.5px; font-weight: 800;
    color: var(--text-primary);
    line-height: 1.2;
    margin-bottom: 5px;
  }
  .gp-chip {
    flex-shrink: 0;
    font-size: 10px; font-weight: 800; letter-spacing: 0.3px;
    color: var(--dc-blue);
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 20%, transparent);
    border-radius: 6px;
    padding: 3px 7px;
  }
  [data-theme="dark"] .gp-chip { color: var(--dc-accent); background: color-mix(in srgb, var(--dc-blue) 15%, transparent); }
  .gp-chip.pre { color: var(--text-muted); background: var(--bg-secondary); border-color: var(--border-light); }
  .gp-page-sub { font-size: 12px; color: var(--text-muted); line-height: 1.55; margin-bottom: 14px; }
  .gp-map-row {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 8px 0; border-bottom: 1px solid var(--border-light);
    font-size: 12.5px; color: var(--text-secondary); line-height: 1.5;
  }
  .gp-map-row:last-child { border-bottom: none; }
  .gp-map-row b { color: var(--dc-blue); font-weight: 800; flex-shrink: 0; width: 48px; }
  [data-theme="dark"] .gp-map-row b { color: var(--dc-accent); }

  .gp-foot {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px;
    border-top: 1px solid var(--border-light);
    flex-shrink: 0;
  }
  .gp-nav {
    display: inline-flex; align-items: center; gap: 5px;
    font-family: var(--font-main);
    font-size: 12.5px; font-weight: 700;
    border: 1px solid var(--border);
    border-radius: 9px;
    padding: 8px 12px;
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.15s;
  }
  .gp-nav:hover:not(:disabled) { border-color: var(--dc-blue); color: var(--dc-blue); }
  .gp-nav:disabled { opacity: 0.4; cursor: not-allowed; }
  .gp-nav.primary { background: var(--dc-blue); color: #fff; border-color: var(--dc-blue); }
  .gp-nav.primary:hover:not(:disabled) { background: var(--dc-blue-dark); color: #fff; }
  .gp-progress { flex: 1; text-align: center; font-size: 11px; font-weight: 700; color: var(--text-muted); letter-spacing: 0.2px; }

  /* Guide panel — responsive */
  @media (max-width: 900px) {
    .guide-panel { width: 340px; }
    .guide-panel.docked { max-width: 60vw; }
  }
  @media (max-width: 640px) {
    /* Floating: near full-screen sheet */
    .guide-panel {
      top: 50px; right: 8px; left: 8px;
      width: auto;
      max-width: none;
      height: auto;
      bottom: 8px;
      max-height: none;
    }
    /* Docked: become an overlay drawer so it never crushes the chat */
    .guide-panel.docked {
      position: fixed;
      top: 0; right: 0; bottom: 0; left: auto !important;
      width: min(var(--guide-w, 384px), 90vw) !important;
      min-width: 0;
      max-width: 90vw;
      height: 100dvh;
      z-index: 600;
      border-left: 1px solid var(--border);
      box-shadow: -10px 0 40px rgba(0,0,0,0.28);
    }
    .guide-panel.docked .gp-resizer { display: none; }   /* no drag-resize on phones */
  }

  .wip-banner {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    background: #FEF9C3;
    border: 1px solid #FDE68A;
    border-radius: 10px;
    padding: 11px 13px;
    margin-bottom: 16px;
    font-size: 12px;
    color: #854D0E;
    line-height: 1.55;
  }
  .wip-banner svg { flex-shrink: 0; margin-top: 1px; }
  .wip-banner strong { font-weight: 800; }
  [data-theme="dark"] .wip-banner {
    background: rgba(133,77,14,0.18);
    border-color: rgba(133,77,14,0.4);
    color: #FCD34D;
  }
  .wip-pill {
    display: inline-block;
    background: #854D0E;
    color: #FEF9C3;
    font-size: 9px;
    font-weight: 800;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    border-radius: 5px;
    padding: 1px 6px;
    margin-left: 6px;
    vertical-align: middle;
  }
  [data-theme="dark"] .wip-pill { background: #FCD34D; color: #422006; }

  /* Phase header (Before camp / Hour 1 … 4) */
  .guide-phase {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 22px 0 12px;
  }
  .guide-phase:first-of-type { margin-top: 4px; }
  .guide-phase-badge {
    flex-shrink: 0;
    min-width: 52px;
    text-align: center;
    background: var(--dc-blue);
    color: white;
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: 0.3px;
    border-radius: 7px;
    padding: 5px 8px;
    line-height: 1.1;
  }
  .guide-phase-badge.pre { background: var(--text-muted); }
  .guide-phase-title {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.25;
  }
  .guide-phase-title small {
    display: block;
    font-size: 11px;
    font-weight: 500;
    color: var(--text-muted);
    margin-top: 1px;
  }

  /* Numbered step */
  .guide-step {
    display: flex;
    gap: 11px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-light);
  }
  .guide-step:last-child { border-bottom: none; }
  .guide-step-num {
    flex-shrink: 0;
    width: 22px; height: 22px;
    border-radius: 50%;
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 20%, transparent);
    color: var(--dc-blue);
    font-size: 11px;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 1px;
  }
  [data-theme="dark"] .guide-step-num { color: var(--dc-accent); background: color-mix(in srgb, var(--dc-blue) 15%, transparent); }
  .guide-step-body { flex: 1; min-width: 0; }
  .guide-step-body b {
    display: block;
    font-size: 13px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 3px;
  }
  .guide-step-body p {
    font-size: 12.5px;
    color: var(--text-secondary);
    line-height: 1.6;
  }
  .guide-step-body p + p { margin-top: 5px; }
  .guide-step-body em { font-style: italic; color: var(--text-muted); }

  /* Inline command / code chip */
  .guide-cmd {
    display: block;
    background: var(--bg-primary);
    border: 1px solid var(--border-light);
    border-radius: 7px;
    padding: 7px 10px;
    margin-top: 6px;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 11.5px;
    color: var(--dc-blue);
    overflow-x: auto;
    white-space: pre;
  }
  [data-theme="dark"] .guide-cmd { color: var(--dc-accent); }
  .guide-step-body a { color: var(--dc-blue); font-weight: 600; text-decoration: none; }
  .guide-step-body a:hover { text-decoration: underline; }
  [data-theme="dark"] .guide-step-body a { color: var(--dc-accent); }

  .logo-wrapper {
    position: relative;
    display: inline-flex;
    cursor: default;
  }
  .sidebar-brand .logo-wrapper {
    flex: 1;
    min-width: 0;
  }

  /* ALIBABA CLOUD FOOTER (legacy) */
  .alibaba-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: linear-gradient(135deg, #FF6A00, #FF9A00);
    border-radius: 4px;
    padding: 1px 7px 1px 5px;
    font-size: 9px;
    font-weight: 800;
    color: white;
    letter-spacing: 0.4px;
    white-space: nowrap;
  }
  .alibaba-cloud-icon {
    display: inline-flex;
    opacity: 0.9;
  }

  /* SETTINGS MODAL */
  .settings-modal {
    position: fixed;
    inset: 0;
    background: rgba(14, 9, 32, 0.72);
    z-index: 500;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 16px;
    backdrop-filter: blur(3px);
    animation: fade-in 0.2s ease;
  }
  .settings-card {
    background: var(--bg-panel);
    border-radius: 20px;
    width: 100%;
    max-width: 420px;
    max-height: 88dvh;
    overflow-y: auto;
    box-shadow: 0 24px 64px rgba(0,0,0,0.3);
    animation: slide-up 0.25s cubic-bezier(0.34,1.56,0.64,1);
    border: 1px solid var(--border);
  }
  .settings-card::-webkit-scrollbar { width: 3px; }
  .settings-card::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
  .settings-header {
    padding: 20px 22px 0;
    display: flex;
    align-items: center;
    gap: 12px;
    border-bottom: 1px solid var(--border-light);
    padding-bottom: 16px;
  }
  .settings-header-icon {
    width: 38px; height: 38px;
    background: var(--dc-blue-light);
    border: 1px solid color-mix(in srgb, var(--dc-blue) 18%, transparent);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--dc-blue);
    flex-shrink: 0;
  }
  [data-theme="dark"] .settings-header-icon { background: color-mix(in srgb, var(--dc-blue) 15%, transparent); color: var(--dc-accent); }
  .settings-header-text { flex: 1; }
  .settings-header-text h2 {
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 2px;
  }
  .settings-header-text p {
    font-size: 11px;
    color: var(--text-muted);
  }
  .settings-body { padding: 20px 22px; display: flex; flex-direction: column; gap: 18px; }
  .settings-field { display: flex; flex-direction: column; gap: 5px; }
  .settings-label {
    font-size: 11px;
    font-weight: 700;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.7px;
  }
  .settings-input {
    background: var(--bg-primary);
    border: 1.5px solid var(--border);
    border-radius: 8px;
    padding: 8px 11px;
    font-family: var(--font-main);
    font-size: 13px;
    color: var(--text-primary);
    outline: none;
    transition: border-color 0.2s;
    width: 100%;
  }
  .settings-input:focus { border-color: var(--dc-blue); }
  .settings-textarea {
    resize: vertical;
    min-height: 80px;
    max-height: 160px;
    line-height: 1.55;
  }
  .settings-hint {
    font-size: 10.5px;
    color: var(--text-muted);
    line-height: 1.5;
  }
  /* Model-control sliders */
  .slider-head { display: flex; align-items: center; justify-content: space-between; }
  .slider-value {
    font-size: 12px;
    font-weight: 700;
    color: var(--dc-blue);
    font-variant-numeric: tabular-nums;
  }
  [data-theme="dark"] .slider-value { color: var(--dc-accent); }
  .settings-slider {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 4px;
    border-radius: 3px;
    background: var(--border);
    outline: none;
    margin: 4px 0 2px;
    cursor: pointer;
  }
  .settings-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--dc-blue);
    border: 2px solid var(--bg-card, #fff);
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
    cursor: pointer;
  }
  .settings-slider::-moz-range-thumb {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--dc-blue);
    border: 2px solid var(--bg-card, #fff);
    cursor: pointer;
  }
  [data-theme="dark"] .settings-slider::-webkit-slider-thumb { background: var(--dc-accent); }
  [data-theme="dark"] .settings-slider::-moz-range-thumb { background: var(--dc-accent); }
  .slider-scale {
    display: flex;
    font-size: 10px;
    color: var(--text-muted);
    margin-top: 3px;
  }
  .slider-scale span { flex: 1; }
  .slider-scale span:first-child { text-align: left; }
  .slider-scale span:last-child { text-align: right; }
  .slider-scale span:not(:first-child):not(:last-child) { text-align: center; }
  /* Persona controls */
  .persona-select-row { display: flex; gap: 8px; align-items: stretch; }
  .persona-select-row select { flex: 1; min-width: 0; cursor: pointer; }
  .persona-new-btn {
    flex: 0 0 auto;
    padding: 0 14px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    background: var(--bg-input, transparent);
    border: 1px solid var(--border);
    border-radius: 8px;
    cursor: pointer;
    font-family: var(--font-main);
    transition: all 0.15s;
    white-space: nowrap;
  }
  .persona-new-btn:hover { border-color: var(--dc-blue); color: var(--dc-blue); }
  .persona-prompt-wrap { position: relative; }
  .persona-expand-btn {
    position: absolute;
    right: 8px;
    bottom: 8px;
    padding: 5px 11px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--dc-blue);
    background: var(--bg-card, #fff);
    border: 1px solid var(--border);
    border-radius: 7px;
    cursor: pointer;
    font-family: var(--font-main);
    transition: all 0.15s;
  }
  .persona-expand-btn:hover { border-color: var(--dc-blue); background: var(--dc-blue); color: #fff; }
  .persona-expand-btn:disabled { opacity: 0.6; cursor: default; }
  [data-theme="dark"] .persona-expand-btn { color: var(--dc-accent); }
  [data-theme="dark"] .persona-expand-btn:hover { background: var(--dc-accent); color: #fff; border-color: var(--dc-accent); }
  .persona-delete-btn {
    align-self: flex-start;
    margin-top: 4px;
    padding: 0;
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    background: none;
    border: none;
    cursor: pointer;
    font-family: var(--font-main);
  }
  .persona-delete-btn:hover { color: #DC2626; text-decoration: underline; }
  .color-row {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
  }
  .color-pick-wrap {
    display: flex;
    align-items: center;
    gap: 7px;
    background: var(--bg-primary);
    border: 1.5px solid var(--border);
    border-radius: 8px;
    padding: 5px 10px 5px 7px;
    cursor: pointer;
    flex: 1;
    min-width: 120px;
    transition: border-color 0.2s;
  }
  .color-pick-wrap:focus-within { border-color: var(--dc-blue); }
  .color-pick-wrap input[type="color"] {
    width: 22px; height: 22px;
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
    border-radius: 4px;
    flex-shrink: 0;
  }
  .color-pick-label {
    font-size: 11.5px;
    color: var(--text-secondary);
    font-weight: 500;
    flex: 1;
  }
  .color-swatch-row { display: flex; gap: 5px; flex-wrap: wrap; }
  .color-swatch {
    width: 20px; height: 20px;
    border-radius: 5px;
    border: 2px solid transparent;
    cursor: pointer;
    transition: transform 0.15s, border-color 0.15s;
    outline: none;
  }
  .color-swatch:hover { transform: scale(1.2); }
  .color-swatch.active { border-color: var(--text-primary); }
  .settings-divider {
    border: none;
    border-top: 1px solid var(--border-light);
    margin: 0;
  }
  .settings-footer {
    padding: 14px 22px 20px;
    border-top: 1px solid var(--border-light);
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .settings-footer-row {
    display: flex;
    gap: 8px;
  }
  .settings-btn-secondary {
    flex: 1;
    padding: 9px;
    background: none;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.15s;
  }
  .settings-btn-secondary:hover { border-color: var(--dc-blue); color: var(--dc-blue); }
  .settings-btn-primary {
    width: 100%;
    padding: 11px;
    background: var(--dc-blue);
    border: none;
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 13px;
    font-weight: 700;
    color: white;
    cursor: pointer;
    transition: background 0.15s;
  }
  .settings-btn-primary:hover { background: var(--dc-blue-dark); }
  .settings-reset-btn {
    flex: 1;
    padding: 9px;
    background: none;
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.15s;
    text-align: center;
  }
  .settings-reset-btn:hover { border-color: var(--border); color: var(--text-secondary); }

  /* TOAST */
  .lang-picker { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
  .lang-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 11px; font-size: 12px; font-weight: 600;
    background: var(--bg-secondary); color: var(--text-secondary);
    border: 1px solid var(--border-light); border-radius: 999px;
    cursor: pointer; font-family: var(--font-main);
    transition: all 0.15s;
  }
  .lang-chip:hover:not(.disabled):not(.active) { border-color: var(--dc-blue); color: var(--dc-blue); }
  .lang-chip.active { background: var(--dc-blue); color: #fff; border-color: var(--dc-blue); }
  .lang-chip.disabled { opacity: 0.55; cursor: not-allowed; }
  .lang-badge {
    font-size: 9.5px; font-weight: 800; letter-spacing: 0.5px;
    padding: 2px 5px; border-radius: 4px; line-height: 1;
  }
  .lang-badge.beta { background: #FEF3C7; color: #92400E; }
  .lang-badge.soon { background: #FDE68A; color: #78350F; }
  .lang-chip.active .lang-badge.beta { background: rgba(255,255,255,0.25); color: #fff; }

  #training-dropzone:hover, #training-dropzone.drag { border-color: var(--dc-blue); color: var(--text-primary); background: color-mix(in srgb, var(--dc-blue) 4%, transparent); }
  .training-file-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border: 1px solid var(--border-light); border-radius: 8px;
    background: var(--bg-secondary); font-size: 12.5px;
  }
  .training-file-item .tf-name { flex: 1; font-weight: 600; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .training-file-item .tf-size { color: var(--text-muted); font-size: 11.5px; }
  .training-file-item .tf-remove {
    background: none; border: none; color: var(--text-muted); cursor: pointer;
    padding: 4px 6px; border-radius: 4px; font-size: 14px; line-height: 1;
  }
  .training-file-item .tf-remove:hover { background: var(--border-light); color: #DC2626; }

  .settings-toast {
    position: fixed;
    bottom: 80px;
    left: 50%;
    transform: translateX(-50%) translateY(10px);
    background: #1A7A45;
    color: white;
    border-radius: 20px;
    padding: 8px 18px;
    font-size: 12.5px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 7px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.2);
    opacity: 0;
    transition: opacity 0.2s, transform 0.2s;
    z-index: 9999;
    pointer-events: none;
    white-space: nowrap;
  }
  .settings-toast.visible { opacity: 1; transform: translateX(-50%) translateY(0); }
  .toast-icon { display: flex; flex-shrink: 0; opacity: 0.9; }

  /* SETTINGS PREVIEW CARD */
  .settings-preview-card {
    display: flex;
    align-items: center;
    gap: 12px;
    background: var(--bg-primary);
    border: 1.5px solid var(--border);
    border-radius: 12px;
    padding: 12px 14px;
  }
  .preview-avatar {
    width: 42px; height: 42px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--dc-blue) 0%, color-mix(in srgb, var(--dc-blue) 73%, transparent) 100%);
    color: #fff;
    font-size: 14px;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    letter-spacing: 0.5px;
    transition: background 0.2s;
    overflow: hidden;
  }
  .preview-info { flex: 1; min-width: 0; }

  /* Profile picture control (Personalize tab).
     Round, because round is the only shape this picture is ever seen in — the
     squircle preview was quietly promising a crop the conversation never uses,
     so a face that fit here could still lose its ears in chat. */
  .settings-avatar-row {
    display: flex;
    align-items: center;
    gap: 14px;
  }
  .avatar-drop {
    position: relative;
    width: 56px; height: 56px;
    flex-shrink: 0;
    padding: 0;
    border: none;
    background: none;
    border-radius: 50%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .avatar-drop:focus-visible { outline: 2px solid var(--dc-blue); outline-offset: 3px; }
  .settings-avatar-preview {
    width: 56px; height: 56px;
    border-radius: 50%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    color: #fff;
    font-size: 18px;
    font-weight: 800;
    letter-spacing: 0.5px;
    box-shadow: 0 2px 8px rgba(22,32,46,0.14);
    transition: filter 0.15s, box-shadow 0.15s;
  }
  .avatar-drop:hover .settings-avatar-preview { filter: brightness(0.9); }
  /* The badge is the affordance: without it a bare picture is just a picture,
     and the only way to learn it was clickable was to click it. */
  .avatar-drop-badge {
    position: absolute;
    right: -1px; bottom: -1px;
    width: 22px; height: 22px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    transition: color 0.15s, border-color 0.15s;
  }
  .avatar-drop:hover .avatar-drop-badge { color: var(--dc-blue); border-color: var(--dc-blue); }
  .settings-avatar-preview .avatar-img,
  .preview-avatar .avatar-img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
  }
  .settings-avatar-text {
    display: flex;
    flex-direction: column;
    gap: 7px;
    min-width: 0;
  }
  .settings-avatar-btns { display: flex; gap: 8px; }
  /* Remove is destructive and rarely what you came for, so it doesn't get the
     same outline as Upload — it stays quiet until you're on it, then commits
     to red rather than pretending the click is reversible. */
  .avatar-remove-btn {
    flex: 0 0 auto;
    padding: 0 12px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    background: none;
    border: 1px solid transparent;
    border-radius: 8px;
    cursor: pointer;
    font-family: var(--font-main);
    transition: all 0.15s;
    white-space: nowrap;
  }
  .avatar-remove-btn:hover {
    color: var(--danger);
    border-color: var(--danger-edge);
    background: var(--danger-tint);
  }
  .preview-greeting {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .preview-name {
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .preview-label {
    font-size: 10px;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.8px;
    margin-left: auto;
    flex-shrink: 0;
    align-self: flex-start;
    padding-top: 2px;
  }

  /* TONE PRESET CHIPS */
  .tone-presets {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    margin-bottom: 2px;
  }
  .tone-preset-chip {
    padding: 7px 4px;
    background: var(--bg-primary);
    border: 1.5px solid var(--border);
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.15s;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .tone-preset-chip:hover { border-color: var(--dc-blue); color: var(--dc-blue); }
  .tone-preset-chip.active {
    background: var(--dc-blue-light);
    border-color: var(--dc-blue);
    color: var(--dc-blue);
  }
  [data-theme="dark"] .tone-preset-chip.active { background: color-mix(in srgb, var(--dc-blue) 18%, transparent); }

  /* ── ICON RAIL ────────────────────────────────────────────────────── */
  .icon-rail {
    width: 58px;
    background: var(--bg-sidebar);
    border: 1px solid var(--sb-border);
    border-right-color: transparent;
    border-radius: var(--radius-lg) 0 0 var(--radius-lg);
    margin: 10px 0 10px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 14px 0 12px;
    gap: 6px;
    flex-shrink: 0;
    z-index: 110;
    /* Snaps back to the square/joined shape as soon as the sidebar starts
       reopening — no delay, since the sidebar is visible from frame one. */
    transition: border-radius 0.15s ease, border-right-color 0.15s ease;
  }
  /* Sidebar panel collapsed — nothing left for the rail's right edge to
     join, so it becomes a standalone rounded pill instead of half-square.
     Delayed to start after the sidebar's own width/margin collapse
     (0.25s) has mostly finished, so the rail doesn't round off early
     while the still-shrinking sidebar is sitting flush against it. */
  .icon-rail:has(+ .sidebar.collapsed) {
    border-right-color: var(--sb-border);
    border-radius: var(--radius-lg);
    transition: border-radius 0.15s ease 0.2s, border-right-color 0.15s ease 0.2s;
  }
  .rail-toggle-btn { margin-bottom: 8px; }
  .rail-btn {
    width: 40px; height: 40px;
    border-radius: 12px;
    border: none; background: transparent;
    color: var(--sb-text-dim);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    position: relative;
    transition: all 0.15s;
    flex-shrink: 0;
  }
  .rail-btn:hover { color: var(--sb-text-strong); background: var(--sb-hover-bg); }
  .rail-btn.active { color: var(--sb-text-strong); background: rgba(255,255,255,0.16); }
  .rail-btn.active::before {
    content: '';
    position: absolute; left: -9px; top: 10px; bottom: 10px;
    width: 3px; border-radius: 3px;
    background: var(--sb-accent);
  }
  .rail-spacer { flex: 1; }
  .rail-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    margin: 4px 0 8px;
    flex-shrink: 0;
  }
  .rail-dot.ok { background: #22C55E; box-shadow: 0 0 0 4px rgba(34,197,94,0.15); }
  .rail-dot.err { background: #EF4444; box-shadow: 0 0 0 4px rgba(239,68,68,0.15); }
  .rail-dot.checking { background: #F59E0B; box-shadow: 0 0 0 4px rgba(245,158,11,0.15); animation: pulse 1s infinite; }
  @media (max-width: 640px) { .icon-rail { display: none; } }

  /* ── MOBILE SIDEBAR TRIGGERS — stand in for the icon rail, which is
     hidden below 640px. One floats over the welcome screen (which has no
     header at all); the other lives inside the header once a chat starts. */
  .mobile-menu-btn, .header-menu-btn { display: none; }
  @media (max-width: 640px) {
    .mobile-menu-btn {
      display: flex;
      align-items: center;
      justify-content: center;
      position: fixed;
      top: 12px; left: 12px;
      width: 38px; height: 38px;
      border-radius: 10px;
      border: 1px solid var(--border);
      background: var(--bg-panel);
      color: var(--text-secondary);
      z-index: 90;
      box-shadow: 0 2px 10px rgba(0,0,0,0.15);
      cursor: pointer;
    }
    /* Hide it once a conversation's header (with its own trigger) is showing,
       or while the drawer is already open. */
    body:has(.main:not(.welcome-mode)) .mobile-menu-btn,
    body:has(.sidebar.open) .mobile-menu-btn { display: none; }

    .header-menu-btn {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 32px; height: 32px;
      border-radius: 8px;
      border: none;
      background: none;
      color: var(--text-secondary);
      cursor: pointer;
      flex-shrink: 0;
    }
    .header-menu-btn:hover { background: var(--border-light); color: var(--text-primary); }
  }

  /* Rail replaces these header buttons on desktop */
  @media (min-width: 641px) {
    .icon-btn[title^="Camp Guide"],
    .icon-btn[title="Personalize AI"],
    .icon-btn[title="Toggle theme"] { display: none; }
  }

  /* ── SIDEBAR COLLAPSE TOGGLE (desktop only — mobile uses the drawer) ── */
  .sidebar-collapse-btn {
    width: 26px; height: 26px;
    border-radius: 7px;
    border: none;
    background: transparent;
    color: var(--sb-text-dim);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex-shrink: 0;
    transition: all 0.15s;
  }
  .sidebar-collapse-btn:hover { background: var(--sb-hover-bg); color: var(--sb-text-strong); }
  @media (max-width: 640px) { .sidebar-collapse-btn { display: none; } }

  /* ── SEGMENTED TABS (Chats / Sources) ────────────────────────────── */
  .seg-tabs {
    display: flex;
    gap: 4px;
    margin: 4px 12px 6px;
    background: var(--border-light);
    border-radius: 11px;
    padding: 3px;
    flex-shrink: 0;
  }
  .seg-tabs button {
    flex: 1;
    border: none;
    border-radius: 9px;
    background: transparent;
    font-family: var(--font-main);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    padding: 7px 0;
    cursor: pointer;
    transition: all 0.15s;
  }
  .seg-tabs button.on {
    background: var(--bg-panel);
    color: var(--text-primary);
    box-shadow: 0 1px 4px rgba(30,22,58,0.10);
  }
  [data-theme="dark"] .seg-tabs button.on { background: var(--bg-primary); }
  #sidebar[data-tab="sources"] .sidebar-section,
  #sidebar[data-tab="sources"] .history-list { display: none; }
  #sidebar[data-tab="chats"] #kb-panel { display: none; }
  .sidebar.collapsed .seg-tabs,
  .sidebar.collapsed #kb-panel { display: none; }

  /* ── SOURCES PANEL ────────────────────────────────────────────────── */
  #kb-panel {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    padding: 4px 8px 8px;
    border-radius: 12px;
    transition: box-shadow 0.3s;
  }
  #kb-panel.flash { box-shadow: 0 0 0 2px var(--dc-blue); }
  .kb-head {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 8px 8px 6px;
    flex-shrink: 0;
  }
  .kb-title {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: var(--text-muted);
  }
  .kb-total {
    font-size: 9.5px;
    font-weight: 700;
    color: var(--text-muted);
    background: var(--border-light);
    border-radius: 10px;
    padding: 1px 7px;
  }
  .kb-add {
    margin-left: auto;
    border: 1px dashed var(--border);
    background: transparent;
    color: var(--dc-blue);
    font-family: var(--font-main);
    font-size: 10.5px;
    font-weight: 700;
    border-radius: 8px;
    padding: 3px 9px;
    cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
  }
  .kb-add:hover { background: var(--dc-blue-light); border-color: var(--dc-blue); }
  [data-theme="dark"] .kb-add { color: var(--dc-blue-tint); }
  [data-theme="dark"] .kb-add:hover { background: color-mix(in srgb, var(--dc-blue) 18%, transparent); }
  .kb-list {
    overflow-y: auto;
    flex: 1;
    min-height: 60px;
    display: flex;
    flex-direction: column;
    gap: 1px;
  }
  .kb-list::-webkit-scrollbar { width: 3px; }
  .kb-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
  .kb-empty {
    font-size: 11px;
    color: var(--text-muted);
    line-height: 1.55;
    padding: 8px 10px 10px;
  }
  .kb-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 6px 8px;
    border-radius: 9px;
    cursor: pointer;
    transition: background 0.12s;
    user-select: none;
  }
  .kb-item:hover { background: var(--bg-primary); }
  [data-theme="dark"] .kb-item:hover { background: rgba(255,255,255,0.04); }
  .kb-item.off { opacity: 0.45; }
  .kb-ico {
    width: 26px; height: 26px;
    border-radius: 7px;
    background: var(--border-light);
    display: flex; align-items: center; justify-content: center;
    font-size: 12px;
    flex-shrink: 0;
  }
  .kb-meta { flex: 1; min-width: 0; }
  .kb-meta b {
    display: block;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .kb-meta i {
    font-style: normal;
    font-size: 9.5px;
    color: var(--text-muted);
  }
  .kb-del {
    display: none;
    border: none; background: none;
    color: var(--text-muted);
    font-size: 11px;
    padding: 2px 5px;
    border-radius: 5px;
    cursor: pointer;
    flex-shrink: 0;
    line-height: 1;
  }
  .kb-item:hover .kb-del { display: block; }
  .kb-del:hover { color: #DC2626; background: rgba(220,38,38,0.1); }
  .kb-item input[type="checkbox"] {
    width: 14px; height: 14px;
    accent-color: var(--dc-blue);
    cursor: pointer;
    flex-shrink: 0;
  }

  /* ── FLAT, DOCUMENT-STYLE ANSWERS (Study 2) ──────────────────────── */
  .bubble.ai {
    background: transparent;
    border: none;
    box-shadow: none;
    border-radius: 0;
    padding: 2px 0 0;
  }
  .bubble.user {
    background: var(--bg-primary);
    color: var(--text-primary);
    border: 1px solid var(--border-light);
    font-weight: 500;
    /* Notch at the top-right, where the avatar now is — it was bottom-right,
       aimed at where a bottom-aligned avatar used to sit. */
    border-radius: 20px 4px 20px 20px;
  }
  [data-theme="dark"] .bubble.user { background: rgba(255,255,255,0.05); }

  /* Centered document column + composer — .message-row/.message-time/
     .msg-meta-wrap/.error-bubble are appended directly as #chat-area
     children (no wrapping group element), so each is centered individually */
  .message-row,
  .message-time,
  .error-bubble {
    max-width: 720px;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
  }
  /* Keep its 42px indent (aligns under the bubble text, not the avatar) —
     use padding instead of margin-left so centering math still works */
  .msg-meta-wrap {
    max-width: 720px;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
    padding-left: 42px;
  }
  /* Popover positions from the wrapper's own edge, before the padding —
     shift it to match the now-padded meta text it hangs off of */
  .msg-meta-wrap .msg-stats-popover { left: 42px; }
  .input-box {
    max-width: 720px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 20px;
    box-shadow: 0 10px 34px rgba(22,32,46,0.08);
  }
  [data-theme="dark"] .input-box { box-shadow: 0 10px 34px rgba(0,0,0,0.45); }
  .input-tip, .legal-disclaimer-inner { max-width: 720px; margin-left: auto; margin-right: auto; }

/* ── PUBLISHED / VISITOR MODE ─────────────────────────────────────────
   A student's deployed AI: usable by anyone, editable by no one. The
   owner-only chrome is REMOVED from the DOM by lockVisitorUI() (see
   app/publish.js) — this rule is the safety net for anything rendered
   after that sweep, e.g. the welcome screen, which re-renders on every
   new conversation. */
body.visitor-mode [data-owner-only]:not([data-visitor-ok]) { display: none !important; }

/* Credit + the honesty line. The camp teaches "free, private, no cloud";
   this public copy answers through a hosted model, and saying so is the
   difference between proving the lesson and contradicting it. */
.published-credit {
  max-width: 720px;
  margin: 8px auto 0;
  padding: 0 4px;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--text-muted);
}
.published-credit-main {
  font-weight: 700;
  color: var(--text-secondary);
}
.published-credit-note { margin-top: 2px; }
.published-credit-note a {
  color: var(--dc-blue);
  font-weight: 600;
  text-decoration: none;
}
.published-credit-note a:hover { text-decoration: underline; }

/* Read-only source rows — no remove button, no include/exclude checkbox */
body.visitor-mode .kb-item { cursor: default; }

/* ── PUBLISH TAB (owner side) ─────────────────────────────────────────── */
.publish-row {
  display: grid;
  grid-template-columns: 108px 1fr;
  gap: 8px;
  padding: 7px 10px;
  font-size: 12.5px;
  border-bottom: 1px solid var(--border-light);
}
.publish-row:last-child { border-bottom: none; }
.publish-row b { font-weight: 700; color: var(--text-secondary); }
.publish-row span { color: var(--text-primary); }
#publish-summary {
  border: 1px solid var(--border-light);
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 12px;
}
/* Rows that report a gap link to the tab that fixes it, so the summary is a
   starting point rather than a verdict. */
.publish-link {
  color: var(--dc-blue);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.publish-link:hover { text-decoration-thickness: 2px; }
.publish-warn { color: var(--danger); font-weight: 600; }
