/* =========================================================
   Orbit — one minimal theme, light + dark
   Liquid glass surfaces, soft shadows, spring transitions.
   Adapts to system light/dark mode.
   ========================================================= */

/* ---------- Self-hosted document fonts ----------
   Curated Google-Fonts subset (latin, 400+700) used by the Document
   Collaborator's font picker. Self-hosted (not via the Google CDN) so
   they're available offline AND actually embedded when html2canvas
   rasterizes a doc for PDF export. Web-safe fonts (Georgia, Times New
   Roman, Arial, Courier New) need no @font-face. Italics synthesize. */
@font-face { font-family: 'Lora'; font-weight: 400; font-display: swap; src: url('../fonts/lora-400.woff2') format('woff2'); }
@font-face { font-family: 'Lora'; font-weight: 700; font-display: swap; src: url('../fonts/lora-700.woff2') format('woff2'); }
@font-face { font-family: 'Merriweather'; font-weight: 400; font-display: swap; src: url('../fonts/merriweather-400.woff2') format('woff2'); }
@font-face { font-family: 'Merriweather'; font-weight: 700; font-display: swap; src: url('../fonts/merriweather-700.woff2') format('woff2'); }
@font-face { font-family: 'Roboto Slab'; font-weight: 400; font-display: swap; src: url('../fonts/roboto-slab-400.woff2') format('woff2'); }
@font-face { font-family: 'Roboto Slab'; font-weight: 700; font-display: swap; src: url('../fonts/roboto-slab-700.woff2') format('woff2'); }
@font-face { font-family: 'Playfair Display'; font-weight: 400; font-display: swap; src: url('../fonts/playfair-display-400.woff2') format('woff2'); }
@font-face { font-family: 'Playfair Display'; font-weight: 700; font-display: swap; src: url('../fonts/playfair-display-700.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-weight: 400; font-display: swap; src: url('../fonts/source-sans-3-400.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-weight: 700; font-display: swap; src: url('../fonts/source-sans-3-700.woff2') format('woff2'); }
@font-face { font-family: 'EB Garamond'; font-weight: 400; font-display: swap; src: url('../fonts/eb-garamond-400.woff2') format('woff2'); }
@font-face { font-family: 'EB Garamond'; font-weight: 700; font-display: swap; src: url('../fonts/eb-garamond-700.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-weight: 400; font-display: swap; src: url('../fonts/jetbrains-mono-400.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-weight: 700; font-display: swap; src: url('../fonts/jetbrains-mono-700.woff2') format('woff2'); }
/* Inter — the UI face. Self-hosted per the no-CDN rule (also keeps the
   privacy policy's third-party list short). */
@font-face { font-family: 'Inter'; font-weight: 400; font-display: swap; src: url('../fonts/inter-400.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-weight: 700; font-display: swap; src: url('../fonts/inter-700.woff2') format('woff2'); }

/* ---------- Design tokens (dark = default) ----------
   One theme, two modes. Minimal, flat, Linear/Notion-leaning: solid
   surfaces separated by hairlines, restrained radii, quiet shadows.
   The legacy --glass* names are kept (hundreds of usages) but now
   resolve to SOLID surface colours — the glass/blur era is over.

   --accent is the brand colour: index.php inlines the admin-chosen
   value after this sheet loads, and every derived accent token uses
   color-mix() so the whole palette follows automatically. */
:root {
    color-scheme: light dark;

    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Text',
                 'Segoe UI', Roboto, system-ui, sans-serif;
    --font-mono: ui-monospace, 'SF Mono', 'JetBrains Mono', 'Menlo', 'Consolas', monospace;

    /* Surfaces — Linear-style frame: --bg-base is the window CHROME
       (near-black, the sidebar sits directly on it), --bg-canvas is the
       single elevated content PANE. Neutral cool greys, no purple cast. */
    --bg-base: #0b0b0d;
    --bg-canvas: #131315;
    --glass: #18181b;
    --glass-strong: #1f1f23;
    --glass-stronger: #27272b;
    --glass-elev: #1b1b1f;
    --hairline: rgba(255, 255, 255, 0.08);
    --hairline-soft: rgba(255, 255, 255, 0.05);
    --hairline-strong: rgba(255, 255, 255, 0.15);

    /* Text */
    --text: #ededef;
    --text-2: rgba(237, 237, 239, 0.62);
    --text-3: rgba(237, 237, 239, 0.40);

    /* Accent — admin-set; purple until changed */
    --accent: #7c3aed;
    --accent-2: color-mix(in srgb, var(--accent) 72%, white);
    --accent-soft: color-mix(in srgb, var(--accent) 20%, transparent);
    --accent-fg: #ffffff;

    /* Status */
    --danger: #f0505c;
    --danger-soft: rgba(240, 80, 92, 0.14);
    --success: #2fbf8f;
    --success-soft: rgba(47, 191, 143, 0.14);
    --warning: #d9a13c;

    /* Geometry — tighter than the old iOS look */
    --radius-xs: 6px;
    --radius-sm: 8px;
    --radius: 10px;
    --radius-lg: 14px;
    --radius-xl: 18px;
    --radius-xl-mobile: 26px;
    --radius-pill: 999px;

    /* Shadows — quiet; hairlines carry the structure */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.25);
    --shadow-2: 0 2px 8px rgba(0, 0, 0, 0.30), 0 8px 28px rgba(0, 0, 0, 0.24);
    --shadow-float: 0 1px 2px rgba(0, 0, 0, 0.28), 0 10px 30px rgba(0, 0, 0, 0.30);

    /* Motion */
    --ease: cubic-bezier(0.32, 0.72, 0, 1);
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --t-fast: 120ms;
    --t: 180ms;
    --t-slow: 320ms;

    /* Layout */
    --sidebar-w: 260px;
    --header-h: 54px;
    --gutter: 8px;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
}

/* ---------- Light mode ---------- */
@media (prefers-color-scheme: light) {
    :root {
        /* Light frame: grey chrome behind a white pane. */
        --bg-base: #f4f4f5;
        --bg-canvas: #ffffff;
        --glass: #ffffff;
        --glass-strong: #f4f4f6;
        --glass-stronger: #ececef;
        --glass-elev: #ffffff;
        --hairline: rgba(18, 17, 24, 0.10);
        --hairline-soft: rgba(18, 17, 24, 0.06);
        --hairline-strong: rgba(18, 17, 24, 0.16);

        --text: #17161d;
        --text-2: rgba(23, 22, 29, 0.64);
        --text-3: rgba(23, 22, 29, 0.42);

        --accent-2: color-mix(in srgb, var(--accent) 82%, black);
        --accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);

        --shadow-1: 0 1px 2px rgba(20, 20, 40, 0.05);
        --shadow-2: 0 2px 6px rgba(20, 20, 40, 0.06), 0 12px 32px rgba(20, 20, 40, 0.08);
        --shadow-float: 0 1px 2px rgba(20, 20, 40, 0.06), 0 10px 28px rgba(20, 20, 40, 0.10);
    }
}

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

html, body { height: 100%; }
html, body { background: var(--bg-base); }
/* Block the browser's horizontal-overscroll-back-navigation gesture so
   the sidebar swipe can't accidentally trigger it (Chrome/Firefox/
   Edge). iOS Safari ignores this — it's blocked separately by
   preventDefault() inside the touchmove handler in app.js. */
html, body { overscroll-behavior-x: none; }
body {
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.5;
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow: hidden;
    overscroll-behavior: none;
    text-rendering: optimizeLegibility;
}
input, textarea, select, button { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-2); }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition-duration: 0.001ms !important; animation-duration: 0.001ms !important; }
}

/* ---------- App shell ---------- */
#app {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    /* No explicit height on desktop: with inset:0 the shell already
       fills the visible viewport. Adding height:100dvh on top would
       cause the bottom edge to clip slightly when the window is
       resized small (dvh > available height). The mobile media query
       below adds dvh back specifically because iOS PWA standalone
       resolves inset:0 to the layout viewport and needs the kicker. */
    z-index: 1;
    display: flex;
    /* Linear frame: the sidebar sits flush on the window chrome (no
       gutter of its own); the content pane carries the inset via its
       margins. Safe-area padding stays for iPad/PWA status bars. */
    padding-top: var(--safe-top);
    padding-bottom: var(--safe-bottom);
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
    gap: 0;
    overflow: hidden;
}

/* ---------- Auth / centered cards ---------- */
.auth-view {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.auth-card {
    width: 100%;
    max-width: 420px;
    padding: 36px 32px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-2);
    animation: rise 420ms var(--ease) both;
}
.auth-card h1 {
    font-size: 28px;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: 6px;
}
.auth-card .subtitle {
    color: var(--text-2);
    font-size: 14px;
    margin-bottom: 28px;
}

@keyframes rise {
    from { opacity: 0; transform: translateY(14px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* ---------- Forms ---------- */
.form-group { margin-bottom: 14px; }
.form-group label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: 6px;
    letter-spacing: 0.01em;
}

.form-input {
    width: 100%;
    padding: 12px 14px;
    font-size: 15px;
    color: var(--text);
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    outline: none;
    transition: border-color var(--t) var(--ease), background var(--t) var(--ease), box-shadow var(--t) var(--ease);
    -webkit-appearance: none;
    appearance: none;
}
.form-input::placeholder { color: var(--text-3); }
.form-input:hover { background: var(--glass-strong); }
.form-input:focus {
    border-color: var(--accent);
    background: var(--glass-strong);
    box-shadow: 0 0 0 4px var(--accent-soft);
}

textarea.form-input { resize: vertical; min-height: 110px; line-height: 1.5; }

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 11px 20px;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.005em;
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: transform var(--t-fast) var(--ease), background var(--t) var(--ease),
                box-shadow var(--t) var(--ease), opacity var(--t) var(--ease);
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn-primary {
    background: var(--accent);
    color: var(--accent-fg);
}
.btn-primary:hover { filter: brightness(1.05); }

.btn-secondary {
    background: var(--glass-strong);
    color: var(--text);
    border: 1px solid var(--hairline);
}
.btn-secondary:hover { background: var(--glass-stronger); }

.btn-danger {
    background: var(--danger-soft);
    color: var(--danger);
    border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
}
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 22%, transparent); }

.btn-sm { padding: 7px 14px; font-size: 13px; }
.btn-full { width: 100%; }

/* ---------- Sidebar ---------- */
/* Linear-style: NOT a card. The sidebar renders directly on the window
   chrome (--bg-base) with no border, radius, or shadow — the content
   pane's border is the only separator. The mobile media query turns it
   back into an opaque floating sheet for the slide-over. */
.sidebar {
    width: var(--sidebar-w);
    height: 100%;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: transparent;
    overflow: hidden;
    transition: transform var(--t) var(--ease);
}

.sidebar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Desktop: collapse to zero — there's no close button here, and the
       brand mark/title were removed. The mobile media query below
       restores the 56px row so the close button has a target. */
    padding: 0 18px;
    min-height: 0;
}
.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 700;
    font-size: 15px;
    letter-spacing: -0.01em;
}
.sidebar-brand-mark {
    width: 28px; height: 28px;
    border-radius: 8px;
    background: var(--accent);
    display: grid; place-items: center;
    color: white;
    font-weight: 800;
    font-size: 13px;
}
/* Higher specificity than `.icon-btn` so the close button stays hidden on
   desktop — the mobile media query re-shows it. */
.sidebar-header .sidebar-close {
    display: none;
    width: 32px; height: 32px;
    border-radius: var(--radius-pill);
    align-items: center; justify-content: center;
    color: var(--text-2);
    background: var(--glass);
    border: 1px solid var(--hairline);
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.sidebar-header .sidebar-close:hover { background: var(--glass-strong); color: var(--text); }

/* Quiet, bordered create button (Linear-style) — the accent is saved
   for primary actions inside the pane, not the nav chrome. */
.new-chat-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 12px 12px 10px;
    padding: 9px 12px;
    background: var(--glass-strong);
    color: var(--text);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    font-weight: 600;
    transition: background var(--t) var(--ease), border-color var(--t) var(--ease), transform var(--t-fast) var(--ease);
}
.new-chat-btn:hover { background: var(--glass-stronger); border-color: var(--hairline-strong); }
.new-chat-btn:active { transform: scale(0.98); }
.new-chat-btn svg { width: 15px; height: 15px; color: var(--text-2); }

.chat-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 4px 8px 8px;
    -webkit-overflow-scrolling: touch;
    scrollbar-gutter: stable;
}

/* Infinite-scroll trip wire at the bottom of the chat list (see sidebar.js). */
.chat-list-sentinel { height: 1px; }

.chat-list-empty {
    padding: 32px 16px;
    text-align: center;
    font-size: 13px;
    color: var(--text-3);
}

.chat-list-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    margin-bottom: 2px;
    transition: background var(--t) var(--ease);
    color: var(--text);
}
/* Hover styles gated to hover-capable pointers (mouse/trackpad). Touch
   devices skip these so a single tap doesn't first paint a hover state
   that the user mistakes for a no-op, then require a second tap. */
@media (hover: hover) {
    .chat-list-item:hover { background: var(--glass); }
    .chat-list-item:hover .chat-actions { opacity: 1; width: auto; overflow: visible; }
}
.chat-list-item.active {
    background: var(--accent-soft);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
}

.chat-list-item .chat-title {
    flex: 1;
    font-size: 13.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.chat-list-item .chat-actions {
    display: flex;
    gap: 2px;
    opacity: 0;
    /* Collapse to zero width while hidden so the invisible edit/delete
       buttons don't reserve space and truncate the title early — that
       reserved gap was the empty strip on every non-active row. Width is
       restored when the row is active (buttons always shown) or hovered. */
    width: 0;
    overflow: hidden;
    transition: opacity var(--t) var(--ease);
}
.chat-list-item.active .chat-actions {
    opacity: 1;
    width: auto;
    overflow: visible;
}

.chat-action-btn {
    width: 28px; height: 28px;
    border-radius: var(--radius-pill);
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--text-3);
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.chat-action-btn:hover { background: var(--glass); color: var(--text); }
.chat-action-btn.delete:hover { background: var(--danger-soft); color: var(--danger); }
.chat-action-btn svg { width: 14px; height: 14px; }

.sidebar-tools {
    padding: 6px 10px;
    border-top: 1px solid var(--hairline-soft);
}

.prompt-builder-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    color: var(--text-2);
    font-size: 13.5px;
    font-weight: 500;
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
    /* Reset <button> defaults so the rule works for both <a> and
       <button>. Without these, buttons render with a browser-supplied
       background/border and don't stretch to fill the sidebar column. */
    width: 100%;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    text-decoration: none;
}
.prompt-builder-link:hover { background: var(--glass); color: var(--text); }
.prompt-builder-link svg { width: 16px; height: 16px; opacity: 0.7; flex-shrink: 0; }
.prompt-builder-link:hover svg { opacity: 1; }

/* Tools popup — groups Automations / Prompt Builder / Image Studio.
   Anchors to the Tools button and opens upward, like the user menu. */
.tools-menu { position: relative; }
.tools-menu-btn .chev {
    margin-left: auto;
    width: 14px;
    height: 14px;
    color: var(--text-3);
}
.tools-menu-dropdown {
    position: absolute;
    bottom: calc(100% + 4px);
    left: 0;
    right: 0;
    padding: 6px;
    background: var(--glass-elev);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--t) var(--ease);
    z-index: 50;
}
.tools-menu-dropdown.show {
    opacity: 1;
    pointer-events: auto;
}
.tools-menu-dropdown .menu-item { font-size: 13.5px; }

