/* Chat — shell, sidebar, header, thread, composer, msgs, empty state, streaming */

/* ----- Chat shell ----------------------------------------------------- */
.chat-shell {
    display: grid;
    grid-template-columns: 280px 1fr;
    height: calc(100vh - 56px);
    height: calc(100dvh - 56px); /* dynamic vh: excludes mobile browser/nav chrome */
    overflow: hidden;
    background: var(--bg);
    transition: grid-template-columns 0.2s ease;
}
.chat-shell.sidebar-collapsed {
    grid-template-columns: 0 1fr;
}
.chat-shell.sidebar-collapsed .chat-sidebar {
    overflow: hidden;
    border-right: none;
}
.chat-main-medium { --chat-col: 1100px; }
.chat-main-wide   { --chat-col: 100%; }
.chat-main-medium .chat-composer,
.chat-main-wide   .chat-composer { max-width: 820px; }

.chat-sidebar {
    border-right: 1px solid var(--border);
    background: var(--bg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.chat-sidebar-head {
    padding: 10px 10px 6px;
}
.btn-newchat {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--text);
    border-radius: 10px;
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 600;
    transition: background 0.15s, border-color 0.15s;
}
.btn-newchat:hover {
    background: var(--bg-surface);
    border-color: var(--primary-muted);
}
.btn-newchat svg { color: var(--text-dim); }

.conversation-list {
    margin: 0;
    padding: 8px;
    overflow-y: auto;
    flex: 1;
}

/* ---- two-level sidebar (AGT-34): project folders + Recent ---------------- */
.sidebar-section { margin-bottom: 8px; }
.sidebar-section-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 6px 8px 2px;
}
.sidebar-section-label {
    margin: 0; font-size: 11px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim);
}
.sidebar-section-action {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border: none; background: none;
    border-radius: 6px; color: var(--text-dim); cursor: pointer;
}
.sidebar-section-action:hover { background: var(--bg-hover); color: var(--text); }
.sidebar-section-action .icon { width: 14px; height: 14px; }
.sidebar-list { list-style: none; margin: 0; padding: 0; }
.sidebar-hint { padding: 4px 10px; font-size: 12.5px; color: var(--text-dim); }
.sidebar-inline-form { padding: 4px 6px; }
.sidebar-input {
    width: 100%; box-sizing: border-box; padding: 6px 8px;
    font: inherit; font-size: 13px; border: 1px solid var(--border);
    border-radius: 8px; background: var(--bg-surface); color: var(--text);
}
.sidebar-input:focus { outline: none; border-color: var(--primary); }

.project-folder { margin-bottom: 1px; }
.project-row {
    position: relative; display: flex; align-items: center; gap: 6px;
    padding: 6px 8px; border-radius: 8px; cursor: pointer; transition: background 0.15s;
}
.project-row:hover { background: var(--bg-hover); }
.project-row > .icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-dim); }
.project-chevron {
    display: inline-flex; align-items: center; justify-content: center;
    width: 14px; height: 14px; flex-shrink: 0; color: var(--text-dim);
    transition: transform 0.15s;
}
.project-chevron .icon { width: 14px; height: 14px; }
.project-chevron-open { transform: rotate(90deg); }
.project-name {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; font-size: 13.5px; font-weight: 500; color: var(--text);
}
.project-row:hover .kebab-btn,
.project-row .kebab-btn.kebab-active { opacity: 1; }
.project-children {
    list-style: none; margin: 0 0 4px 14px; padding: 0;
}
.project-add-btn {
    display: flex; align-items: center; gap: 6px; width: 100%;
    padding: 5px 10px; border: none; background: none; color: var(--text-dim);
    font: inherit; font-size: 12.5px; cursor: pointer; border-radius: 6px;
}
.project-add-btn:hover { background: var(--bg-hover); color: var(--text); }
.project-add-btn .icon { width: 13px; height: 13px; }
.conversation-item {
    position: relative;
    display: flex;
    align-items: center;
    border-radius: 8px;
    transition: background 0.15s;
}
.conversation-item:hover { background: var(--bg-hover); }
.conversation-active { background: var(--primary-light) !important; }
/* Pending-approval "!" — this chat is parked waiting for your approval. */
.conversation-attention {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    margin-right: 4px;
    border-radius: 50%;
    background: #e8a33d;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
}
/* Auto-approve banner — shown when the chat auto-approves some/all commands. */
.auto-approve-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 6px 16px 0;
    padding: 6px 12px;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    font-size: 12.5px;
    color: var(--text-dim);
}
.auto-approve-banner .icon { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-dim); }
.auto-approve-banner span { flex: 1; }
.auto-approve-off {
    flex-shrink: 0;
    background: none;
    border: none;
    padding: 2px 6px;
    color: var(--primary);
    cursor: pointer;
    font: inherit;
    font-size: 12.5px;
}
.auto-approve-off:hover { text-decoration: underline; }

/* Permissions modal — friendly label per standing auto-approval, with the raw
   signature on a muted second line. Grouped by program; client-side search +
   per-program tag chips. The body fills the modal; only the list scrolls. */
.modal-permissions {
    width: min(720px, calc(100vw - 32px));
    height: min(78vh, 760px);
}
.permissions-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Sticky top controls — search + tag chips — stay put while the list scrolls. */
.permissions-controls {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 16px 20px 14px;
    border-bottom: 1px solid var(--border);
}
.permissions-search {
    width: 100%;
    box-sizing: border-box;
    padding: 9px 12px;
    font: inherit;
    font-size: 13.5px;
    color: var(--text);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: 9px;
}
.permissions-search:focus { outline: none; border-color: var(--primary-muted); background: var(--bg); }
.perm-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.perm-tag {
    padding: 3px 10px;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 11.5px;
    color: var(--text-dim);
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.perm-tag:hover { color: var(--text); border-color: var(--primary-muted); }
.perm-tag-active {
    color: #fff;
    background: var(--primary);
    border-color: var(--primary);
}

/* Scrollable list region. */
.permissions-list { flex: 1; min-height: 0; overflow-y: auto; padding: 8px 16px 16px; }

.perm-group { display: flex; flex-direction: column; gap: 2px; }
.perm-group + .perm-group { margin-top: 22px; }
.perm-group-head {
    padding: 0 6px 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--border);
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.perm-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 11px 12px;
    border-radius: 9px;
    transition: background 0.12s;
}
.perm-row:hover { background: var(--bg-surface); }
.perm-row-allowall { margin: 12px 16px 0; padding: 12px 14px; background: var(--primary-light); }
.perm-row-body { display: flex; flex-direction: column; gap: 5px; flex: 1; min-width: 0; }
.perm-row-label { font-size: 14px; line-height: 1.4; color: var(--text); }
.perm-row-sub { font-size: 11.5px; line-height: 1.4; color: var(--text-dim); }
/* Secondary line: the raw signature under the friendly label. Muted + mono. */
.permissions-sig {
    min-width: 0;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 11.5px;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--text-dim);
}
.perm-row .popover-row-action { margin-top: 1px; }

/* Working — this chat is actively running a turn (pulsing). */
.conversation-working {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    margin-right: 6px;
    border-radius: 50%;
    background: var(--primary);
    animation: conversation-pulse 1.1s ease-in-out infinite;
}
@keyframes conversation-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.35; transform: scale(0.7); }
}
/* Unread/activity dot — a turn finished in this conv while you weren't viewing it. */
.conversation-unread {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    margin-right: 6px;
    border-radius: 50%;
    background: var(--primary);
}
.conversation-active .conversation-title { color: var(--primary); font-weight: 600; }