.sidebar-footer {
    position: relative;
    padding: 10px;
    border-top: 1px solid var(--hairline-soft);
}

.user-menu-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border-radius: var(--radius);
    color: var(--text);
    transition: background var(--t) var(--ease);
}
.user-menu-btn:hover { background: var(--glass); }
.user-menu-btn .username {
    flex: 1;
    text-align: left;
    font-size: 14px;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.user-menu-btn .chev { color: var(--text-3); width: 14px; height: 14px; }

.user-avatar {
    width: 34px; height: 34px;
    border-radius: var(--radius-pill);
    background: var(--accent);
    display: grid; place-items: center;
    color: white;
    font-size: 14px;
    font-weight: 700;
    flex-shrink: 0;
}

.user-menu-dropdown {
    position: absolute;
    bottom: calc(100% - 4px);
    left: 10px;
    right: 10px;
    padding: 6px;
    background: var(--glass-elev);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    opacity: 0;
    pointer-events: none;
    /* Opacity-only fade — NO transform animation. A previous version
       used `transform: translateY(8px) scale(0.96)` for a scoot-up
       entry, but on mobile the 220ms `--t` window was long enough
       that users tapping "Settings" quickly hit the dropdown while
       its hit-target was still 5-8px below its final position. The
       touch fell through to the Projects button directly behind in
       `.sidebar-tools`. Removing the transform makes the dropdown's
       hit area match its visual area from frame 1. */
    transition: opacity var(--t) var(--ease);
    z-index: 50;
}
.user-menu-dropdown.show {
    opacity: 1;
    pointer-events: auto;
}

.menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    border-radius: var(--radius-xs);
    text-align: left;
    color: var(--text);
    font-size: 14px;
    text-decoration: none; /* also used for <a> items in the Tools popup */
    transition: background var(--t-fast) var(--ease);
}
.menu-item:hover { background: var(--glass); }
.menu-item svg { width: 16px; height: 16px; opacity: 0.75; }
.menu-item.danger { color: var(--danger); }
.menu-divider { height: 1px; background: var(--hairline-soft); margin: 4px 0; }

/* ---------- Main column ---------- */
/* The single elevated pane of the Linear frame: slightly lighter than
   the chrome, hairline-bordered, small radius, inset by --gutter on
   top/right/bottom (left inset comes from the sidebar). height:auto —
   the flex stretch minus the margins sizes it; height:100% would
   overflow the shell by the margin amount. */
.main-content {
    flex: 1 1 auto;
    min-width: 0;
    height: auto;
    margin: var(--gutter) var(--gutter) var(--gutter) 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-canvas);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    box-shadow: var(--shadow-1);
    overflow: hidden;
    position: relative;
}

.main-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    min-height: var(--header-h);
    border-bottom: 1px solid var(--hairline-soft);
    background: transparent;
    flex-shrink: 0;
    z-index: 5;
}

.icon-btn {
    width: 36px; height: 36px;
    display: inline-flex;
    align-items: center; justify-content: center;
    border-radius: var(--radius-pill);
    color: var(--text-2);
    transition: background var(--t) var(--ease), color var(--t) var(--ease), transform var(--t-fast) var(--ease);
    -webkit-tap-highlight-color: transparent;
}
.icon-btn:hover { background: var(--glass); color: var(--text); }
.icon-btn:active { transform: scale(0.94); }
.icon-btn svg { width: 18px; height: 18px; }

.hamburger-btn { display: none; }

.header-title {
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.model-selector {
    appearance: none;
    -webkit-appearance: none;
    padding: 7px 32px 7px 14px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-pill);
    color: var(--text);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    outline: none;
    transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239e9eb0' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 12px 12px;
}
.model-selector:hover { background-color: var(--glass-strong); }
.model-selector:focus { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.model-selector option { background: #1c1c24; color: var(--text); }
@media (prefers-color-scheme: light) {
    .model-selector option { background: white; color: var(--text); }
}

.header-spacer { flex: 1; }

/* Local-model reachability badge — sits next to the model selector when
 * the chat's current model is a local-provider one. The model dropdown
 * also renders a 🟢/⚪ dot per local option, but the explicit badge
 * makes the active model's status unmissable. */
.local-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    background: var(--glass);
    border: 1px solid var(--hairline);
    color: var(--text-2);
    font-size: 12px;
    line-height: 1;
    user-select: none;
    /* Never wrap or shrink — the model selector beside it is the flexible
       element that yields space, so "Reachable" stays on one line even when
       the header is tight (homepage empty state on a narrow screen). */
    white-space: nowrap;
    flex-shrink: 0;
}
.local-status-badge.is-down { color: var(--text-3); }

/* Local-model endpoint card — Settings → API. Shares the panel-card
 * shell but adds a few specifics for the detected-models list and the
 * inline reachability indicator. */
.local-endpoint-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 10px;
    font-size: 12px;
    color: var(--text-2);
}
.local-detected-models {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.local-detected-models .detected-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border-radius: var(--radius-xs);
    background: var(--glass);
}
.local-detected-models .detected-row .detected-id {
    color: var(--text-3);
    font-size: 12px;
    font-family: ui-monospace, SFMono-Regular, monospace;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.local-cors-hint {
    margin-top: 14px;
    padding: 10px 12px;
    border-radius: var(--radius-xs);
    background: var(--glass);
    border: 1px solid var(--hairline);
    color: var(--text-2);
    font-size: 12px;
    line-height: 1.55;
}
.local-cors-hint code {
    background: rgba(0, 0, 0, 0.18);
    padding: 1px 6px;
    border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, monospace;
    font-size: 11.5px;
}

/* ---------- Chat container ---------- */
.chat-container {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 22px 16px 28px;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
}

.chat-empty {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--text-3);
    padding: 40px 20px;
    gap: 6px;
}
.chat-empty .empty-icon {
    width: 72px; height: 72px;
    border-radius: var(--radius-xl);
    background: var(--accent);
    display: grid; place-items: center;
    color: white;
    margin-bottom: 16px;
}
.chat-empty .empty-icon svg { width: 32px; height: 32px; }
/* Image Studio: gentle pulse while an image is generating in the chat view. */
.chat-empty .empty-icon.generating { animation: empty-pulse 1.5s ease-in-out infinite; }
@keyframes empty-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.06); }
}
.chat-empty h2 {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text);
}
.chat-empty p { font-size: 14px; color: var(--text-2); max-width: 360px; }

/* ---------- Messages ---------- */
.message {
    max-width: 820px;
    margin: 0 auto 14px;
    animation: msgIn 320ms var(--ease) both;
}
@keyframes msgIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Layout: 2-column grid (avatar + bubble) with the actions row
   spanning both columns underneath. Earlier this was a flex-wrap
   container with `.message-actions { flex-basis: 100% }` to force the
   actions onto their own line — but flex's line-break decision uses
   each child's flex-basis (auto → content max-content for the bubble),
   not its max-width. On a long response whose intrinsic max-content
   exceeded "container minus avatar", flex wrap would push the bubble
   to a new row UNDER the avatar (rendering full-bleed) instead of
   alongside it. Grid sizes the bubble against its grid cell directly,
   so max-width: 70%/85% on the bubble actually clamps placement, not
   just rendered width. */
.message-user,
.message-assistant {
    display: grid;
    column-gap: 8px;
    align-items: end;
}
.message-assistant {
    grid-template-columns: auto 1fr;
    grid-template-areas:
        "avatar bubble"
        "actions actions";
}
.message-user {
    grid-template-columns: 1fr auto;
    grid-template-areas:
        "bubble avatar"
        "actions actions";
}
.message-avatar  { grid-area: avatar; }
.message-bubble  { grid-area: bubble; }
.message-actions { grid-area: actions; }
/* Keep the bubble at content size inside its 1fr cell — without
   justify-self the bubble would stretch to fill the cell, losing the
   chat-bubble feel. max-width on the bubble still caps the upper
   bound; this just keeps short bubbles short. */
.message-assistant .message-bubble { justify-self: start; }
.message-user      .message-bubble { justify-self: end;   }

/* Per-bubble action row (copy / export / delete). Quiet by default,
   brightens on hover/focus of the parent message — same pattern as
   ChatGPT / Claude / iMessage. The 40px inline padding lines the
   actions up with the bubble's outer edge (skipping the 32px avatar
   + 8px gap on the avatar side). */
.message-actions {
    display: flex;
    gap: 2px;
    margin-top: 4px;
    opacity: 0;
    transition: opacity 160ms var(--ease);
}
.message-actions-user      { justify-content: flex-end;   padding-right: 40px; }
.message-actions-assistant { justify-content: flex-start; padding-left:  40px; }
.message:hover .message-actions,
.message:focus-within .message-actions { opacity: 1; }
/* Touch devices: keep the actions discoverable without hover. We
   land on a quiet-but-visible default that brightens on tap-focus. */
@media (hover: none) {
    .message-actions { opacity: 0.55; }
    .message:focus-within .message-actions { opacity: 1; }
}

.message-action-btn {
    width: 28px; height: 28px;
    border-radius: var(--radius-pill);
    color: var(--text-3);
    background: transparent;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--t) var(--ease), color var(--t) var(--ease),
                transform var(--t-fast) var(--ease);
}
.message-action-btn:hover { background: var(--glass-strong); color: var(--text); }
.message-action-btn:active { transform: scale(0.92); }
.message-action-btn svg { width: 14px; height: 14px; }
.message-action-btn.message-action-danger:hover {
    background: var(--danger-soft);
    color: var(--danger);
}
.message-action-btn.is-success { color: var(--success); }

.message-avatar {
    width: 32px; height: 32px;
    border-radius: var(--radius-pill);
    display: grid; place-items: center;
    font-size: 18px; line-height: 1;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(0,0,0,0.18);
    user-select: none;
}

.message-user .message-bubble {
    background: var(--accent);
    color: white;
    border-radius: var(--radius-lg) var(--radius-lg) 6px var(--radius-lg);
    padding: 10px 14px;
    max-width: 70%;
    /* Flex items default to min-width:auto, which is "as wide as the
       intrinsic content size". An intrinsic 1024×1024 generated image
       (or a long unbroken token in a code block) will then refuse to
       shrink below that — pushing the bubble past max-width and out
       of the viewport on mobile. min-width:0 releases that floor so
       max-width actually clamps. */
    min-width: 0;
    font-size: 14.5px;
    line-height: 1.5;
    word-break: break-word;
}
.message-user .message-bubble div { white-space: pre-wrap; }

.message-assistant .message-bubble {
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) 6px;
    padding: 14px 18px;
    max-width: 85%;
    /* See min-width:0 note on .message-user .message-bubble — same
       reasoning: lets the intrinsic size of a generated image / wide
       code block be clamped by max-width instead of pushing the
       bubble past the viewport edge. */
    min-width: 0;
    font-size: 14.5px;
    line-height: 1.65;
    color: var(--text);
}

/* Markdown */
.message-assistant .message-bubble h1,
.message-assistant .message-bubble h2,
.message-assistant .message-bubble h3,
.message-assistant .message-bubble h4,
.message-assistant .message-bubble h5,
.message-assistant .message-bubble h6 {
    margin: 16px 0 8px;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.25;
}
.message-assistant .message-bubble h1 { font-size: 1.35em; }
.message-assistant .message-bubble h2 { font-size: 1.2em; }
.message-assistant .message-bubble h3 { font-size: 1.08em; }
.message-assistant .message-bubble h4 { font-size: 1em; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-2); }
.message-assistant .message-bubble h5 { font-size: 0.92em; color: var(--text-2); }
.message-assistant .message-bubble h6 { font-size: 0.85em; color: var(--text-3); text-transform: uppercase; letter-spacing: 0.06em; }
.message-assistant .message-bubble > h1:first-child,
.message-assistant .message-bubble > h2:first-child,
.message-assistant .message-bubble > h3:first-child,
.message-assistant .message-bubble > h4:first-child,
.message-assistant .message-bubble > h5:first-child,
.message-assistant .message-bubble > h6:first-child { margin-top: 0; }

.message-assistant .message-bubble p { margin: 0 0 10px; }
.message-assistant .message-bubble p:last-child { margin-bottom: 0; }

.message-assistant .message-bubble ul,
.message-assistant .message-bubble ol { margin: 6px 0 10px; padding-left: 22px; }
.message-assistant .message-bubble li { margin-bottom: 4px; }
.message-assistant .message-bubble li > p { margin: 0; }
.message-assistant .message-bubble li::marker { color: var(--text-3); }

.message-assistant .message-bubble hr {
    border: 0;
    height: 1px;
    background: var(--hairline);
    margin: 18px 0;
}

.message-assistant .message-bubble code {
    font-family: var(--font-mono);
    font-size: 0.88em;
    background: var(--glass-strong);
    padding: 2px 6px;
    border-radius: 6px;
    border: 1px solid var(--hairline-soft);
}
.message-assistant .message-bubble pre {
    background: rgba(0, 0, 0, 0.32);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    padding: 14px 16px;
    margin: 10px 0;
    overflow-x: auto;
}
.message-assistant .message-bubble pre code {
    background: none;
    padding: 0;
    border: 0;
    font-size: 13px;
    line-height: 1.55;
    white-space: pre;
}
@media (prefers-color-scheme: light) {
    .message-assistant .message-bubble pre { background: rgba(20, 20, 30, 0.05); }
}

.message-assistant .message-bubble blockquote {
    border-left: 3px solid var(--accent);
    padding: 6px 14px;
    margin: 10px 0;
    color: var(--text-2);
    background: var(--glass);
    border-radius: 8px;
}
.message-assistant .message-bubble blockquote > p:last-child { margin-bottom: 0; }

.message-assistant .message-bubble a { color: var(--accent-2); border-bottom: 1px solid var(--accent-soft); }
.message-assistant .message-bubble a:hover { border-bottom-color: var(--accent-2); }

.message-assistant .message-bubble strong { font-weight: 700; }
.message-assistant .message-bubble em { font-style: italic; }
.message-assistant .message-bubble del { color: var(--text-3); }

/* Tables — wrapped for horizontal overflow on narrow screens */
.message-assistant .message-bubble .md-table-wrap {
    margin: 12px 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    background: var(--glass);
}
.message-assistant .message-bubble .md-table-wrap table {
    margin: 0;
    border: 0;
    border-collapse: separate;
    border-spacing: 0;
    width: 100%;
    font-size: 13px;
}
.message-assistant .message-bubble .md-table-wrap th,
.message-assistant .message-bubble .md-table-wrap td {
    border: 0;
    border-bottom: 1px solid var(--hairline-soft);
    border-right: 1px solid var(--hairline-soft);
    padding: 9px 12px;
    text-align: left;
    vertical-align: top;
    line-height: 1.45;
}
.message-assistant .message-bubble .md-table-wrap th:last-child,
.message-assistant .message-bubble .md-table-wrap td:last-child { border-right: 0; }
.message-assistant .message-bubble .md-table-wrap tr:last-child td { border-bottom: 0; }
.message-assistant .message-bubble .md-table-wrap th {
    background: var(--glass-strong);
    font-weight: 600;
    color: var(--text);
    font-size: 12.5px;
    letter-spacing: 0.01em;
}
.message-assistant .message-bubble .md-table-wrap tbody tr:nth-child(even) { background: var(--glass); }
.message-assistant .message-bubble .md-table-wrap code { font-size: 0.85em; }

/* Attachment chips */
.message-attachments {
    display: flex; flex-wrap: wrap; gap: 8px;
    margin-top: 10px;
}
.attachment-badge {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    background: rgba(255,255,255,0.18);
    border: 1px solid rgba(255,255,255,0.22);
    border-radius: var(--radius-pill);
    font-size: 12.5px;
}
.message-assistant .attachment-badge {
    background: var(--glass);
    border-color: var(--hairline);
    color: var(--text-2);
}
.attachment-preview {
    max-width: 240px;
    max-height: 180px;
    border-radius: var(--radius);
    margin-top: 8px;
    box-shadow: var(--shadow-1);
}

/* ---------- Citations / Sources ---------- */

/* Inline [N] reference, post-processed from markdown into a clickable
   superscript that points at the matching source. */
.citation-ref {
    font-size: 0.72em;
    line-height: 1;
    margin: 0 1px;
    vertical-align: super;
}
.citation-ref a {
    display: inline-block;
    min-width: 16px;
    padding: 1px 5px;
    border-radius: 6px;
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
    text-decoration: none;
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.citation-ref a:hover {
    background: var(--accent);
    color: var(--accent-fg);
}

/* Sources footer attached to assistant messages that drew on web
   results. Sits below the markdown body and any attachments. */
.message-sources {
    margin-top: 14px;
    padding-top: 10px;
    border-top: 1px solid var(--hairline-soft);
}
.message-sources-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
    margin-bottom: 6px;
}
.message-sources-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.message-sources-list li {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 12.5px;
    line-height: 1.4;
}
.message-sources-list .citation-num {
    flex-shrink: 0;
    min-width: 18px;
    padding: 1px 5px;
    border-radius: 5px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 11px;
    font-weight: 600;
    text-align: center;
}
.message-sources-list .citation-link {
    color: var(--text-2);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.message-sources-list .citation-link:hover .citation-title { color: var(--accent); }
.message-sources-list .citation-title { color: var(--text); }
.message-sources-list .citation-host {
    color: var(--text-3);
    margin-left: 6px;
    font-size: 11.5px;
}

/* "Retry with web search" call-to-action attached to assistant
   messages where the model raised the suggest_web_search flag.
   Sits at the bottom of the bubble below any sources, in its own
   row so it reads as an action rather than continuation text. */
.retry-search-cta {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--hairline-soft);
}
.retry-search-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 13px;
    font-weight: 600;
    border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
    transition: background var(--t) var(--ease), color var(--t) var(--ease),
                transform var(--t-fast) var(--ease);
}
.retry-search-btn:hover:not(:disabled) {
    background: var(--accent);
    color: var(--accent-fg);
}
.retry-search-btn:active:not(:disabled) { transform: scale(0.97); }
.retry-search-btn:disabled { opacity: 0.55; cursor: default; }
.retry-search-btn svg { width: 14px; height: 14px; }

/* Pre-stream status line (e.g. "Searching the web…") shown inside the
   assistant bubble before the model's first token arrives. Italic +
   muted so it reads as a transient system note rather than content.
   `display: flex` would otherwise win against `[hidden]`'s default
   `display: none`, so we explicitly opt back into hiding here. */
.message-status {
    font-size: 12.5px;
    color: var(--text-3);
    font-style: italic;
    padding: 2px 0 8px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.message-status[hidden] { display: none; }
.message-status::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    animation: think 1.3s ease-in-out infinite;
}

/* Thinking */
.thinking-indicator {
    max-width: 820px;
    margin: 0 auto 14px;
    animation: msgIn 220ms var(--ease) both;
}
.thinking-dots {
    display: inline-flex;
    gap: 6px;
    padding: 14px 18px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) 6px;
}
.thinking-dots span {
    width: 8px; height: 8px;
    background: var(--accent);
    border-radius: 50%;
    animation: think 1.3s ease-in-out infinite;
}
.thinking-dots span:nth-child(2) { animation-delay: 0.16s; }
.thinking-dots span:nth-child(3) { animation-delay: 0.32s; }
@keyframes think {
    0%, 80%, 100% { transform: scale(0.55); opacity: 0.4; }
    40%           { transform: scale(1);    opacity: 1;   }
}

/* ---------- Composer ---------- */
.chat-input-area {
    flex-shrink: 0;
    /* Desktop: extra bottom breathing room so the input never feels clipped
       against the browser's window edge or OS chrome (Dock/taskbar).
       The mobile media query overrides this with a tighter, safe-area-aware
       value that matches native iOS app spacing. */
    padding: 12px 14px;
    border-top: 1px solid var(--hairline-soft);
    background: transparent;
}

.upload-preview-area {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    max-width: 820px;
    margin: 0 auto 10px;
}
.upload-preview-area:empty { display: none; }

.upload-preview-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px 6px 12px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-pill);
    font-size: 12.5px;
    color: var(--text-2);
    animation: rise 220ms var(--ease) both;
}
.upload-preview-item .remove-upload {
    width: 22px; height: 22px;
    border-radius: 50%;
    color: var(--text-3);
    background: var(--glass-strong);
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.upload-preview-item .remove-upload:hover { background: var(--danger-soft); color: var(--danger); }
.upload-preview-item svg { width: 14px; height: 14px; opacity: 0.7; }

.chat-input-row {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    max-width: 820px;
    margin: 0 auto;
}

.chat-input-wrapper {
    flex: 1;
    position: relative;
    background: var(--glass-strong);
    border: 1px solid var(--hairline);
    border-radius: 24px;
    transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease), background var(--t) var(--ease);
}
.chat-input-wrapper:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--accent-soft);
    background: var(--glass-stronger);
}

/* Wrapper outer height = #chat-input min-height (44) + wrapper border (2)
   = 46px, matching .send-btn so they align flush in the row.
   Right padding makes room for the .composer-tools cluster (four 34px
   buttons + 3×2px gaps = 142px, plus the 6px right offset + ~8px
   breathing margin so text wraps before sliding under the icons). When
   you add or remove a composer tool button, bump this AND the mobile
   override below. */
#chat-input {
    width: 100%;
    padding: 10px 156px 10px 18px;
    min-height: 44px;
    max-height: 220px;
    background: transparent;
    border: 0;
    color: var(--text);
    font-size: 15px;
    outline: none;
    resize: none;
    line-height: 1.45;
    overflow-y: auto;
}
#chat-input::placeholder { color: var(--text-3); }

/* Cluster of in-composer tool buttons (web search + paperclip). Pinned
   to the bottom-right of the input wrapper so they sit alongside the
   last line of input regardless of how tall the textarea grows. */
.composer-tools {
    position: absolute;
    right: 6px;
    bottom: 5px;
    display: flex;
    align-items: center;
    gap: 2px;
}

.composer-tool-btn {
    width: 34px; height: 34px;
    border-radius: var(--radius-pill);
    color: var(--text-2);
    background: transparent;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--t) var(--ease), color var(--t) var(--ease),
                box-shadow var(--t) var(--ease);
}
.composer-tool-btn:hover { background: var(--glass); color: var(--text); }
.composer-tool-btn svg { width: 16px; height: 16px; }
/* Active web-search toggle: tinted background + accent foreground so
   it's unmistakably "on" without being garish. */
.composer-tool-btn.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent-soft);
}
.composer-tool-btn.is-active:hover {
    background: var(--accent-soft);
    color: var(--accent-2);
}

/* Backwards-compat shim: existing layouts and offline rules still
   reference `.upload-btn`. Reset position/size now that the button is
   inside `.composer-tools` instead of being absolutely positioned. */
.upload-btn { position: static; right: auto; bottom: auto; }

.send-btn {
    width: 46px; height: 46px;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    color: white;
    background: var(--accent);
    display: inline-flex; align-items: center; justify-content: center;
    transition: transform var(--t-fast) var(--ease), filter var(--t) var(--ease), opacity var(--t) var(--ease);
    -webkit-tap-highlight-color: transparent;
}
.send-btn:hover { filter: brightness(1.06); }
.send-btn:active { transform: scale(0.94); }
.send-btn:disabled { opacity: 0.4; transform: none; }
.send-btn svg { width: 18px; height: 18px; }

/* ---------- Offline banner ---------- */
.offline-banner {
    display: none;
    align-items: center; justify-content: center;
    gap: 8px;
    padding: 10px 14px;
    margin: 10px 14px 0;
    background: var(--danger-soft);
    border: 1px solid color-mix(in srgb, var(--danger) 26%, transparent);
    color: var(--danger);
    font-size: 13px;
    font-weight: 500;
    border-radius: var(--radius);
}
.offline-banner svg { width: 16px; height: 16px; }
body.offline .offline-banner { display: flex; }
body.offline #chat-input { pointer-events: none; opacity: 0.5; }
body.offline .send-btn,
body.offline .upload-btn { pointer-events: none; opacity: 0.4; }

/* ---------- Settings & Admin panels ---------- */
.panel-view {
    flex: 1;
    overflow-y: auto;
    padding: 24px 20px 32px;
    -webkit-overflow-scrolling: touch;
}

.panel-card {
    max-width: 680px;
    margin: 0 auto 18px;
    padding: 22px 22px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    animation: rise 320ms var(--ease) both;
}

.panel-card h2 {
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-bottom: 4px;
}

.panel-card h3 {
    font-size: 15px;
    margin-bottom: 12px;
    color: var(--text-2);
    font-weight: 600;
}

.panel-card .description {
    font-size: 13px;
    color: var(--text-2);
    margin-bottom: 16px;
    line-height: 1.5;
}

/* ---------- Toggle ---------- */
.toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 6px 0;
    font-size: 14px;
}

.toggle-switch {
    position: relative;
    /* inline-block matters: this is a <label>, and outside flex rows
       (e.g. inside a table cell in Admin → AI & Models) an inline label
       ignores width/height and collapses, leaving the knob floating
       over neighbouring content. */
    display: inline-block;
    vertical-align: middle;
    width: 50px;
    height: 30px;
    flex-shrink: 0;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
    position: absolute;
    inset: 0;
    background: rgba(120, 120, 128, 0.28);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: background var(--t) var(--ease);
}
.toggle-slider::before {
    content: '';
    position: absolute;
    width: 26px; height: 26px;
    left: 2px; top: 2px;
    background: white;
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(0,0,0,0.18);
    transition: transform var(--t) var(--ease);
}
.toggle-switch input:checked + .toggle-slider { background: #34c759; }
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(20px); }

/* ---------- Models management ---------- */
.add-model-form {
    margin: 8px 0 16px;
    padding: 16px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius);
    animation: rise 220ms var(--ease) both;
}
.add-model-form code {
    font-family: var(--font-mono);
    font-size: 0.92em;
    background: var(--glass-strong);
    padding: 1px 6px;
    border-radius: 5px;
}

.model-list {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.model-group-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    margin-bottom: 6px;
    padding-left: 4px;
}

.model-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius);
    transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
    flex-wrap: wrap;
}
.model-row:hover { background: var(--glass-strong); border-color: var(--hairline); }

.model-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 200px;
    cursor: pointer;
}
.model-toggle input[type="checkbox"] {
    accent-color: var(--accent);
    width: 18px; height: 18px;
    cursor: pointer;
    flex-shrink: 0;
}
.model-toggle .model-name {
    font-size: 14px;
    font-weight: 600;
}
.model-toggle .model-id {
    font-family: var(--font-mono);
    font-size: 11.5px;
    color: var(--text-3);
    padding: 2px 8px;
    background: var(--glass);
    border-radius: var(--radius-pill);
    border: 1px solid var(--hairline-soft);
}

.default-radio {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-2);
    cursor: pointer;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    transition: background var(--t) var(--ease);
}
.default-radio:hover { background: var(--glass-strong); }
.default-radio input { accent-color: var(--accent); }
.default-radio:has(input:checked) {
    background: var(--accent-soft);
    color: var(--accent);
}

.model-actions {
    display: flex;
    align-items: center;
    gap: 6px;
}

@media (max-width: 540px) {
    .model-toggle .model-id { display: none; }
    .model-row { gap: 8px; }
}

/* ---------- Invite list ---------- */
.invite-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 0;
    border-bottom: 1px solid var(--hairline-soft);
    font-size: 13px;
}
.invite-item:last-child { border-bottom: none; }
.invite-link-text {
    flex: 1;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.invite-status {
    font-size: 11px;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    font-weight: 600;
    letter-spacing: 0.02em;
}
.invite-status.used    { background: var(--success-soft); color: var(--success); }
.invite-status.expired { background: var(--danger-soft);  color: var(--danger); }
.invite-status.active  { background: var(--accent-soft);  color: var(--accent); }
.invite-delete { width: 28px; height: 28px; flex: none; }
.invite-delete svg { width: 15px; height: 15px; }
.invite-delete:hover { background: var(--danger-soft); color: var(--danger); }

/* ---------- Users table ---------- */
.users-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
.users-table th,
.users-table td {
    padding: 12px 12px;
    text-align: left;
    border-bottom: 1px solid var(--hairline-soft);
}
.users-table th {
    color: var(--text-3);
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.users-table tr:last-child td { border-bottom: 0; }

.badge {
    font-size: 10.5px;
    padding: 3px 8px;
    border-radius: var(--radius-pill);
    font-weight: 600;
    letter-spacing: 0.02em;
    margin-left: 6px;
    vertical-align: middle;
}
.badge-admin     { background: var(--accent-soft); color: var(--accent); }
.badge-suspended { background: var(--danger-soft); color: var(--danger); }

/* Sessions */
.session-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 0;
    border-bottom: 1px solid var(--hairline-soft);
}
.session-item:last-child { border-bottom: none; }
.session-info { flex: 1; min-width: 0; }
.session-device { font-size: 14px; font-weight: 500; margin-bottom: 2px; }
.session-meta { font-size: 12px; color: var(--text-3); }
.session-current {
    font-size: 11px;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    background: var(--success-soft);
    color: var(--success);
    font-weight: 600;
}

/* API key rows */
.api-key-row {
    display: flex;
    gap: 10px;
    margin-bottom: 10px;
    align-items: center;
}
.api-key-row .form-input { flex: 1; font-family: var(--font-mono); font-size: 13px; }
.api-key-row .provider-label {
    width: 90px;
    font-size: 13px;
    font-weight: 600;
    text-transform: capitalize;
    flex-shrink: 0;
}
@media (max-width: 480px) {
    .api-key-row { flex-wrap: wrap; }
    .api-key-row .provider-label { width: 100%; }
}

/* ---------- 2FA card + setup dialog ---------- */
.totp-state {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 8px 0 14px;
}
.totp-pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.totp-pill.is-on  { background: var(--success-soft); color: var(--success); }
.totp-pill.is-off { background: var(--danger-soft);  color: var(--danger);  }
.totp-meta { color: var(--text-3); font-size: 13px; }
.totp-warning {
    margin: 8px 0 14px;
    padding: 10px 12px;
    background: rgba(251, 191, 36, 0.10);
    border: 1px solid rgba(251, 191, 36, 0.28);
    border-radius: var(--radius-sm);
    color: var(--text);
    font-size: 12.5px;
    line-height: 1.5;
}
.totp-actions { display: flex; flex-wrap: wrap; gap: 10px; }

/* TOTP code input (login + setup confirm) — large, monospace, tracked,
   reads as a code-entry rather than ordinary text input. */
.totp-input {
    font-family: var(--font-mono);
    font-size: 22px;
    letter-spacing: 0.18em;
    text-align: center;
}

/* Setup dialog: ordered list with numbered steps, secret + backup-codes
   blocks. Caps width so the steps don't sprawl on desktop. */
.totp-dialog {
    max-width: 460px;
    width: 100%;
    max-height: 88vh;
    overflow-y: auto;
}
.totp-steps {
    margin: 0 0 16px;
    padding-left: 24px;
}
.totp-steps li { margin-bottom: 16px; line-height: 1.5; font-size: 14px; }
.totp-steps li:last-child { margin-bottom: 0; }
.totp-steps strong { font-weight: 600; }

/* QR block in the setup dialog. White background even in dark mode —
   authenticator apps need high contrast on a stable colour to scan
   reliably, and the surrounding dialog already provides the visual
   chrome. Clamp size so the QR doesn't dwarf the rest of the dialog
   on narrow screens. */
.totp-qr {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 12px 0 8px;
    padding: 12px;
    background: #fff;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
}
.totp-qr svg {
    display: block;
    width: 100%;
    max-width: 240px;
    height: auto;
    aspect-ratio: 1 / 1;
}

.totp-link-row { margin: 8px 0 6px; }
.totp-link {
    display: inline-block;
    padding: 7px 12px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
    font-size: 13px;
    border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
}
.totp-link:hover { background: var(--accent); color: var(--accent-fg); }
.totp-or {
    color: var(--text-3);
    font-size: 12.5px;
    margin: 6px 0 4px;
}

.totp-secret-row {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.totp-secret {
    flex: 1;
    min-width: 0;
    padding: 8px 10px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: 13px;
    letter-spacing: 0.04em;
    word-break: break-all;
    user-select: all;
}

.totp-backup-codes {
    margin-top: 6px;
    padding: 10px 12px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-all;
    user-select: all;
}

/* Encryption info card — bullet list of what's covered. */
.encryption-list {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.encryption-list li {
    padding: 8px 12px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-2);
}
.encryption-list strong { color: var(--text); margin-right: 4px; font-weight: 600; }

.checkbox-row {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--text-2);
}

/* ---------- Automations ---------- */

/* List view: card per automation with quick actions. */
.automation-card {
    /* Tighter than a generic panel-card so the list reads as a
       scannable feed rather than walls of chrome. */
    padding: 18px 20px;
}
.automation-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}
.automation-name {
    margin: 0 0 4px;
    font-size: 16px;
    font-weight: 600;
}
.automation-trigger {
    margin: 0;
    color: var(--text-2);
    font-size: 13px;
}
.automation-meta {
    display: flex;
    gap: 18px;
    flex-wrap: wrap;
    color: var(--text-3);
    font-size: 12.5px;
    margin: 10px 0 14px;
}
.automation-meta strong { color: var(--text-2); font-weight: 600; }
.automation-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