.conversation-link {
    flex: 1;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
    padding: 9px 12px;
    border-radius: 8px;
    color: var(--text);
    overflow: hidden;
    /* gradient mask on the right so the title fades out near the kebab */
    mask-image: linear-gradient(to right, #000 calc(100% - 34px), transparent);
}
.conversation-title {
    font-size: 13.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
}
.conversation-empty {
    list-style: none;
    text-align: center;
    color: var(--text-dim);
    padding: 32px 16px;
    font-size: 13px;
    line-height: 1.6;
}
.conversation-empty .dim { font-size: 12px; }

/* ----- Chat main + thread --------------------------------------------- */
.chat-main {
    display: flex;
    flex-direction: column;
    min-height: 0;        /* let overflow children shrink instead of pushing */
    overflow: hidden;
}
.chat-thread {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;        /* same reason — composer must stay in view */
    overflow: hidden;
    position: relative;   /* anchors the floating "New messages" jump pill */
}
.chat-thread-head {
    padding: 14px 24px;
    border-bottom: 1px solid var(--border);
    background: var(--bg);
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}
.chat-thread-title {
    font-size: 16px;
    font-weight: 700;
}
.chat-thread-meta {
    font-size: 12px;
    color: var(--text-dim);
}
.chat-thread-messages {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;                 /* never scroll sideways */
    -webkit-overflow-scrolling: touch;  /* momentum scrolling on iOS */
    overscroll-behavior: contain;
    padding: 0 0 0;
    background: var(--bg);
    width: 100%;
    scrollbar-gutter: stable;
}
/* Long tokens wrap instead of forcing horizontal scroll; wide code/tables scroll
   in their own box, not the thread. */
.chat-msg-prose { overflow-wrap: break-word; word-break: break-word; }
.chat-msg-prose pre,
.chat-msg-prose table { max-width: 100%; overflow-x: auto; }
.chat-msg-prose table { display: block; }
.chat-thread-messages::before {
    content: '';
    display: block;
    position: sticky;
    top: 0;
    height: 24px;               /* replaces the removed padding-top: 24px */
    background: linear-gradient(to bottom, var(--bg), transparent);
    pointer-events: none;
    z-index: 1;
}
.chat-thread-messages::after {
    content: '';
    display: block;
    position: sticky;
    bottom: 0;
    height: 24px;
    margin-top: -24px;
    background: linear-gradient(to top, var(--bg), transparent);
    pointer-events: none;
    z-index: 1;
}
/* Each message stays in a centered 820px lane within the full-width scroll area. */
/* Lane: every direct child of the scroll area sits in the same centered
   820 px column with 24 px gutter. Tool-call cards are wrapped in a
   .chat-msg.chat-msg-tool so they share this geometry too. */
.chat-thread-messages > .chat-msg {
    max-width: var(--chat-col);
    margin: 0 auto 14px;
    padding: 0 24px;
    width: 100%;
    display: flex;
    flex-direction: column;
}
/* The live "Working…" block shares the SAME centered lane as messages (it's a
   direct child of the scroll area but not a .chat-msg), so it lines up with the
   text column instead of sticking to the left edge. */
.chat-thread-messages > #turn-activity {
    max-width: var(--chat-col);
    margin: 0 auto;
    padding: 8px 24px;
    width: 100%;
    box-sizing: border-box;
}

.chat-empty {
    margin: auto;
    text-align: center;
    color: var(--text-dim);
    padding: 40px;
}
.chat-empty h2 { color: var(--text); margin-bottom: 8px; font-weight: 700; }
.chat-empty .dim { font-size: 14px; }
.dim { color: var(--text-dim); }