/* Builder header — name doubles as the page title. */
.automation-name-input {
    flex: 1;
    max-width: 360px;
    font-size: 15px;
    font-weight: 600;
}
.automation-builder { padding-bottom: 60px; }

/* Vertical chain of nodes with a connector line between them. */
.chain-editor {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 8px;
}

.chain-node {
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    padding: 14px 16px;
    transition: border-color var(--t) var(--ease), background var(--t) var(--ease);
}
.chain-node:focus-within {
    border-color: var(--accent);
    background: var(--glass-stronger);
}
.chain-node-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}
.chain-node-icon {
    font-size: 13px;
    font-weight: 700;
    color: var(--accent);
    width: 18px;
    text-align: center;
}
.chain-node-title {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-2);
    flex: 1;
}
.chain-node-delete {
    width: 26px; height: 26px;
    border-radius: var(--radius-pill);
    color: var(--text-3);
    background: transparent;
    display: inline-flex; align-items: center; justify-content: center;
}
.chain-node-delete:hover { background: var(--danger-soft); color: var(--danger); }
.chain-node-delete svg { width: 12px; height: 12px; }

.chain-node textarea { resize: vertical; }
.chain-node-row {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    flex-wrap: wrap;
}
.chain-node-row .form-input { flex: 1; min-width: 140px; }

/* Per-node web-search toggle. Renders as a quiet pill that lights up
   in accent colours when active — same visual cue as the chat
   composer's globe toggle so the connection is obvious. */
.chain-node-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--hairline);
    background: transparent;
    color: var(--text-2);
    font-size: 12.5px;
    cursor: pointer;
    user-select: none;
    transition: background var(--t) var(--ease), color var(--t) var(--ease),
                border-color var(--t) var(--ease);
}
.chain-node-toggle input { /* offscreen — the label IS the control */
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.chain-node-toggle:hover { color: var(--text); }
.chain-node-toggle svg { width: 13px; height: 13px; }
.chain-node-toggle.is-on {
    background: var(--accent-soft);
    border-color: color-mix(in srgb, var(--accent) 30%, transparent);
    color: var(--accent);
}
.chain-node-toggle.is-on:hover { color: var(--accent-2); }

/* End nodes are visually quieter — they're terminators, not editors. */
.chain-node-end {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    background: var(--glass-strong);
    border: 1px dashed var(--hairline-strong);
    color: var(--text-3);
    font-size: 12.5px;
    width: max-content;
    border-radius: var(--radius-pill);
}

/* If-nodes spawn two indented sub-chains. The branch-group is a
   horizontal layout on desktop, stacks on mobile. */
.branch-group {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    border-left: 2px dashed var(--accent-soft);
    padding: 8px 0 8px 12px;
    margin-top: 4px;
}
.branch-column {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}
.branch-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.branch-true  { color: var(--success); }
.branch-false { color: var(--danger);  }
@media (max-width: 720px) {
    .branch-group { grid-template-columns: 1fr; }
}

/* "Add step" affordance between nodes — quiet by default, brightens
   on hover. */
.chain-insert {
    display: flex;
    justify-content: center;
    padding: 2px 0;
}
.chain-insert-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: var(--radius-pill);
    color: var(--text-3);
    font-size: 12px;
    font-weight: 500;
    background: transparent;
    transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.chain-insert-btn:hover {
    background: var(--accent-soft);
    color: var(--accent);
}
.chain-insert-btn svg { width: 12px; height: 12px; }

/* Tiny floating menu shown when the user hits "Add step". */
.insert-menu {
    position: fixed;
    z-index: 1100;
    background: var(--glass-elev);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-2);
    padding: 4px;
    display: flex;
    flex-direction: column;
    min-width: 160px;
}
.insert-menu button {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border-radius: var(--radius-xs);
    color: var(--text);
    font-size: 13px;
    text-align: left;
    background: transparent;
}
.insert-menu button:hover { background: var(--accent-soft); }
.insert-menu button svg { width: 14px; height: 14px; }

/* iOS-style toggle switch used on the automations list. */
.toggle {
    position: relative;
    display: inline-block;
    flex-shrink: 0;
}
.toggle input {
    opacity: 0;
    width: 0; height: 0;
    position: absolute;
}
.toggle-track {
    display: block;
    width: 44px;
    height: 26px;
    background: var(--hairline-strong);
    border-radius: var(--radius-pill);
    transition: background var(--t) var(--ease);
    cursor: pointer;
}
.toggle-thumb {
    display: block;
    position: absolute;
    top: 2px; left: 2px;
    width: 22px; height: 22px;
    background: white;
    border-radius: 50%;
    box-shadow: var(--shadow-1);
    transition: transform var(--t) var(--ease);
}
.toggle input:checked + .toggle-track { background: var(--accent); }
.toggle input:checked + .toggle-track .toggle-thumb { transform: translateX(18px); }

/* Automation-output badge inside chat bubbles. Subtle pill at the
   top-right of an assistant bubble that the cron wrote. */
.message-bubble.is-automation-output {
    border-color: var(--accent-soft);
    box-shadow: inset 0 0 0 1px var(--accent-soft);
}
.automation-output-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 8px;
}
.automation-output-badge svg { width: 12px; height: 12px; }

/* Quiet pill shown when the AI writes to memory or rewrites its persona
   on a turn. Multiple can stack in .message-notice-host. */
.message-notice-host { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.message-notice:first-child { margin-top: 8px; }
.message-notice {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--text-2);
    font-size: 12px;
    line-height: 1.3;
}
.message-notice svg { width: 13px; height: 13px; color: var(--accent); flex-shrink: 0; }
.message-notice a { color: var(--accent); text-decoration: none; font-weight: 600; }
.message-notice a:hover { text-decoration: underline; }

/* Run history list inside the builder. */
.run-history-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.run-history-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 10px 0;
    border-top: 1px solid var(--hairline-soft);
    font-size: 13px;
}
.run-history-list li:first-child { border-top: 0; }
.run-status {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: var(--radius-pill);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.run-status-success { background: var(--success-soft); color: var(--success); }
.run-status-failed  { background: var(--danger-soft);  color: var(--danger); }
.run-status-running { background: var(--accent-soft);  color: var(--accent); }
.run-status-skipped { background: var(--glass-strong); color: var(--text-3); }
.run-time { color: var(--text-2); }
.run-source { color: var(--text-3); font-size: 11px; }
.run-error {
    flex-basis: 100%;
    color: var(--danger);
    background: var(--danger-soft);
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-family: var(--font-mono);
}

/* Admin cron-setup panel. Three blocks (HTTP / CLI / token) with
   monospace boxes for the values. */
.automations-admin-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    padding: 12px 14px;
    margin-top: 12px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--text-2);
}
.automations-admin-stats strong { color: var(--text); margin-right: 4px; }

.cron-setup {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--hairline-soft);
}
.cron-setup h4 {
    font-size: 14px;
    margin-bottom: 6px;
}
.cron-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0;
    flex-wrap: wrap;
}
.cron-row > label {
    width: 80px;
    flex-shrink: 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
}
.cron-row .form-input { flex: 1; font-family: var(--font-mono); font-size: 12.5px; }
.cron-code {
    font-family: var(--font-mono);
    font-size: 12.5px;
    padding: 6px 10px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-xs);
    color: var(--text);
}
.cron-code-block {
    flex: 1;
    word-break: break-all;
    user-select: all;
}

/* ---------- Toasts ---------- */
.toast-container {
    position: fixed;
    top: calc(20px + var(--safe-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    gap: 10px;
    pointer-events: none;
}
@media (min-width: 640px) {
    .toast-container { top: calc(24px + var(--safe-top)); left: auto; right: 24px; transform: none; }
}

.toast {
    pointer-events: auto;
    padding: 12px 18px;
    border-radius: var(--radius-pill);
    font-size: 13.5px;
    font-weight: 500;
    background: var(--glass-elev);
    border: 1px solid var(--hairline);
    box-shadow: var(--shadow-2);
    color: var(--text);
    max-width: 380px;
    animation: toastIn 280ms var(--ease) both, toastOut 280ms var(--ease) 2.7s forwards;
    display: inline-flex;
    align-items: center;
    gap: 10px;
}
.toast::before {
    content: '';
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--accent);
    flex-shrink: 0;
}
.toast-success { color: var(--success); }
.toast-success::before { background: var(--success); }
.toast-error   { color: var(--danger);  }
.toast-error::before   { background: var(--danger); }
.toast-info::before    { background: var(--accent); }

@keyframes toastIn {
    from { opacity: 0; transform: translateY(-12px) scale(0.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toastOut {
    to { opacity: 0; transform: translateY(-8px) scale(0.96); }
}

/* ---------- Dialog ---------- */
.dialog-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 500;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    animation: fadeIn 200ms var(--ease);
}
@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.dialog {
    background: var(--glass-elev);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
    padding: 24px;
    max-width: 420px;
    width: 100%;
    animation: rise 260ms var(--ease) both;
}
.dialog h3 {
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-bottom: 6px;
}
.dialog p {
    font-size: 14px;
    color: var(--text-2);
    margin-bottom: 18px;
    line-height: 1.5;
}
.dialog-actions { display: flex; gap: 10px; justify-content: flex-end; }

/* ---------- Sidebar overlay (mobile) ---------- */
.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    /* Plain scrim — surfaces are solid now (no blur), so the veil is a
       simple darkening layer. */
    background: rgba(0, 0, 0, 0.42);
    z-index: 199;
    opacity: 0;
    transition: opacity var(--t) var(--ease);
}
.sidebar.open ~ .sidebar-overlay { display: block; opacity: 1; }

/* ---------- Scrollbars ---------- */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: rgba(150, 150, 170, 0.28);
    border-radius: 4px;
    border: 2px solid transparent;
    background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(150, 150, 170, 0.44); background-clip: content-box; }

/* ---------- Responsive ---------- */
@media (max-width: 768px) {
    :root { --gutter: 8px; }

    /* Full-bleed shell on phones — main panel meets the safe-area edges,
       individual sections pad themselves so nothing sits under the
       status bar or home indicator.

       The explicit dvh height matters on iOS PWA standalone: when added
       to the home screen, `position: fixed; inset: 0` alone can resolve
       to the layout viewport (which excludes the area beneath the home
       indicator), leaving a visible gap below the composer. dvh extends
       the shell to the visual viewport so the composer's safe-area
       padding is what actually clears the indicator. */
    #app {
        padding: 0;
        height: 100vh;
        height: 100dvh;
    }

    .main-content {
        border: 0;
        border-radius: 0;
        box-shadow: none;
        margin: 0;
        height: 100%;
        /* Drop the canvas tint on mobile — the body background is the
           canvas, no panel tint should paint over part of the screen
           and not the rest. */
        background: transparent;
        /* Reserve vertical space for the fixed header below. The header
           is taken out of the flex flow by `position: fixed`, so without
           this padding the chat-container / settings-tabs / panel-view
           would jump up and sit underneath it. The 60px is a safe lower
           bound on the rendered header height — min-height is 58px but
           the 36px icon buttons + 12+12 padding push it to ~60px. */
        padding-top: calc(60px + var(--safe-top));
    }

    /* Pin the header to the top of the visual viewport so opening the
       on-screen keyboard doesn't drag it out of view. iOS Safari in a
       regular browser tab scrolls the layout viewport upwards when an
       input is focused to keep that input above the keyboard — anything
       in normal flow gets pulled with it. `position: fixed; top: 0`
       anchors to the visual viewport instead, matching what the
       composer already does at the bottom via the inline style in
       index.php. Solid background so chat content can scroll behind it
       without bleeding through. */
    .main-header {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 10;
        background: var(--bg-base);
        border-bottom: 1px solid var(--hairline-soft);
        padding-top: calc(12px + var(--safe-top));
        padding-left: calc(16px + env(safe-area-inset-left, 0px));
        padding-right: calc(16px + env(safe-area-inset-right, 0px));
    }

    /* iOS 26 introduces floating window controls on iPad that overlap the
       top-left corner of windowed apps. Indent the header enough to clear
       them. Only applies when the viewport is narrow enough to show the
       hamburger (≤768px), which is the split-screen / Stage Manager case
       where the controls are present. */
    html.is-ipad .main-header {
        padding-left: 80px;
    }

    .panel-view {
        padding-left: calc(14px + env(safe-area-inset-left, 0px));
        padding-right: calc(14px + env(safe-area-inset-right, 0px));
        padding-bottom: calc(28px + var(--safe-bottom));
    }

    .chat-container {
        padding-left: calc(14px + env(safe-area-inset-left, 0px));
        padding-right: calc(14px + env(safe-area-inset-right, 0px));
    }

    .chat-input-area {
        /* env() inline (not via CSS variable) — some iOS PWA contexts
           don't propagate env() through custom properties reliably.
           On iPhones with a home indicator the inset is ~34px, which
           is much more than native iOS apps actually leave below their
           composer (Messages/ChatGPT use ~6-10px above the indicator).
           Subtract 24px so the input hugs the bottom like a native app
           while still clearing the indicator's gesture area. Clamp to
           a min of 8px for devices without an inset. */
        padding-top: 8px;
        padding-bottom: max(8px, calc(env(safe-area-inset-bottom, 0px) - 24px));
        padding-left: calc(14px + env(safe-area-inset-left, 0px));
        padding-right: calc(14px + env(safe-area-inset-right, 0px));
    }

    .offline-banner {
        margin-left: calc(10px + env(safe-area-inset-left, 0px));
        margin-right: calc(10px + env(safe-area-inset-right, 0px));
    }

    /* Auth view should also clear status bar / home indicator */
    .auth-view {
        padding-top: calc(24px + var(--safe-top));
        padding-bottom: calc(24px + var(--safe-bottom));
        padding-left: calc(24px + env(safe-area-inset-left, 0px));
        padding-right: calc(24px + env(safe-area-inset-right, 0px));
    }

    /* Sidebar remains a floating overlay, but inset by safe-area on top + bottom.
       `height: auto` is required to override the base `height: 100%`, which would
       otherwise win against `top`/`bottom` and erase the bottom margin. */
    .sidebar {
        position: fixed;
        top: calc(var(--gutter) + var(--safe-top));
        bottom: calc(var(--gutter) + var(--safe-bottom));
        left: calc(var(--gutter) + env(safe-area-inset-left, 0px));
        height: auto;
        width: min(86vw, 320px);
        z-index: 200;
        transform: translateX(calc(-100% - var(--gutter) * 2 - env(safe-area-inset-left, 0px)));
        transition: transform 320ms var(--ease);
        border-radius: var(--radius-xl-mobile);
        /* The desktop sidebar is a transparent column on the chrome; as
           a slide-over it must be an opaque sheet again. */
        background: var(--bg-canvas);
        border: 1px solid var(--hairline);
        box-shadow: var(--shadow-2);
    }
    .sidebar.open { transform: translateX(0); }
    .sidebar-header {
        padding: 16px 18px 12px;
        min-height: 56px;
    }
    .sidebar-header .sidebar-close { display: inline-flex; }
    .hamburger-btn { display: inline-flex; }

    .message-user .message-bubble { max-width: 88%; }
    .message-assistant .message-bubble { max-width: 96%; }

    .auth-card { padding: 28px 24px; }
    .panel-card { padding: 18px; border-radius: var(--radius); }
    .users-table { font-size: 12px; }
    .users-table th, .users-table td { padding: 10px 8px; }
}

@media (max-width: 480px) {
    .chat-input-row { gap: 8px; }
    /* Keep wrapper outer (42 + 2 border) = send-btn (44) for alignment.
       Right padding clears the four 32px tool buttons + 3×2px gaps
       (134px) + 5px right offset + breathing so text wraps before
       overlapping the icons. */
    #chat-input { min-height: 42px; padding: 10px 146px 10px 16px; font-size: 14.5px; }
    .send-btn { width: 44px; height: 44px; }
    .composer-tools { right: 5px; bottom: 5px; gap: 2px; }
    .composer-tool-btn { width: 32px; height: 32px; }
    .header-title { display: none; }
}

/* ---------- Encryption notice on registration ---------- */
.encryption-notice {
    background: rgba(251, 191, 36, 0.10);
    border: 1px solid rgba(251, 191, 36, 0.28);
    color: var(--text);
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    line-height: 1.55;
    margin-top: 14px;
}
.encryption-notice strong {
    display: block;
    margin-bottom: 4px;
    color: var(--warning);
    font-weight: 600;
}

/* ---------- Skills list (Settings) ---------- */
.skills-grid {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.skill-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    transition: opacity var(--t-fast) var(--ease);
}
.skill-row.is-disabled { opacity: 0.55; }
.skill-body { flex: 1; min-width: 0; }
.skill-name {
    font-size: 14.5px;
    font-weight: 600;
    margin-bottom: 2px;
    color: var(--text);
}
.skill-desc {
    font-size: 13px;
    color: var(--text-2);
    line-height: 1.45;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.skill-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

/* ---------- Reasoning ("Thinking…") block ---------- */
/* Live thinking stream for local reasoning models (LM Studio Gemma/Qwen/
   DeepSeek). Collapsible; muted so it reads as secondary to the answer. */
.reasoning-block {
    margin: 0 0 10px;
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm, 10px);
    background: var(--glass);
    overflow: hidden;
}
.reasoning-summary {
    cursor: pointer;
    list-style: none;
    padding: 7px 12px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-3);
    user-select: none;
}
.reasoning-summary::-webkit-details-marker { display: none; }
.reasoning-summary::before {
    content: '▸';
    display: inline-block;
    margin-right: 7px;
    transition: transform var(--t-fast, 0.15s) var(--ease, ease);
}
.reasoning-block[open] .reasoning-summary::before { transform: rotate(90deg); }
.reasoning-content {
    padding: 0 12px 10px;
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-3);
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 320px;
    overflow-y: auto;
}

/* ---------- Tool-call cards (chat body) ---------- */
.text-segment + .text-segment { margin-top: 8px; }
.text-segment + .tool-call-card,
.tool-call-card + .text-segment { margin-top: 10px; }
.tool-call-card {
    margin: 6px 0;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    font-size: 13px;
}
.tool-call-card.is-pending {
    border-color: var(--hairline);
    background: rgba(125, 108, 245, 0.06);
}
.tool-call-card.is-success { border-left: 3px solid var(--success, #34d399); }
.tool-call-card.is-error   { border-left: 3px solid var(--danger); }
.tool-call-header {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-2);
}
.tool-call-header svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-3); }
.tool-call-name {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--text);
}
.tool-call-slug {
    color: var(--text-3);
    font-weight: 500;
}
.tool-call-slug::after {
    content: '/';
    margin: 0 2px;
    color: var(--text-3);
}
.tool-call-tool { font-weight: 600; }
.tool-call-status {
    font-size: 12px;
    color: var(--text-3);
    flex-shrink: 0;
}
.tool-call-status .thinking-dots { transform: scale(0.7); }
.tool-call-ok { color: var(--success, #34d399); font-weight: 600; }
.tool-call-error { color: var(--danger); font-weight: 600; }
.tool-call-detail {
    margin-top: 6px;
    border-top: 1px solid var(--hairline-soft);
    padding-top: 6px;
    font-size: 12px;
}
.tool-call-detail summary {
    cursor: pointer;
    color: var(--text-3);
    font-weight: 500;
    user-select: none;
    padding: 2px 0;
}
.tool-call-detail summary:hover { color: var(--text-2); }
.tool-call-detail pre {
    margin: 6px 0 2px;
    padding: 8px 10px;
    background: var(--bg-deep, rgba(0,0,0,0.18));
    border-radius: 6px;
    font-size: 11.5px;
    line-height: 1.45;
    max-height: 240px;
    overflow: auto;
    white-space: pre-wrap;
    word-break: break-word;
}

/* ---------- MCP plugins list (Settings) ---------- */
.plugins-grid {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.plugin-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 14px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    transition: opacity var(--t-fast) var(--ease);
}
.plugin-row.is-disabled { opacity: 0.55; }
.plugin-body {
    flex: 1;
    min-width: 0;
}
.plugin-name-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 2px;
}
.plugin-name {
    font-size: 14.5px;
    font-weight: 600;
    color: var(--text);
}
.plugin-flag {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-3);
    background: var(--hairline-soft);
    padding: 2px 6px;
    border-radius: 999px;
    font-weight: 600;
}
.plugin-flag.warn {
    color: var(--warning, #fbbf24);
    background: rgba(251, 191, 36, 0.12);
}
.plugin-meta {
    font-size: 12.5px;
    color: var(--text-2);
    line-height: 1.4;
    word-break: break-all;
    font-family: var(--font-mono);
}
.plugin-status-row {
    margin-top: 6px;
    font-size: 12px;
}
.plugin-status {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--text-3);
}
.plugin-status svg { width: 13px; height: 13px; }
.plugin-status.ok { color: var(--success, #34d399); }
.plugin-status.err { color: var(--danger); }
.plugin-status.pending { color: var(--text-3); font-style: italic; }
.plugin-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
    flex-wrap: wrap;
    justify-content: flex-end;
}
.plugin-oauth-row {
    margin-top: 8px;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
@media (max-width: 600px) {
    .plugin-row { flex-direction: column; }
    .plugin-actions { justify-content: flex-start; }
}

/* ---------- Chat header controls (project picker + favourite) ---------- */
.chat-header-controls {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: 4px;
}
.chat-header-controls:empty { display: none; }
.header-project-select {
    height: 32px;
    border-radius: var(--radius-sm);
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    color: var(--text);
    font-size: 13px;
    padding: 0 26px 0 10px;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238a8a96' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    background-repeat: no-repeat;
    background-position: right 8px center;
    cursor: pointer;
    max-width: 180px;
    text-overflow: ellipsis;
}
.header-project-select:hover { border-color: var(--hairline); }
.header-fav-btn {
    width: 32px; height: 32px;
    color: var(--text-3);
}
.header-fav-btn svg { width: 18px; height: 18px; }
.header-fav-btn.is-active { color: var(--accent-2); }

/* The cog button replaces the inline model/project selectors below the
   mobile breakpoint — keeping the header from squashing those down to
   nothing while still leaving room for the favourite toggle. */
.header-cog-btn {
    display: none;
    width: 32px; height: 32px;
    color: var(--text-2);
}
.header-cog-btn svg { width: 18px; height: 18px; }

@media (max-width: 600px) {
    .model-selector,
    .header-project-select { display: none; }
    .header-cog-btn { display: inline-flex; }
    /* flex-grow runs BEFORE auto-margin in the flex algorithm, so the
       trailing .header-spacer's flex:1 was eating all the free space
       and pinning the controls to the left. Hide just the spacer that
       directly follows the chat header controls (other pages' spacers
       are untouched), then margin-left:auto on the controls works. */
    .chat-header-controls + .header-spacer { display: none; }
    .chat-header-controls { margin-left: auto; }
    /* Homepage / empty state (no loaded chat) has an uncrowded header, so
       show the model selector inline on mobile too — it's the only place a
       first message's model can be chosen before the chat is created (the
       send-from-empty path reads #model-selector.value). A loaded chat drops
       .chat-home, re-hiding it and exposing the picker via the cog modal.
       Scoped to .main-header on purpose: other views (and the Document
       editor's .doc-chat-head selector) reuse .main-content via innerHTML
       without clearing classes, so .chat-home can linger — but only the chat
       shell puts a .model-selector inside .main-header, so nothing else is
       affected even when the marker leaks. */
    .main-content.chat-home .main-header .model-selector {
        display: inline-block;
        /* Shrink-to-fit so the reachability badge (shown for a local model)
           keeps its own line instead of squashing "Reachable" onto two.
           min-width:0 lets this flex item shrink below the <select>'s
           intrinsic width; the badge is flex-shrink:0, so the selector is
           what gives up space and ellipsizes. flex-grow:0 keeps it from
           stretching full-width when no badge is present. */
        flex: 0 1 auto;
        min-width: 0;
        max-width: calc(100vw - 96px);
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
}

/* ---------- Sidebar search ---------- */
.sidebar-search {
    position: relative;
    margin: 6px 10px 4px;
}
.sidebar-search-icon {
    position: absolute;
    left: 10px; top: 50%; transform: translateY(-50%);
    color: var(--text-3);
    pointer-events: none;
    display: flex;
}
.sidebar-search-icon svg { width: 14px; height: 14px; }
.sidebar-search-input {
    width: 100%;
    height: 34px;
    padding: 0 30px 0 32px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--hairline-soft);
    background: var(--glass);
    color: var(--text);
    font-size: 13.5px;
    font-family: inherit;
    outline: none;
    transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.sidebar-search-input::placeholder { color: var(--text-3); }
.sidebar-search-input:focus {
    border-color: var(--accent-soft);
    background: var(--glass-strong);
}
.sidebar-search-clear {
    position: absolute;
    right: 6px; top: 50%; transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-3);
    cursor: pointer;
    padding: 4px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.sidebar-search-clear:hover { background: var(--glass-strong); color: var(--text); }
.sidebar-search-clear svg { width: 12px; height: 12px; }

.chat-list-item-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}
.chat-snippet {
    font-size: 12px;
    color: var(--text-3);
    line-height: 1.4;
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
mark {
    background: var(--accent-soft);
    color: var(--text);
    border-radius: 3px;
    padding: 0 2px;
}

/* ---------- Offline pill (sidebar header) ---------- */
.offline-pill {
    display: none;
    align-items: center;
    gap: 5px;
    padding: 3px 8px 3px 6px;
    border-radius: var(--radius-pill);
    background: var(--danger-soft);
    color: var(--danger);
    font-size: 11px;
    font-weight: 600;
    border: 1px solid color-mix(in srgb, var(--danger) 26%, transparent);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.offline-pill svg { width: 12px; height: 12px; }
body.offline .offline-pill { display: inline-flex; }

/* ---------- Sidebar favourites ---------- */
.favorites-section {
    padding: 6px 10px 0;
}
.sidebar-section-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
    padding: 8px 10px 6px;
    font-weight: 600;
}
.favorites-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-bottom: 8px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--hairline-soft);
}
.favorite-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--t-fast) var(--ease);
    color: var(--text-2);
    font-size: 13.5px;
}
.favorite-item:hover { background: var(--glass-strong); color: var(--text); }
.favorite-item.active {
    background: var(--accent-soft);
    color: var(--text);
}
.favorite-icon {
    flex-shrink: 0;
    width: 14px; height: 14px;
    color: var(--accent-2);
    display: grid; place-items: center;
}
.favorite-icon svg { width: 14px; height: 14px; }
.favorite-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
}