/* .chat-msg is the per-message centered lane; align its inner bubble. */
.chat-msg-user .chat-msg-role { display: none; }
.chat-msg-user .chat-msg-content {
    align-self: flex-end;
    background: var(--bg-surface);
    border-radius: 18px;
    padding: 10px 16px;
    color: var(--text);
    max-width: 75%;
}
.chat-msg-assistant .chat-msg-content {
    padding: 0;
    background: transparent;
}
.chat-msg-assistant .chat-msg-role {
    color: var(--primary-muted);
}
.chat-msg-assistant .chat-msg-actions { align-self: flex-start; }
.chat-msg-role {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text-dim);
    font-weight: 700;
    margin-bottom: 4px;
}
.chat-msg-content {
    font-size: 14.5px;
    line-height: 1.6;
    white-space: pre-wrap;
    word-wrap: break-word;
    color: var(--text);
}
.chat-msg-streaming .chat-msg-content {
    /* nothing extra — caret span carries the indicator */
}
.chat-msg-caret {
    display: inline-block;
    width: 8px;
    height: 1em;
    margin-left: 2px;
    vertical-align: text-bottom;
    background: var(--primary);
    border-radius: 1px;
    animation: caret-blink 0.9s steps(2) infinite;
}
@keyframes caret-blink { 50% { opacity: 0; } }

/* ----- Composer (LibreChat / ChatGPT style: floating rounded box) ---- */
.chat-composer {
    /* No border-top, no surface fill — composer floats on the white thread. */
    display: flex;
    flex-direction: column;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 22px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.05);
    padding: 12px 14px 8px 18px;
    width: calc(100% - 32px);
    max-width: var(--chat-col);
    /* Keep the composer clear of the home/nav bar on mobile (safe-area is 0
       on desktop, so this is a no-op there). */
    margin: 16px auto calc(18px + env(safe-area-inset-bottom));
    transition: box-shadow 0.18s, border-color 0.18s;
}
.chat-composer:focus-within {
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.09);
    border-color: var(--primary-muted);
}

.chat-composer-input {
    flex: 1;
    padding: 0;
    border: none;
    background: transparent;
    font-family: inherit;
    font-size: 15px;
    line-height: 1.5;
    resize: none;
    overflow-y: hidden;        /* widgets.js toggles to auto past 5 rows */
    color: var(--text);
}
.chat-composer-input:focus { outline: none; }
.chat-composer-input::placeholder { color: var(--text-dim); }
.chat-composer-input:disabled { opacity: 0.6; cursor: not-allowed; }

/* Bottom row inside the composer: hint on the left, send/stop on the right. */
.chat-composer-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 6px;
    gap: 8px;
}
.chat-composer-hint {
    font-size: 11.5px;
    color: var(--text-dim);
    letter-spacing: 0.2px;
    user-select: none;
}

.composer-icon-btn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.15s, opacity 0.15s, transform 0.05s;
}
.composer-icon-btn:active { transform: translateY(1px); }
.composer-send {
    background: var(--text);
    color: #fff;
}
.composer-send:hover { background: var(--primary); }
.composer-send:disabled {
    background: var(--text-dim);
    opacity: 0.35;
    cursor: not-allowed;
    pointer-events: none;
}
.composer-stop {
    background: var(--error);
    color: #fff;
}
.composer-stop:hover { filter: brightness(1.05); }

/* ----- Mobile chat ---------------------------------------------------- */
/* ----- Responsive: mobile / tablet (AGT-27) -------------------------- */

/* Hamburger lives in the chat header; desktop-hidden, shown under the
   breakpoint. The backdrop sits behind the drawer and is inert on desktop. */
.chat-nav-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-right: 2px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg);
    color: var(--text);
    cursor: pointer;
    flex-shrink: 0;
}
.chat-nav-toggle:hover { background: var(--bg-surface); }
.chat-nav-backdrop { display: none; }

/* Bare editor header exists only to host the mobile hamburger (skills empty
   state); collapsed/invisible on desktop where the hamburger is hidden. */
.skill-editor-head-bare { border-bottom: none; min-height: 0; padding: 0; }

/* Pills go ICON-ONLY when the header gets too crowded for labels. The layout
   control buttons added to the right side push this threshold up to 1180px. */
@media (max-width: 1180px) {
    .chat-header-left {
        gap: 4px;
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }
    .chat-header-left::-webkit-scrollbar { display: none; }
    .header-skill-label,
    .header-skill-value { display: none; }
    .header-skill-btn,
    .chat-model-badge { padding: 6px 8px; flex-shrink: 0; }
    .chat-model-badge code { max-width: 84px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

@media (max-width: 860px) {
    /* Single column; the sidebar floats over the chat as an off-canvas drawer.
       sidebar-collapsed has no meaning on mobile — sidebar is always off-canvas. */
    .chat-shell,
    .chat-shell.sidebar-collapsed { grid-template-columns: 1fr; }


    /* "↵ to send · ⇧↵ newline" is a desktop convention — no Shift+Enter on a
       phone keyboard, and Send is a button. Hide it; the send row keeps its
       right-aligned buttons. */

    /* Layout controls are desktop-only: mobile has its own hamburger for the
       sidebar, and narrow viewports have no room for width cycling. */
    .header-layout-btn { display: none !important; }

    .chat-nav-toggle { display: inline-flex; }

    .chat-sidebar {
        position: fixed;
        top: 56px;               /* below the global navbar */
        left: 0;
        bottom: 0;
        width: 280px;
        max-width: 84vw;
        z-index: 60;
        border-right: 1px solid var(--border);
        box-shadow: 2px 0 16px rgba(0, 0, 0, 0.28);
        transform: translateX(-100%);
        transition: transform 0.2s ease;
    }
    .chat-sidebar.nav-open { transform: translateX(0); }

    .chat-nav-backdrop {
        display: block;
        position: fixed;
        inset: 56px 0 0 0;
        z-index: 55;
        background: rgba(0, 0, 0, 0.45);
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.2s ease;
    }
    .chat-nav-backdrop.nav-open { opacity: 1; pointer-events: auto; }

    /* Tighten the header gutters for narrow widths. */
    .chat-header { padding: 8px 10px 0; }
    .skill-editor-head-bare { padding: 8px 12px; }

    /* The pill dropdowns would be clipped by the scrolling header, so on
       mobile they detach to a fixed panel pinned under the header bar
       (navbar 56 + header 48 = 104px). */
    .chat-header .popover-menu {
        position: fixed;
        top: 104px;
        left: 8px;
        right: 8px;
        width: auto;
        max-width: none;
        max-height: calc(100vh - 120px);
        overflow-y: auto;
        z-index: 70;
    }
}

/* Smallest phones: drop the model name too — just the gear (tap to see/switch). */
@media (max-width: 480px) {
    .chat-model-badge code { display: none; }
}

/* ----- Chat header (above thread) ------------------------------------ */
/* The header spans the full chat-main width so the model badge sits flush
   against the sidebar and the action icons sit flush against the right
   edge — independent of the centered 820 px message column below. */
.chat-header {
    height: 48px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 16px 0;
    background: var(--bg);
    gap: 12px;
    width: 100%;
}
.chat-header-left, .chat-header-right {
    display: flex;
    align-items: center;
    gap: 6px;
}
.chat-model-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    color: var(--text-secondary);
    font-size: 12px;
}
.chat-model-badge code {
    background: transparent;
    color: var(--primary);
    padding: 0;
    font-size: 12px;
    font-weight: 600;
}
.chat-model-badge svg {
    color: var(--primary-muted);
}
.header-icon-btn {
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    background: transparent;
    border: none;
    color: var(--text-dim);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    font-family: inherit;
}
.header-icon-btn:hover {
    background: var(--bg-surface);
    color: var(--text);
}
.header-icon-btn.header-icon-danger:hover {
    background: var(--error-bg);
    color: var(--error);
}

/* ----- Empty state: greeting + centered composer --------------------- */
.chat-empty-center {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    padding: 24px;
    max-width: 720px;
    margin: 0 auto;
    width: 100%;
}
.chat-empty-icon {
    color: var(--primary-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    border-radius: 50%;
    background: var(--primary-light);
}
.chat-empty-greeting {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.3px;
    color: var(--text);
    text-align: center;
}
/* Rotating one-liner under the time-of-day greeting. */
.chat-empty-greeting-sub {
    color: var(--text);
    font-size: 16px;
    text-align: center;
    margin-top: -10px;
    opacity: 0.85;
}
.chat-empty-sub {
    color: var(--text-dim);
    font-size: 13.5px;
    text-align: center;
    margin-top: -4px;
}
/* In the empty state, let the composer sit nicely under the greeting. */
.chat-empty-center .chat-composer {
    margin: 8px 0 0;
}

/* ----- Thinking pill (shows in empty assistant bubble while LLM warms up) */
.thinking-pill {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 6px 14px 6px 10px;
    background: var(--bg-surface);
    border-radius: 16px;
    color: var(--text-dim);
    font-size: 13.5px;
    font-style: italic;
    user-select: none;
}
.thinking-dots {
    display: inline-flex;
    gap: 4px;
}
.thinking-dots > span {
    width: 5px;
    height: 5px;
    background: var(--primary-muted);
    border-radius: 50%;
    animation: thinking-bounce 1.4s infinite ease-in-out;
}
.thinking-dots > span:nth-child(2) { animation-delay: 0.18s; }
.thinking-dots > span:nth-child(3) { animation-delay: 0.36s; }
@keyframes thinking-bounce {
    0%, 70%, 100% { transform: scale(0.7); opacity: 0.45; }
    35%          { transform: scale(1.2); opacity: 1; }
}
.thinking-verb {
    display: inline-block;
    min-width: 92px;     /* keeps the pill width stable while verb cycles */
}

/* ----- Live turn activity block (#turn-activity) --------------------- */
/* The single "Working…" line shown at the bottom of the thread while a turn
   runs; patched IN PLACE on every tool step so the thread never re-renders.
   Folds into "Worked · N steps" at turn end. Matches the thinking-pill look. */
.turn-activity {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin: 4px 0 2px;
    color: var(--text-dim);
    font-size: 13.5px;
    font-style: italic;
    animation: turn-activity-in 0.18s ease-out;
}
.turn-activity { display: block; }
.turn-activity[hidden] { display: none; }
@keyframes turn-activity-in { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
.turn-activity-head { display: flex; align-items: center; gap: 10px; }
.turn-activity-spinner {
    flex: none;
    width: 13px;
    height: 13px;
    border: 2px solid var(--primary-muted);
    border-right-color: transparent;
    border-radius: 50%;
    animation: turn-activity-spin 0.7s linear infinite;
}
@keyframes turn-activity-spin { to { transform: rotate(360deg); } }
.turn-activity-label { font-style: normal; font-weight: 600; color: var(--text); }

/* Accumulating short status lines — one per step, the model's narration. Each
   is clipped to a single line; the current one carries a typing cursor. */
.turn-activity-lines { margin: 6px 0 0 23px; display: flex; flex-direction: column; gap: 3px; }
.ta-line {
    font-style: italic;
    font-size: 13px;
    color: var(--text-dim);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    animation: turn-activity-in 0.18s ease-out;
}
.ta-line.ta-done { opacity: 0.7; }
.ta-line.ta-done::before { content: "✓ "; font-style: normal; color: var(--primary-muted); }
/* Blinking typing cursor on the line currently being written. */
.ta-line.ta-current::after {
    content: "";
    display: inline-block;
    width: 6px;
    height: 1em;
    margin-left: 2px;
    vertical-align: text-bottom;
    background: var(--primary);
    border-radius: 1px;
    animation: caret-blink 0.9s steps(2) infinite;
}
/* A command needing approval is surfaced live, in full, inside the block. */
.turn-activity-approval { width: 100%; margin-top: 8px; }
.turn-activity-approval:empty { margin-top: 0; }
.turn-activity-approval-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
    font-style: normal;
    font-weight: 600;
    color: var(--text);
}
.turn-activity-approval-head svg { width: 15px; height: 15px; }

/* KaTeX inline tweaks: keep math text size matched to body. No scrollbars
   on display math — let it wrap or overflow visibly. */
.katex { font-size: 1em; max-width: 100%; }
.katex-display {
    margin: 12px 0;
    overflow: visible;
}
.katex-display > .katex {
    white-space: normal;
}
/* Block code already scrolls; inline math should never scroll. */
.chat-msg-prose .katex-html { white-space: normal; }

/* ----- Markdown tables ------------------------------------------------ */
.chat-msg-prose table {
    border-collapse: collapse;
    font-size: 13.5px;
    line-height: 1.5;
}
.chat-msg-prose th,
.chat-msg-prose td {
    padding: 6px 14px;
    border: 1px solid var(--border);
    text-align: left;
    white-space: nowrap;
}
.chat-msg-prose th {
    background: var(--bg-surface);
    font-weight: 600;
    font-size: 12.5px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-dim);
}
.chat-msg-prose tr:hover td { background: var(--bg-hover); }

/* Table outer: positioning context (no overflow, so btn stays visible) */
.table-outer {
    position: relative;
    margin: 12px 0;
}
/* Table wrap: scroll container only — no position, no margin */
.table-wrap {
    display: block;
    overflow-x: auto;
    border-radius: 8px;
    border: 1px solid var(--border);
}
.table-wrap table { margin: 0; border: none; border-radius: 0; width: 100%; }
.table-wrap th,
.table-wrap td { border-left: none; border-right: none; }
.table-wrap tr { border-bottom: 1px solid var(--border); }
.table-wrap tr:last-child { border-bottom: none; }

/* Action button: top-right of outer (not inside the scroll container) */
.table-action-btn {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 26px;
    height: 26px;
    opacity: 0.5;
    transition: opacity 0.15s;
    z-index: 2;
}
.table-action-btn .icon { width: 13px; height: 13px; }
.table-outer:hover .table-action-btn,
.table-action-btn:focus { opacity: 1; }

/* Dropdown reuses .popover-menu / .popover-item */
.table-action-menu {
    display: none;
    position: absolute;
    top: 34px;
    right: 5px;
    left: auto;
}
.table-action-menu.open { display: block; }

/* Save-to-workspace modal: body layout + input (modal chrome reuses .modal/.modal-narrow) */
.table-save-modal .modal-body { display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 20px; }
.table-save-input {
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--bg-surface);
    color: var(--text);
    font-size: 13.5px;
    font-family: var(--font-mono);
    width: 100%;
    box-sizing: border-box;
}
.table-save-input:focus { outline: none; border-color: var(--primary-muted); }
.table-save-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* ----- Streaming caret + assistant-label tweaks ---------------------- */