/* ---------- Projects ---------- */
.project-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
}
.project-card {
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius);
    padding: 16px;
    cursor: pointer;
    transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.project-card:hover {
    transform: translateY(-1px);
    border-color: var(--hairline);
    background: var(--glass-strong);
}
.project-card.is-favorite { border-color: var(--accent-soft); }
.project-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}
.project-card-name {
    font-size: 16px;
    font-weight: 600;
    margin: 0;
    line-height: 1.3;
    letter-spacing: -0.01em;
}
.project-card-desc {
    font-size: 13.5px;
    color: var(--text-2);
    line-height: 1.5;
    margin: 0 0 10px;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.project-card-meta {
    font-size: 12px;
    color: var(--text-3);
}
.project-fav-btn {
    width: 32px; height: 32px;
    color: var(--text-3);
    flex-shrink: 0;
}
.project-fav-btn svg { width: 18px; height: 18px; }
.project-card.is-favorite .project-fav-btn,
.is-favorite .project-fav-btn {
    color: var(--accent-2);
}
.project-instructions { margin-top: 16px; }
.project-instructions h3 {
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-3);
    margin: 0 0 8px;
    font-weight: 600;
}
.project-instructions-text {
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.6;
    color: var(--text);
    white-space: pre-wrap;
    word-break: break-word;
    margin: 0;
}
.project-chat-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.project-chat-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--t-fast) var(--ease);
    gap: 12px;
}
.project-chat-item:hover { background: var(--glass-strong); }
.project-chat-title {
    font-size: 14px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.project-chat-meta {
    font-size: 12px;
    color: var(--text-3);
    flex-shrink: 0;
}
.dialog-wide { max-width: 560px; }

/* ---------- Settings tabs (in-shell, sit under the main-header) ---------- */
.settings-tabs {
    display: flex;
    gap: 2px;
    padding: 0 16px;
    overflow-x: auto;
    border-bottom: 1px solid var(--hairline-soft);
    background: var(--glass);
    flex-shrink: 0;
    scrollbar-width: none;
}
.settings-tabs::-webkit-scrollbar { display: none; }
.settings-tab {
    background: transparent;
    border: none;
    color: var(--text-2);
    font-size: 14px;
    font-weight: 500;
    padding: 12px 16px;
    cursor: pointer;
    position: relative;
    white-space: nowrap;
    font-family: inherit;
    transition: color var(--t-fast) var(--ease);
}
.settings-tab:hover { color: var(--text); }
.settings-tab.active { color: var(--text); }
.settings-tab.active::after {
    content: '';
    position: absolute;
    left: 14px; right: 14px;
    bottom: -1px;
    height: 2px;
    background: var(--accent);
    border-radius: 2px 2px 0 0;
}
@media (max-width: 600px) {
    .settings-tabs { padding: 0 8px; }
    .settings-tab { padding: 11px 12px; font-size: 13.5px; }
}

/* ---------- Profile / avatar builder (Settings) ---------- */
.avatar-builders {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 18px;
    margin-top: 12px;
}
.avatar-builder {
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius);
    padding: 16px;
}
.avatar-builder-label {
    font-size: 13px;
    color: var(--text-2);
    margin-bottom: 10px;
    font-weight: 500;
}
.avatar-section-label {
    font-size: 11px;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: 14px 0 6px;
    font-weight: 600;
}
.avatar-preview {
    width: 64px; height: 64px;
    border-radius: var(--radius-pill);
    display: grid; place-items: center;
    font-size: 32px; line-height: 1;
    margin: 4px auto 4px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.22);
}
.color-grid {
    display: grid;
    /* The 34px floor keeps swatches selectable when the grid sits in a
       flex row (Admin → Brand accent presets) — bare 1fr tracks have a
       zero min-content there and collapse to invisible dots. In block
       contexts (avatar builder card) the 1fr still stretches to fill. */
    grid-template-columns: repeat(6, minmax(34px, 1fr));
    gap: 6px;
}
.color-swatch {
    aspect-ratio: 1 / 1;
    border-radius: var(--radius-pill);
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.color-swatch:hover { transform: scale(1.08); }
.color-swatch.selected {
    border-color: var(--text);
    box-shadow: 0 0 0 2px var(--bg-base), 0 0 0 4px var(--text);
}
.emoji-grid {
    display: grid;
    /* auto-fit picks however many ~38px cells fit the column width;
       narrower avatar-builder cards get fewer columns + more rows
       instead of squeezing emojis into too-tight cells. */
    grid-template-columns: repeat(auto-fit, minmax(38px, 1fr));
    gap: 6px;
}
.emoji-swatch {
    aspect-ratio: 1 / 1;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0;
    color: inherit;
    /* Center the glyph and give the emoji breathing room inside the
       cell so wide emojis (flags, ZWJ sequences) don't clip the edges. */
    display: grid;
    place-items: center;
    overflow: hidden;
    transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.emoji-swatch:hover {
    background: var(--glass-strong);
    transform: scale(1.08);
}
.emoji-swatch.selected {
    background: var(--accent-soft);
    border-color: var(--accent);
}

/* ---------- Theme picker (Settings → Profile) ---------- */
.theme-picker-section {
    margin-top: 18px;
    padding-top: 18px;
    border-top: 1px solid var(--hairline-soft);
}
.theme-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
}
.theme-card {
    display: flex;
    flex-direction: column;
    text-align: left;
    padding: 10px;
    background: var(--glass);
    border: 1px solid var(--hairline-soft);
    border-radius: var(--radius);
    cursor: pointer;
    transition: transform var(--t-fast) var(--ease),
                border-color var(--t-fast) var(--ease),
                background var(--t-fast) var(--ease);
}
.theme-card:hover { transform: translateY(-1px); background: var(--glass-strong); }
.theme-card.selected {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-soft);
}
.theme-card-preview {
    position: relative;
    display: block;
    height: 64px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    margin-bottom: 8px;
    border: 1px solid var(--hairline-soft);
}
.theme-card-surface {
    position: absolute;
    inset: 8px 8px 28px 8px;
    border-radius: 6px;
}
.theme-card-bubble {
    position: absolute;
    height: 12px;
    border-radius: 6px;
}
.theme-card-bubble-ai {
    left: 14px;
    bottom: 10px;
    width: 38%;
}
.theme-card-bubble-user {
    right: 14px;
    bottom: 10px;
    width: 30%;
}
.theme-card-dot {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 10px;
    height: 10px;
    border-radius: var(--radius-pill);
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
}
.theme-card-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}
.theme-card-blurb {
    font-size: 11.5px;
    color: var(--text-3);
    margin-top: 2px;
    line-height: 1.4;
}

/* ---------- Utility ---------- */
.hidden { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); }
.text-center { text-align: center; }
.mt-8  { margin-top: 8px; }
.mt-12 { margin-top: 12px; }
.mt-16 { margin-top: 16px; }
.mt-20 { margin-top: 20px; }
.mb-8  { margin-bottom: 8px; }
.mb-16 { margin-bottom: 16px; }
.gap-8 { gap: 8px; }
.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }

/* ============================================================
   Shared chats (group conversations)
   ============================================================ */

/* Another member's message: left-aligned like the assistant, but keeps
   the user-bubble treatment plus a name label. */
.message-peer { justify-content: flex-start; }
.message-peer .message-bubble { border-bottom-left-radius: 6px; }
.message-sender-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-3);
    margin-bottom: 4px;
}

.chat-shared-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--accent);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    vertical-align: 1px;
    white-space: nowrap;
}

.chat-list-section-label {
    padding: 10px 12px 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-3);
}

.header-share-btn.is-active { color: var(--accent); }

/* ============================================================
   Branding — admin logo / wordmark
   ============================================================ */

.sidebar-brand-logo {
    max-height: 40px;
    max-width: 200px;
    display: block;
    /* The desktop sidebar-header collapses to zero when empty (no-logo
       workspaces show nothing there) — so the logo brings its own
       breathing room instead of relying on header padding. */
    margin: 14px 0 4px;
}
.auth-brand { display: flex; align-items: center; margin-bottom: 18px; }
.auth-brand-name {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text-2);
}
.auth-brand-logo {
    max-height: 32px;
    max-width: 200px;
    display: block;
}

/* =========================================================
   Recovered component sections (media popovers, slash menu,
   composer Tools menu, context markers, media modals,
   attachment rendering, code-block actions, Document
   Collaborator, project files, usage report).
   Restored from the pre-retheme stylesheet — the original
   line-range theme deletion took these with it. Order matters:
   they sat at the end of the old file, so appending here keeps
   the original cascade (e.g. the full attachment rendering
   rules override the early attachment-chip block above).
   ========================================================= */

/* =========================================================
   Media generation — composer popover, modals, attachments
   ========================================================= */

/* Popover anchored to the composer's media button. Positioned in JS
   so it always sits above (or below if there's no room) the trigger. */
.media-picker {
    position: fixed;
    z-index: 1000;
    min-width: 260px;
    max-width: 320px;
    background: var(--bg-panel, var(--bg-base));
    border: 1px solid var(--hairline);
    border-radius: 14px;
    box-shadow: var(--shadow-float);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    animation: media-picker-in 120ms ease-out;
}
@keyframes media-picker-in {
    from { opacity: 0; transform: translateY(4px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.media-picker-arrow { display: none; }
.media-picker-item {
    display: flex;
    align-items: center;
    gap: 10px;
    /* Buttons default to width:auto and don't stretch in column flex
       parents in every browser despite the parent's align-items:stretch,
       which is why the rows previously sat at content-width with a gap
       on the right. Force the full container width here. */
    width: 100%;
    box-sizing: border-box;
    padding: 9px 10px;
    border: none;
    border-radius: 10px;
    background: transparent;
    color: var(--text-1, var(--text));
    cursor: pointer;
    text-align: left;
    font: inherit;
    transition: background 120ms ease;
}
.media-picker-item:hover,
.media-picker-item:focus-visible {
    background: var(--glass);
    outline: none;
}
.media-picker-item.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.media-picker-item.is-disabled:hover { background: transparent; }
.media-picker-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--glass);
    color: var(--text-1, var(--text));
    flex-shrink: 0;
}
.media-picker-icon svg { width: 16px; height: 16px; }
.media-picker-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.media-picker-label {
    font-size: 13.5px;
    font-weight: 500;
}
.media-picker-sub {
    font-size: 11.5px;
    color: var(--text-3);
}

/* Composer slash-command autocomplete — reuses .media-picker chrome, adds a
   scrollable list, a keyboard-highlight state, and a monospace command name.
   Anchored above the composer by Chat._positionSlashMenu. */
.slash-menu {
    max-height: 340px;
    overflow-y: auto;
    min-width: 300px;
    max-width: 440px;
}
.slash-menu .media-picker-item.is-highlighted {
    background: var(--glass-strong);
}
.slash-cmd-name {
    font-family: var(--font-mono);
    font-size: 0.92em;
}
.slash-help-alias {
    font-family: var(--font-mono);
    font-size: 0.82em;
    color: var(--text-3);
    margin-left: 6px;
}
.slash-help-dialog { max-width: 520px; width: 100%; }
.slash-help-list {
    margin-top: 12px;
    max-height: 52vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.slash-help-row {
    display: grid;
    grid-template-columns: minmax(120px, 40%) 1fr;
    gap: 12px;
    align-items: baseline;
    padding: 7px 8px;
    border-radius: 8px;
}
.slash-help-row:hover { background: var(--glass); }
.slash-help-desc { color: var(--text-3); font-size: 13px; }

/* Composer Tools menu — reuses .media-picker chrome. Toggle rows show an
   active state + a check on the right; a separator splits the on/off
   toggles from the one-shot context actions. */
.tools-picker { min-width: 280px; }
.tools-toggle { position: relative; }
.tools-toggle.is-on .media-picker-icon {
    background: var(--accent);
    color: #fff;
}
.tools-toggle-state {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    color: var(--accent);
    flex-shrink: 0;
}
.tools-toggle-state svg { width: 16px; height: 16px; }
.tools-picker-sep {
    height: 1px;
    margin: 4px 8px;
    background: var(--hairline);
}

/* Reasoning-effort segmented control inside the Tools popover. Only
   rendered for reasoning-capable models. Icon + label on one row, the
   Auto/Low/Medium/High segments wrap beneath on narrow widths. */
.tools-reasoning {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 12px;
}
.tools-reasoning-label {
    font-size: 13px;
    font-weight: 500;
    color: var(--text);
}
.tools-reasoning-seg {
    display: inline-flex;
    margin-left: auto;
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm, 8px);
    overflow: hidden;
}
.tools-reasoning-opt {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--text-2);
    font: inherit;
    font-size: 12px;
    padding: 4px 9px;
    cursor: pointer;
    border-left: 1px solid var(--hairline);
}
.tools-reasoning-opt:first-child { border-left: 0; }
.tools-reasoning-opt:hover { background: var(--glass); color: var(--text); }
.tools-reasoning-opt.is-on {
    background: var(--accent);
    color: #fff;
}

/* Context-boundary dividers (Condense / Clear Session Context). A quiet
   horizontal rule with a centred label so the user can see exactly where
   the AI's working context begins. */
.context-marker {
    margin: 18px 0;
    font-size: 12px;
    color: var(--text-3);
}
.context-marker-line {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    text-align: center;
}
.context-marker-line svg { width: 14px; height: 14px; flex-shrink: 0; }
/* Flanking rules drawn with pseudo-elements so the label sits centred. */
.context-marker-line::before,
.context-marker-line::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--hairline);
    max-width: 120px;
}
.context-marker-condense details > summary {
    list-style: none;
    cursor: pointer;
}
.context-marker-condense details > summary::-webkit-details-marker { display: none; }
.context-marker-condense summary:hover { color: var(--text); }
.context-marker-summary {
    margin: 10px auto 0;
    max-width: 640px;
    padding: 12px 14px;
    background: var(--glass);
    border: 1px solid var(--hairline);
    border-radius: 12px;
    font-size: 13px;
    color: var(--text);
    line-height: 1.5;
}
.context-marker-summary > :first-child { margin-top: 0; }
.context-marker-summary > :last-child { margin-bottom: 0; }
/* Lists need explicit indent — the global reset zeroes ul/ol padding, which
   pushes the bullet markers outside the summary box. Match the in-bubble
   list indent so they sit inside. */
.context-marker-summary ul,
.context-marker-summary ol { margin: 6px 0; padding-left: 22px; }
.context-marker-summary li { margin: 2px 0; }

/* Transient "Condensing…" row shown while the summary is generated (the
   blocking/cloud call or the in-browser local summary can take a while). */
.context-marker-progress .context-marker-line { color: var(--text-3); }
.context-marker-progress .thinking-dots {
    padding: 0;
    background: none;
    border: none;
    gap: 4px;
}
.context-marker-progress .thinking-dots span { width: 6px; height: 6px; }

/* Modal — reuses .dialog-overlay/.dialog structure with media-specific
   spacing/layout tweaks. */
.media-modal-overlay .media-modal {
    max-width: 520px;
    width: 92vw;
}
.media-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
}
.media-modal-header h3 { margin: 0; }
.media-modal-header .icon-btn {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: transparent;
    border: none;
    color: var(--text-2, var(--text-3));
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.media-modal-header .icon-btn:hover { background: var(--glass); color: var(--text-1, var(--text)); }
.media-form .form-input { width: 100%; }
.media-label {
    display: block;
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-2, var(--text-3));
    margin: 0 0 6px;
}
.media-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin-top: 10px;
}
.media-field { display: flex; flex-direction: column; }
.media-option-fields { margin-top: 4px; }
.media-modal-actions { margin-top: 16px; }
.media-hint {
    font-size: 11.5px;
    color: var(--text-3);
    margin: 6px 0 0;
}
.media-status { min-height: 1em; }

/* =========================================================
   Attachment rendering — image / audio / video / file badge
   ========================================================= */

.message-attachments {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 10px;
    /* Belt-and-braces against intrinsic-content overflow on mobile:
       cap to parent width, release the flex min-width floor, and any
       last-resort overflow gets clipped instead of forcing horizontal
       scroll on the whole viewport. */
    max-width: 100%;
    min-width: 0;
    overflow: hidden;
}
.attachment-figure {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-width: min(480px, 100%);
    min-width: 0;
}
.attachment-figure img.attachment-preview {
    /* `max-width: 100%` is the standard responsive-image clamp; pairing
       it with `width: auto; height: auto` keeps aspect ratio intact at
       any container width. Together with the bubble's min-width:0, a
       1024×1024 generated image now scales down cleanly on a 320px
       viewport instead of pushing the bubble out. */
    max-width: 100%;
    width: auto;
    height: auto;
    max-height: 520px;
    border-radius: 10px;
    border: 1px solid var(--hairline);
    display: block;
}
.attachment-figure audio,
.attachment-figure video {
    width: 100%;
    max-width: 100%;
    border-radius: 10px;
    display: block;
}
.attachment-figure video {
    max-height: 480px;
    background: #000;
}
.attachment-caption {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-3);
}
.attachment-caption span:first-of-type:not(.attachment-icon),
.attachment-caption > span:not(.attachment-icon) {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.attachment-icon {
    display: inline-flex;
    align-items: center;
    color: var(--text-3);
    flex-shrink: 0;
}
.attachment-icon svg { width: 14px; height: 14px; }
.attachment-download {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 7px;
    color: var(--text-3);
    text-decoration: none;
    transition: background 120ms ease, color 120ms ease;
}
.attachment-download:hover {
    background: var(--glass);
    color: var(--text-1, var(--text));
}
.attachment-download svg { width: 14px; height: 14px; }

/* The fallback badge for non-image / non-audio / non-video files — PDFs,
   code dumps, anything else. Whole row is clickable for download. */
.attachment-badge.attachment-file {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border: 1px solid var(--hairline);
    border-radius: 10px;
    color: var(--text-1, var(--text));
    text-decoration: none;
    font-size: 13px;
    background: var(--glass);
    max-width: fit-content;
    transition: background 120ms ease;
}
.attachment-badge.attachment-file:hover {
    background: var(--hairline-soft, var(--glass));
}
.attachment-badge.attachment-file svg:first-child { width: 16px; height: 16px; flex-shrink: 0; }
.attachment-badge.attachment-file svg:last-child  { width: 14px; height: 14px; color: var(--text-3); margin-left: auto; }
.attachment-badge.attachment-file > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 320px;
}