/* Hide the "ASSISTANT" label; the bubble's left-aligned, no-card style
   is enough indication of who's talking. (User bubbles already had role
   hidden via .chat-msg-user .chat-msg-role { display: none }.) */
.chat-msg-assistant .chat-msg-role { display: none; }

/* Blinking caret at the end of the last block while streaming.
   Using ::after on the last child means the caret renders inline at the
   tail of the last paragraph — no extra DOM node, no per-token churn. */
.chat-msg-streaming .chat-msg-content > *:last-child::after {
    content: '';
    display: inline-block;
    width: 7px;
    height: 1em;
    margin-left: 3px;
    vertical-align: text-bottom;
    background: var(--primary);
    border-radius: 1px;
    animation: caret-blink 0.9s steps(2) infinite;
}
/* No caret while the thinking pill is shown (pill is the indicator). */
.chat-msg-streaming .chat-msg-content > .thinking-pill::after { display: none; }


/* ----- Skill editor (reuses .chat-shell sidebar) ---------------------- */
.skill-editor {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.skill-editor-head {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 20px 8px;
    gap: 12px;
}
.skill-editor-title h2 {
    font-size: 16px;
    font-weight: 700;
    color: var(--text);
}
.skill-editor-meta {
    font-size: 11.5px;
    color: var(--text-dim);
}
.skill-editor-meta code {
    background: var(--bg-surface);
    color: var(--text-secondary);
    font-size: 11px;
}
.skill-editor-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}
.skill-editor-status {
    font-size: 11.5px;
    color: var(--warning);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.skill-editor-slug {
    display: flex;
    align-items: center;
    gap: 8px;
}
.skill-editor-slug label {
    font-size: 11px;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.skill-editor-slug input {
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg-surface);
    font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
    font-size: 13px;
    color: var(--text);
    min-width: 220px;
}
.skill-editor-slug input:focus {
    outline: none;
    border-color: var(--primary);
    background: var(--bg);
}

.skill-editor-body {
    flex: 1;
    margin: 0 20px 18px;
    padding: 16px 18px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg-surface);
    font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
    font-size: 13px;
    line-height: 1.6;
    color: var(--text);
    resize: none;
    outline: none;
    tab-size: 4;
}
.skill-editor-body:focus {
    border-color: var(--primary-muted);
    background: var(--bg);
}

/* ----- Skills page filter strip + persona badge ---------------------- */
.skill-filter {
    display: flex;
    gap: 4px;
    padding: 8px 10px 4px;
    border-bottom: 0;
}
.skill-filter-btn {
    flex: 1;
    padding: 5px 10px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 16px;
    color: var(--text-dim);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.skill-filter-btn:hover { color: var(--text); background: var(--bg-surface); }
.skill-filter-btn.skill-filter-active {
    background: var(--primary-light);
    color: var(--primary);
    border-color: var(--primary-muted);
}

.skill-mode-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    background: var(--primary-light);
    color: var(--primary);
    border-radius: 10px;
    font-size: 9.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    vertical-align: middle;
}

/* ---- read state: sticky divider, jump pill, connection banner ------------ */

/* "New messages" line. Server-rendered inside the thread, frozen in place —
   it marks where you stopped reading and stays there while you catch up. */
.new-divider {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 18px 0 6px;
    color: var(--error);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
}
.new-divider::before,
.new-divider::after {
    content: "";
    height: 1px;
    background: currentColor;
    opacity: 0.35;
}
.new-divider::before { flex: 0 0 12px; }
.new-divider::after { flex: 1; }
/* display:flex would otherwise beat the [hidden] attribute when the line is
   retired client-side on reaching the bottom. */