/* Floating popover that lists each attachment when a message has more
   than one — pick which file to download. */
.attachment-download-menu {
    position: fixed;
    z-index: 1100;
    min-width: 220px;
    max-width: 320px;
    background: var(--bg-panel, var(--bg-base));
    border: 1px solid var(--hairline);
    border-radius: 12px;
    box-shadow: var(--shadow-float);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.attachment-download-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: 8px;
    color: var(--text-1, var(--text));
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    text-align: left;
}
.attachment-download-item:hover { background: var(--glass); }
.attachment-download-item svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-3); }
.attachment-download-item span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* =========================================================
   Code block — copy + download affordances
   ========================================================= */

.code-block-wrap {
    position: relative;
    margin: 10px 0;
}
.code-block-wrap pre {
    margin: 0;
}
.code-block-actions {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    gap: 4px;
    opacity: 0;
    transition: opacity 120ms ease;
}
.code-block-wrap:hover .code-block-actions,
.code-block-wrap:focus-within .code-block-actions {
    opacity: 1;
}
.code-block-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 7px;
    background: var(--bg-panel, var(--bg-base));
    border: 1px solid var(--hairline);
    color: var(--text-2, var(--text-3));
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}
.code-block-btn:hover {
    background: var(--glass);
    color: var(--text-1, var(--text));
}
.code-block-btn svg { width: 14px; height: 14px; }

/* On touch devices the hover gate means the buttons are invisible —
   always show them there. */
@media (hover: none) {
    .code-block-actions { opacity: 1; }
}


/* =========================================================
   Document Collaborator
   List page (#documents), the WYSIWYG editor (#document?id=N)
   with its chat sidebar, wiki autocomplete, export menu, chips.
   ========================================================= */