.new-divider[hidden] { display: none; }

/* Jump-to-latest pill — the alternative to yanking the viewport when new
   content lands while you're reading further up. */
.thread-jump {
    position: absolute;
    left: 50%;
    bottom: 124px;   /* clears the composer at its resting height, with air */
    transform: translateX(-50%);
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-card);
    color: var(--text);
    font-size: 0.8rem;
    box-shadow: var(--shadow-md);
    cursor: pointer;
}
.thread-jump:hover { background: var(--bg-hover); }
.thread-jump svg { width: 14px; height: 14px; transform: rotate(90deg); }
.thread-jump[hidden] { display: none; }

/* Connection banner — a dead SSE stream is otherwise indistinguishable from
   an idle chat, which is what made mobile taps look ignored. */
.conn-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 6px 12px;
    background: var(--warning-bg);
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-size: 0.8rem;
}
.conn-banner[hidden] { display: none; }
.conn-banner-retry {
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 2px 8px;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

/* Optimistic approval feedback: the row commits on tap, before the network
   answers, so a slow or dead stream can't make the click look ignored. */
.tool-call-actions-sent { opacity: 0.6; }
.tool-call-sending {
    align-self: center;
    color: var(--text-dim);
    font-size: 0.78rem;
}
.tool-call-sending[hidden] { display: none; }