/* ---- List page ---- */
.empty-panel {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; padding: 60px 20px; color: var(--text-3); gap: 6px;
}
.empty-panel .empty-icon { width: 44px; height: 44px; opacity: .5; margin-bottom: 6px; }
.empty-panel h3 { color: var(--text); font-size: 17px; }
.doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; padding: 16px; }
.doc-card {
    position: relative; display: flex; align-items: center; gap: 12px;
    padding: 14px; border-radius: var(--radius); border: 1px solid var(--hairline);
    background: var(--glass); cursor: pointer; transition: border-color .15s, transform .15s;
}
.doc-card:hover { border-color: var(--accent); transform: translateY(-1px); }
.doc-card-icon { width: 30px; height: 30px; color: var(--accent); flex: 0 0 auto; }
.doc-card-icon svg { width: 30px; height: 30px; }
.doc-card-body { min-width: 0; flex: 1; }
.doc-card-title { font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.doc-card-meta { font-size: 12px; color: var(--text-3); margin-top: 3px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.doc-badge { font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 99px; background: var(--accent-soft, rgba(125,108,245,.18)); color: var(--accent); }
.doc-badge-ai { background: rgba(80,200,120,.18); color: #36b46a; }
.doc-card-del { opacity: 0; flex: 0 0 auto; }
.doc-card:hover .doc-card-del { opacity: .7; }
.doc-card-del:hover { opacity: 1; color: var(--danger, #e5484d); }

/* ---- Editor shell ----
   The editor is a full-bleed takeover. #app is normally a padded flex row
   (sidebar + main) with overflow:hidden — leaving the editor as a flex
   child means its default min-width:auto refuses to shrink below the Toast
   editor's intrinsic width, so it overflows and gets CLIPPED instead of
   reflowing (this is why it looked "not responsive"). `app-doc-mode`
   (added by documents.js, removed by renderChatShell) strips that chrome
   so the view fills #app and its own responsive rules can take over. */
#app.app-doc-mode { display: block; padding: 0; gap: 0; }
.doc-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: 10px; color: var(--text-3); }
.doc-editor-view { display: flex; flex-direction: column; width: 100%; height: 100%; min-width: 0; background: var(--bg-base); }
.doc-editor-header {
    display: flex; align-items: center; gap: 8px; padding: 8px 12px;
    border-bottom: 1px solid var(--hairline); background: var(--bg-elevated, var(--glass)); flex: 0 0 auto;
}
.doc-title-input {
    flex: 1; min-width: 60px; border: none; background: transparent; color: var(--text);
    font-size: 17px; font-weight: 600; padding: 6px 8px; border-radius: 8px; outline: none;
}
.doc-title-input:focus { background: var(--glass); }
.doc-header-tools { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.doc-mode-toggle { display: inline-flex; border: 1px solid var(--hairline); border-radius: 8px; overflow: hidden; }
.doc-mode-btn { border: none; background: transparent; color: var(--text-3); font-size: 12px; font-weight: 600; padding: 5px 10px; cursor: pointer; }
.doc-mode-btn.is-active { background: var(--accent); color: #fff; }
.doc-font-select { border: 1px solid var(--hairline); background: var(--glass); color: var(--text); border-radius: 8px; padding: 5px 8px; font-size: 12px; max-width: 150px; }
.doc-save-state { font-size: 11px; color: var(--text-3); min-width: 56px; text-align: right; }
.doc-save-error { color: var(--danger, #e5484d); }

.doc-editor-body { flex: 1; display: flex; min-height: 0; min-width: 0; }
.doc-editor-main { flex: 1; min-width: 0; overflow: auto; background: #fff; }
/* Toast UI is light by default; pin the writing surface to paper-white in
   every theme so a document reads like a document, not app chrome. */
.doc-editor-main .toastui-editor-defaultUI { border: none; height: 100%; }
.doc-editor-main .toastui-editor-contents { font-size: 16px; }

/* Compact the (otherwise chunky 45px / 32px-button / 25px-pad) Toast
   toolbar, and let it scroll horizontally instead of overflowing on
   narrow viewports — the default toolbar is wider than a phone screen. */
.doc-editor-main .toastui-editor-defaultUI-toolbar {
    height: auto; min-height: 38px; padding: 2px 6px;
    flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
}
.doc-editor-main .toastui-editor-toolbar { height: auto; }
.doc-editor-main .toastui-editor-defaultUI-toolbar button { height: 28px; width: 28px; margin: 4px 2px; }
.doc-editor-main .toastui-editor-toolbar-group { flex: 0 0 auto; }
/* Hide the thin group divider's big margins so groups pack tighter. */
.doc-editor-main .toastui-editor-toolbar-divider { margin: 8px 3px; }

.doc-chat-sidebar {
    flex: 0 0 340px; width: 340px; display: flex; flex-direction: column;
    border-left: 1px solid var(--hairline); background: var(--bg-base); min-height: 0;
}
.doc-chat-shell { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.doc-chat-head { padding: 8px 10px; border-bottom: 1px solid var(--hairline); display: flex; align-items: center; gap: 8px; }
.doc-chat-head .model-selector { flex: 1; min-width: 0; max-width: 100%; }
/* The close button only matters when the sidebar is an overlay (it covers
   the header toggle); on the desktop two-pane layout the sidebar is always
   present so there's nothing to close. Shown in the ≤1024px block below. */
.doc-chat-close { display: none; flex: 0 0 auto; margin-left: auto; }
.doc-chat-sidebar #chat-input::placeholder { font-size: 13px; opacity: .7; }
/* The main composer reserves ~156px of right padding for its absolutely-
   positioned icon cluster — fine at 820px, but in the narrow sidebar it
   squeezes the text into a sliver. Stack instead: full-width textarea on
   top, the tool icons in their own row beneath. */
.doc-chat-sidebar .chat-input-wrapper { display: flex; flex-direction: column; }
.doc-chat-sidebar #chat-input { padding: 10px 14px; min-height: 40px; font-size: 14px; }
.doc-chat-sidebar .composer-tools {
    position: static; right: auto; bottom: auto;
    padding: 0 6px 6px; gap: 4px; justify-content: flex-start;
}
.doc-chat-container { flex: 1; overflow-y: auto; padding: 12px; }
/* Beat the iOS-PWA inline override in index.php (#chat-input-area.chat-input-area,
   position:fixed) so the sidebar composer stays inside the pane on mobile —
   needs an id in the selector to out-specify it. */
.doc-chat-sidebar #chat-input-area.chat-input-area {
    position: static !important; margin: 0 !important;
    left: auto !important; right: auto !important; bottom: auto !important;
    border: none !important; border-top: 1px solid var(--hairline) !important;
    border-radius: 0 !important; box-shadow: none !important;
    padding: 8px !important; background: var(--bg-base) !important;
}
.doc-chat-toggle { display: none; }

/* ---- Wiki [[ ]] autocomplete ---- */
.wiki-menu {
    position: fixed; z-index: 9999; display: none; min-width: 220px; max-width: 320px;
    max-height: 260px; overflow-y: auto; background: var(--bg-elevated, var(--bg-base));
    border: 1px solid var(--hairline); border-radius: 10px; box-shadow: var(--shadow-float); padding: 4px;
}
.wiki-item { padding: 7px 10px; border-radius: 7px; cursor: pointer; font-size: 14px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wiki-item.is-active, .wiki-item:hover { background: var(--accent); color: #fff; }
.wiki-menu-empty { padding: 8px 10px; font-size: 12.5px; color: var(--text-3); }
.toastui-editor-contents a[data-wikilink], .doc-editor-main a[href^="#document"] { color: var(--accent); }

/* ---- Export menu ---- */
.doc-export-menu {
    position: fixed; z-index: 9999; background: var(--bg-elevated, var(--bg-base));
    border: 1px solid var(--hairline); border-radius: 10px; box-shadow: var(--shadow-float); padding: 4px; min-width: 220px;
}
.doc-export-menu button {
    display: flex; align-items: center; gap: 10px; width: 100%; border: none; background: transparent;
    color: var(--text); font-size: 14px; padding: 9px 10px; border-radius: 7px; cursor: pointer; text-align: left;
}
.doc-export-menu button:hover { background: var(--glass); }
.doc-export-menu svg { width: 16px; height: 16px; opacity: .7; }

/* ---- Settings modal rows ---- */
.doc-setting-row { display: flex; gap: 10px; align-items: flex-start; padding: 10px 0; border-bottom: 1px solid var(--hairline-soft, var(--hairline)); cursor: pointer; }
.doc-setting-row input[type=checkbox] { margin-top: 3px; flex: 0 0 auto; }
.doc-setting-row small { color: var(--text-3); }

/* ---- Chat-side doc chips / edit notices ---- */
.doc-chip {
    display: inline-flex; align-items: center; gap: 6px; margin: 4px 6px 0 0; padding: 4px 10px;
    border-radius: 99px; font-size: 12px; text-decoration: none; border: 1px solid var(--hairline);
    background: var(--glass); color: var(--text);
}
.doc-chip svg { width: 13px; height: 13px; opacity: .7; }
.doc-chip:hover { border-color: var(--accent); color: var(--accent); }
.doc-chip-created { background: rgba(80,200,120,.14); }
.doc-edit-notice {
    display: flex; align-items: center; gap: 8px; margin-top: 6px; padding: 8px 10px;
    border-radius: 10px; background: var(--glass); border: 1px solid var(--hairline); font-size: 13px; color: var(--text);
}
.doc-edit-notice svg { width: 15px; height: 15px; opacity: .7; flex: 0 0 auto; }
.doc-edit-proposed { border-color: var(--accent); }
.doc-undo-btn { margin-left: auto; border: none; background: transparent; color: var(--accent); font-size: 12px; cursor: pointer; }
/* "Updating the document…" indicator shown while the AI streams a (hidden)
   edit block, so the long edit doesn't read as idle. */
.doc-edit-progress {
    display: flex; align-items: center; gap: 8px; margin-top: 6px;
    font-size: 13px; color: var(--text-3);
}
.doc-edit-progress .thinking-dots { display: inline-flex; }

/* ---- Narrow viewports: chat sidebar becomes a slide-over ----
   Below 1024px the two-pane layout gets cramped, so the editor takes the
   full width and the chat opens as an overlay via the header toggle. */
@media (max-width: 1024px) {
    .doc-chat-toggle { display: inline-flex; }
    .doc-chat-close { display: inline-flex; }
    .doc-chat-sidebar {
        position: fixed; top: 0; right: 0; bottom: 0; width: min(92vw, 380px); flex-basis: auto;
        transform: translateX(100%); transition: transform .25s ease; z-index: 60; box-shadow: var(--shadow-float);
    }
    .doc-editor-view.chat-open .doc-chat-sidebar { transform: translateX(0); }
}

/* ---- Phones / small windows: wrap the header, shrink controls ----
   The formatting tools (mode toggle, font, settings, export) wrap to a
   second row; the title, save state, and chat toggle stay on row one. */
@media (max-width: 680px) {
    .doc-editor-header { flex-wrap: wrap; gap: 6px; padding: 6px 8px; }
    .doc-editor-header > .icon-btn:first-child { order: 1; }
    .doc-title-input { order: 2; flex: 1 1 100px; font-size: 16px; padding: 4px 6px; }
    .doc-save-state { order: 3; min-width: 0; }
    .doc-chat-toggle { order: 4; }
    .doc-header-tools { order: 5; flex: 1 1 100%; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
    .doc-mode-btn { padding: 4px 8px; font-size: 11px; }
    .doc-font-select { flex: 1 1 auto; min-width: 0; max-width: none; }
    /* Tighten the editor's own content gutters on small screens. */
    .doc-editor-main .toastui-editor-contents { font-size: 15px; padding: 12px 14px; }
    .doc-editor-main .toastui-editor-md-container .toastui-editor-md-preview,
    .doc-editor-main .toastui-editor-ww-container { padding: 0; }
}

/* ---- iPad: indent the header past the OS window controls ----
   The editor is full-bleed (app-doc-mode strips #app's --gutter inset), so
   on iPadOS windowed mode the back button + title sit flush under the
   top-left window controls. The main app and the Prompt Builder / Image
   Studio avoid this because they float inset by --gutter. Restore a leading
   inset here on iPad-class devices (any coarse pointer, tablet+ width),
   preferring the OS-reserved safe-area inset when it's reported. Desktop and
   phones are unaffected. */
@media (any-pointer: coarse) and (min-width: 768px) {
    .doc-editor-header { padding-left: max(calc(var(--gutter) + 16px), env(safe-area-inset-left, 0px)); }
}

/* ===== Project files (Settings → Project Settings) =====
   File list with per-file context toggles. Tokens only; mirrors the
   panel-card / doc-chip visual language. */
.project-files-card { margin-top: 14px; }
.pf-rows { display: flex; flex-direction: column; gap: 8px; }
.pf-row {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 12px; flex-wrap: wrap;
    padding: 10px 12px;
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    background: var(--glass);
}
.pf-main { display: flex; align-items: flex-start; gap: 10px; min-width: 0; flex: 1 1 220px; }
.pf-icon svg { width: 18px; height: 18px; color: var(--text-3); flex-shrink: 0; margin-top: 1px; }
.pf-info { min-width: 0; }
.pf-name { font-size: 13px; font-weight: 600; color: var(--text); word-break: break-word; }
.pf-size { font-weight: 400; color: var(--text-3); font-size: 12px; margin-left: 4px; }
.pf-meta { font-size: 12px; color: var(--text-3); margin-top: 3px; }
.pf-hint { font-size: 12px; color: var(--warning); margin-top: 3px; }
.pf-badge {
    display: inline-block; font-size: 11px; font-weight: 600;
    padding: 1px 7px; border-radius: var(--radius-pill);
    border: 1px solid var(--hairline); color: var(--text-2);
}
.pf-badge-ok { color: var(--success); border-color: var(--success); }
.pf-badge-warn { color: var(--warning); border-color: var(--warning); }
.pf-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pf-toggle {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; color: var(--text-2); cursor: pointer; user-select: none;
}
.pf-toggle input { accent-color: var(--accent); cursor: pointer; }

/* ===== Usage tab ===== */
.usage-controls {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin: 14px 0 4px;
}
.usage-controls label { font-size: 13px; color: var(--text-2); }
.usage-controls select { max-width: 220px; }

.usage-summary {
    display: grid; grid-template-columns: repeat(4, 1fr);
    gap: 10px; margin: 18px 0 6px;
}
.usage-stat {
    display: flex; flex-direction: column; gap: 4px;
    padding: 14px; border: 1px solid var(--hairline);
    border-radius: var(--radius-sm); background: var(--glass);
}
.usage-stat-num { font-size: 20px; font-weight: 700; color: var(--text); letter-spacing: -0.01em; }
.usage-stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-3); }

.usage-section-title { font-size: 14px; margin: 26px 0 12px; color: var(--text); }
.usage-empty { margin-top: 16px; }

.usage-bars { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.usage-bar-row { display: grid; grid-template-columns: 120px 1fr auto; align-items: center; gap: 10px; }
.usage-bar-label {
    font-size: 12px; color: var(--text-2);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.usage-bar-track {
    height: 12px; border-radius: var(--radius-pill);
    background: var(--accent-soft); overflow: hidden;
}
.usage-bar-fill {
    display: block; height: 100%; border-radius: var(--radius-pill);
    background: var(--accent);
}
.usage-bar-value { font-size: 12px; color: var(--text-2); font-variant-numeric: tabular-nums; min-width: 56px; text-align: right; }

.usage-table-wrap { overflow-x: auto; margin-bottom: 4px; }
.usage-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.usage-table th, .usage-table td {
    padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--hairline-soft);
    white-space: nowrap;
}
.usage-table th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-3); font-weight: 600; }
.usage-table td { color: var(--text-2); }
.usage-num { text-align: right; font-variant-numeric: tabular-nums; }
.usage-strong { color: var(--text); font-weight: 600; }
.usage-model { max-width: 260px; overflow: hidden; text-overflow: ellipsis; color: var(--text); }

@media (max-width: 620px) {
    .usage-summary { grid-template-columns: repeat(2, 1fr); }
    .usage-bar-row { grid-template-columns: 90px 1fr auto; }
}

/* =========================================================
   Admin → Agents — agent catalogue rows + default badge
   ========================================================= */
.agent-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--glass);
    margin-bottom: 8px;
}
.agent-row-avatar {
    width: 38px; height: 38px;
    border-radius: var(--radius-pill);
    display: grid; place-items: center;
    font-size: 19px; line-height: 1;
    flex-shrink: 0;
    user-select: none;
}
.agent-row-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}
.agent-row-name {
    font-size: 14px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.agent-default-badge {
    font-size: 11px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    color: var(--accent);
}
.agent-row-model {
    font-size: 12px;
    color: var(--text-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.agent-row-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    flex-wrap: wrap;
    justify-content: flex-end;
}
@media (max-width: 560px) {
    .agent-row { flex-wrap: wrap; }
    .agent-row-actions { width: 100%; }
}

/* ===================================================================
   Knowledge (org wiki) — #knowledge view + folder nav pane, and the
   unified search result rows in the sidebar. Reuses the Document
   editor's .wiki-menu / .doc-mode-toggle / .doc-save-state styles.
   =================================================================== */

.kb-body {
    flex: 1;
    display: flex;
    min-height: 0;
    overflow: hidden;
}

/* File navigation pane — hidden by default, revealed via the toggle. */
.kb-nav {
    display: none;
    width: 260px;
    flex-shrink: 0;
    border-right: 1px solid var(--hairline);
    overflow-y: auto;
    padding: 10px 8px;
}
.kb-body.kb-nav-open .kb-nav { display: block; }

.kb-nav-head { padding: 4px 8px 8px; }
.kb-nav-title {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 12px; font-weight: 600; letter-spacing: .02em;
    text-transform: uppercase; color: var(--text-3);
}
.kb-nav-title svg { width: 15px; height: 15px; }

.kb-tree { display: flex; flex-direction: column; gap: 1px; }

.kb-page-row {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 8px; border-radius: var(--radius-sm);
    cursor: pointer; color: var(--text-2); font-size: 13.5px;
    white-space: nowrap; overflow: hidden;
}
.kb-page-row:hover { background: var(--hairline-soft); color: var(--text); }
.kb-page-row.active { background: var(--accent-soft); color: var(--text); }
.kb-page-icon { display: inline-flex; flex-shrink: 0; }
.kb-page-icon svg { width: 15px; height: 15px; }
.kb-page-name { overflow: hidden; text-overflow: ellipsis; }

.kb-folder { margin: 1px 0; }
.kb-folder-head {
    display: flex; align-items: center; gap: 6px;
    padding: 6px 8px; border-radius: var(--radius-sm);
    cursor: pointer; color: var(--text-2); font-size: 13.5px;
}
.kb-folder-head:hover { background: var(--hairline-soft); color: var(--text); }
.kb-folder-head:hover .kb-folder-actions { opacity: 1; }
.kb-folder-chev { display: inline-flex; transition: transform .15s ease; }
.kb-folder-chev svg { width: 14px; height: 14px; }
.kb-folder.collapsed .kb-folder-chev { transform: rotate(-90deg); }
.kb-folder.collapsed .kb-folder-children { display: none; }
.kb-folder-icon { display: inline-flex; flex-shrink: 0; }
.kb-folder-icon svg { width: 15px; height: 15px; }
.kb-folder-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-folder-children { padding-left: 14px; }
.kb-folder-actions { display: inline-flex; gap: 2px; opacity: 0; transition: opacity .12s ease; }
.kb-mini-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border: none; background: transparent;
    color: var(--text-3); border-radius: 5px; cursor: pointer;
}
.kb-mini-btn:hover { background: var(--hairline); color: var(--text); }
.kb-mini-btn svg { width: 13px; height: 13px; }

/* Content area — rendered page (view) or editor (edit). */
.kb-content {
    flex: 1; min-height: 0;
    display: flex; flex-direction: column; overflow: hidden;
}
.kb-page { flex: 1; min-height: 0; overflow-y: auto; padding: 24px; }
.kb-page.kb-editing { overflow: hidden; display: flex; flex-direction: column; padding: 12px 16px; }

/* One centered reading column shared by the title bar and the rendered
   body, so their left/right edges always line up (the Toast viewer won't
   honour a max-width applied to its own container). */
.kb-page-inner { max-width: 820px; margin: 0 auto; width: 100%; }
.kb-page-bar {
    display: flex; align-items: center; gap: 12px;
    margin: 0 0 14px; width: 100%;
}
.kb-page.kb-editing .kb-page-bar { margin-bottom: 10px; }
/* The page title sits above the body as a quiet label — smaller and greyed
   so the 16px body reads as the primary content. */
.kb-page-title { flex: 1; font-size: 18px; font-weight: 600; color: var(--text-2); margin: 0; }
.kb-page-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.kb-title-input {
    flex: 1; font-size: 18px; font-weight: 600; color: var(--text-2);
    background: transparent; border: none; outline: none; padding: 4px 0;
}
.kb-page-render { width: 100%; }

/* Larger, more readable type for wiki pages (view + edit). Toast UI's theme
   defaults to ~14px, smaller than the rest of the app — scope a bump to the
   Knowledge view only so the Document editor is untouched. Selectors carry a
   .kb-content ancestor so they out-specify the vendor rules without !important. */
.kb-content .toastui-editor-contents { font-size: 16px; line-height: 1.75; }
.kb-content .toastui-editor-contents p,
.kb-content .toastui-editor-contents li,
.kb-content .toastui-editor-contents td,
.kb-content .toastui-editor-contents th,
.kb-content .toastui-editor-contents blockquote { font-size: 16px; line-height: 1.75; }
.kb-content .toastui-editor-contents h1 { font-size: 1.9em; }
.kb-content .toastui-editor-contents h2 { font-size: 1.5em; }
.kb-content .toastui-editor-contents h3 { font-size: 1.25em; }
.kb-content .toastui-editor-contents h4 { font-size: 1.1em; }
.kb-content .toastui-editor-contents h5,
.kb-content .toastui-editor-contents h6 { font-size: 1em; }
.kb-content .toastui-editor-contents pre,
.kb-content .toastui-editor-contents code { font-size: 14.5px; }
/* Match the WYSIWYG editing surface to the reading size. */
.kb-content .toastui-editor-ww-container .toastui-editor-contents,
.kb-content .toastui-editor-md-container .toastui-editor { font-size: 16px; line-height: 1.75; }

/* Toast UI theming — the vendored stylesheet is light-only, so in dark mode
   the wiki showed dark text/backgrounds on the dark canvas. App tokens already
   flip per colour-scheme, so a single token-based block themes BOTH modes;
   only the toolbar icon sprite (a fixed-colour image) needs a mode-gated
   invert. Scoped to .kb-content so the Document editor is untouched. */
.kb-content .toastui-editor-contents,
.kb-content .toastui-editor-contents p,
.kb-content .toastui-editor-contents li,
.kb-content .toastui-editor-contents strong,
.kb-content .toastui-editor-contents em,
.kb-content .toastui-editor-contents h1,
.kb-content .toastui-editor-contents h2,
.kb-content .toastui-editor-contents h3,
.kb-content .toastui-editor-contents h4,
.kb-content .toastui-editor-contents h5,
.kb-content .toastui-editor-contents h6 { color: var(--text); }
.kb-content .toastui-editor-contents h1,
.kb-content .toastui-editor-contents h2 { border-bottom-color: var(--hairline); }
.kb-content .toastui-editor-contents a { color: var(--accent-2); }
.kb-content .toastui-editor-contents hr { border-top-color: var(--hairline); }
.kb-content .toastui-editor-contents blockquote {
    color: var(--text-2); border-left-color: var(--hairline-strong);
}
.kb-content .toastui-editor-contents code {
    background: var(--hairline-soft); color: var(--text);
}
.kb-content .toastui-editor-contents pre,
.kb-content .toastui-editor-contents pre code {
    background: var(--hairline-soft); color: var(--text);
}
.kb-content .toastui-editor-contents table th,
.kb-content .toastui-editor-contents table td {
    border-color: var(--hairline); color: var(--text);
}
.kb-content .toastui-editor-contents table th { background: var(--hairline-soft); }
.kb-content .toastui-editor-contents table td { background: transparent; }
.kb-content .toastui-editor-contents ul > li::before { background-color: var(--text-2); }
.kb-content .toastui-editor-contents ol > li::before { color: var(--text-2); }

/* Editor chrome (admin edit mode). */
.kb-content .toastui-editor-defaultUI { border-color: var(--hairline); background: transparent; }
.kb-content .toastui-editor-defaultUI-toolbar { background: var(--hairline-soft); border-color: var(--hairline); }
.kb-content .toastui-editor-toolbar-group { border-color: var(--hairline); }
.kb-content .toastui-editor-toolbar-divider { background-color: var(--hairline); }
.kb-content .toastui-editor-md-container,
.kb-content .toastui-editor-ww-container { background: transparent; }
.kb-content .toastui-editor-md-container .toastui-editor,
.kb-content .toastui-editor-md-preview { background: transparent; color: var(--text); }
.kb-content .toastui-editor-md-splitter { background-color: var(--hairline); }
.kb-content .ProseMirror { color: var(--text); }
.kb-content .toastui-editor-mode-switch { background: var(--hairline-soft); border-top-color: var(--hairline); }
.kb-content .toastui-editor-mode-switch .tab-item {
    color: var(--text-2); background: transparent; border-color: var(--hairline);
}
.kb-content .toastui-editor-mode-switch .tab-item.active {
    color: var(--text); background: var(--bg-canvas);
}

/* Toast portals its right-click menu + popups to <body>, so they can't be
   scoped to .kb-content. Token-based, so they match in both modes. */
.toastui-editor-context-menu,
.toastui-editor-popup,
.toastui-editor-popup-body,
.toastui-editor-dropdown-toolbar {
    background-color: var(--bg-canvas); color: var(--text); border-color: var(--hairline);
}
.toastui-editor-context-menu .menu-button:hover,
.toastui-editor-context-menu .menu-item:hover { background-color: var(--hairline-soft); }
.toastui-editor-popup .toastui-editor-popup-body label,
.toastui-editor-popup .toastui-editor-popup-body span { color: var(--text); }
.toastui-editor-popup input[type="text"], .toastui-editor-popup input[type="file"] {
    background: var(--hairline-soft); color: var(--text); border-color: var(--hairline);
}

/* The toolbar icons are a fixed dark sprite (built for a light background).
   The app renders dark by default, so invert them there; restore under a
   light colour-scheme. Same treatment for the portaled popups' icons. */
.kb-content .toastui-editor-toolbar-icons,
.toastui-editor-context-menu .menu-item .menu-icon {
    filter: invert(0.92) hue-rotate(180deg);
}
@media (prefers-color-scheme: light) {
    .kb-content .toastui-editor-toolbar-icons,
    .toastui-editor-context-menu .menu-item .menu-icon {
        filter: none;
    }
}
#kb-editor { flex: 1; min-height: 0; }

/* Unified search results in the sidebar (type icon + title + type badge). */
.search-result-item {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: var(--radius-sm);
    cursor: pointer; color: var(--text);
}
.search-result-item:hover { background: var(--hairline-soft); }
.search-result-icon {
    display: inline-flex; flex-shrink: 0;
    width: 28px; height: 28px; align-items: center; justify-content: center;
    border-radius: 7px; background: var(--hairline-soft); color: var(--text-2);
}
.search-result-icon svg { width: 15px; height: 15px; }
.search-result-knowledge { color: var(--accent-2); background: var(--accent-soft); }
.search-result-item .chat-list-item-body { flex: 1; min-width: 0; }
.search-result-type {
    flex-shrink: 0; font-size: 10.5px; font-weight: 600; letter-spacing: .03em;
    text-transform: uppercase; color: var(--text-3);
}

@media (max-width: 768px) {
    /* On phones the nav pane overlays the content rather than splitting it. */
    .kb-body.kb-nav-open .kb-nav {
        position: absolute; z-index: 5; top: 0; bottom: 0; left: 0;
        width: 80%; max-width: 300px;
        background: var(--bg-canvas);
        box-shadow: 0 0 0 100vmax rgba(0,0,0,0.35);
    }
    .kb-body { position: relative; }
    .kb-page { padding: 16px; }
    .kb-page-title { font-size: 22px; }
}
