    :root {
      --bg-surface: #fff;
      --bg-surface-2: #ffffff;
      --bg-error-soft: #fee;
      --bg-success-soft: #efe;
      --bg-header: #f9f9f9;
      --bg-sidebar: #f5f5f5;
      --bg-user-bar: #f0f0f0;
      --bg-row-quiet: #fafafa;
      --bg-diff: #fafbfc;
      --bg-recap: #f7f7f7;
      --bg-code-inline: #f4f4f4;
      --bg-pill-soft: #f3f3f3;
      --bg-tool-rollup: #f5f5f7;
      --bg-tail-task: #f5f5fa;
      --bg-diff-edit-sep: #f0f1f3;
      --bg-share-gh-hover: #f0f5fb;
      --bg-ac-hover: #eef4fb;
      --bg-details-action-hover: #eaf3fb;
      --bg-question: #f3f7ff;
      --bg-question-hover: #e8efff;
      --bg-dropzone-active: #f0f7ff;
      --bg-mention: #fff3cd;
      --bg-self-mention: #fff8d6;
      --bg-task-pending: #fff8e6;
      --bg-task-in-progress: #fffbe6;
      --bg-details-danger: #fff8f8;
      --bg-perm-approved: #e6f7e6;
      --bg-task-done: #e8f4e8;
      --bg-tool-post: #f0f8f0;
      --bg-rollup-post: #d4f0d4;
      --bg-presence-ring: #d6efd6;
      --bg-question-chosen: #d6f0d6;
      --bg-toggle-on: #d8efd9;
      --bg-published: #e8f5e9;
      --bg-error-tint: #fdebeb;
      --bg-danger-hover: #fbecec;
      --bg-unread-hover: #f5dada;
      --bg-rollup-fail: #f8d4d4;
      --bg-diff-del: #ffeef0;
      --bg-diff-add: #e6ffec;
      --bg-file-event: #e8f0fe;
      --bg-pending-chip-hover: #d0e2f5;
      --bg-question-header: #d6e0f5;
      --bg-hover-grey-1: #e8e8e8;
      --bg-rollup-pre: #e8e8ec;
      --bg-hover-grey-2: #ebebeb;
      --bg-tool-rollup-hover: #ebebef;
      --bg-hover-pale: #eee;
      --border-strong: #ccc;
      --border-default: #ddd;
      --border-soft: #bbb;
      --border-op-msg: #aac;
      --text-faint: #aaa;
      --text-meta: #999;
      --text-muted: #888;
      --text-muted-2: #777;
      --text-muted-3: #666;
      --text-secondary: #555;
      --text-secondary-2: #444;
      --text-primary: #333;
      --text-strong: #222;
      --text-black: #000;
      --text-success-bright: #3c3;
      --text-error-bright: #c33;
      --accent: #0066cc;
      --accent-hover: #0052a3;
      --accent-hover-2: #0050a0;
      --accent-disabled: #99c0e6;
      --accent-pip-focused-text: #c5e2ff;
      --accent-pip-banner-text: #d1ecff;
      --accent-pip-focused-bg: #1e3a5f;
      --accent-pip-focused-border: #4a90e2;
      --accent-deep: #1a4a7a;
      --accent-deep-2: #1f3870;
      --accent-deep-3: #1f2d4a;
      --accent-op-msg: #224466;
      --border-question: #c9d6f0;
      --border-question-hover: #8eaaef;
      --border-op-msg-soft: #c8dcef;
      --border-file-event: #b3d9f2;
      --bg-inbound-cmd: #1f2937;
      --ok: #2a8a2a;
      --ok-strong: #1f6b1f;
      --ok-strong-2: #1a6b2c;
      --ok-status: #1a7f3c;
      --ok-deep: #2a5a2a;
      --ok-deep-2: #2e7d32;
      --ok-soft: #7fc97f;
      --ok-medium: #6bb56b;
      --ok-pale: #b3dab3;
      --ok-pale-2: #c8e6c9;
      --ok-pale-3: #d0e8d0;
      --ok-text-diff: #1a4521;
      --ok-rollup-text: #1f6b2c;
      --danger: #d33;
      --danger-deep: #b22;
      --danger-text: #b32020;
      --danger-hover: #a01818;
      --danger-text-deep: #6b2020;
      --danger-rollup-text: #8a1f1f;
      --danger-text-diff: #5c1a1a;
      --danger-border: #d97a7a;
      --danger-border-soft: #f0c0c0;
      --danger-border-soft-2: #e8c0c0;
      --code-inline-text: #c7254e;
      --border-pale: #d0d0d0;
      --warn-border: #f5d97a;
      --warn-text: #6b5400;
      --warn-status: #b8860b;
      --warn-mention-border: #f5b400;
      --pip-bg: #1f1f23;
      --pip-fg: #e8e8ec;          /* PIP text — same in both themes */
      --pip-meta-bg: #27272c;
      --pip-header-bg: #2a2a30;
      --pip-border: #3a3a40;
      --bg-input: #fff;            /* form-control background */
      --border-tool-rollup: #d8d8e0;
      --border-tool-rollup-2: #e0e0e8;
      --border-diff: #e1e4e8;
      --border-pre: #e8e8e8;
      --shadow-row-hover: rgba(0, 0, 0, 0.03);
      --shadow-code-bg: rgba(0, 0, 0, 0.05);
      --shadow-popover: rgba(0, 0, 0, 0.1);
      --shadow-popover-2: rgba(0, 0, 0, 0.12);
      --shadow-menu: rgba(0, 0, 0, 0.15);
      --shadow-pill: rgba(0, 0, 0, 0.18);
      --shadow-modal: rgba(0, 0, 0, 0.25);
      --shadow-backdrop: rgba(0, 0, 0, 0.35);
      --shadow-pip: rgba(0, 0, 0, 0.4);
      --shadow-modal-backdrop: rgba(0, 0, 0, 0.45);
      --task-recap-bg: rgba(255, 255, 255, 0.6);
      --shadow-pip-focused: rgba(74, 144, 226, 0.35);
    }

    /* Dark palette — applied two ways:
       1. @media (prefers-color-scheme: dark) — auto-follow OS
       2. [data-theme="dark"] on <html> — manual override
       Manual override wins because the media-query block scopes its
       :root selector with :not([data-theme="light"]), and the
       [data-theme="dark"] rule on documentElement carries equal
       specificity to :root but appears later in source order.
       [data-theme="light"] on <html> opts out of the media-query
       dark block so a dark-OS operator can force light without
       clearing localStorage.

       PIP terminal palette (--pip-*) is intentionally NOT overridden
       — the terminal is already dark in light mode and operators are
       used to it. Status colors (ok/danger/warn) get slightly more
       saturated values so red doesn't disappear against dark
       backgrounds and green has enough contrast on warm-grey bg. */
    [data-theme="dark"] {
      color-scheme: dark;                /* native form-control + scrollbar styling follows */
      /* Page surfaces — warm dark greys, not OLED black */
      --bg-surface:               #1a1a1d;   /* main bg, panels */
      --bg-surface-2:             #1a1a1d;
      --bg-input:                 #2a2b32;   /* textarea / input fields */
      --bg-header:                #1f2025;   /* header strip */
      --bg-sidebar:               #22232a;   /* nav */
      --bg-user-bar:              #1f2025;
      --bg-row-quiet:             #1f2025;
      --bg-recap:                 #1f2025;
      --bg-code-inline:           #2a2b32;
      --bg-pill-soft:             #2a2b32;
      --bg-tool-rollup:           #25262d;
      --bg-tail-task:             #25262d;
      --bg-diff:                  #0f0f12;
      --bg-diff-edit-sep:         #25262d;
      --bg-share-gh-hover:        #2a3340;
      --bg-ac-hover:              #2a3340;
      --bg-details-action-hover:  #233248;
      --bg-question:              #1b2538;
      --bg-question-hover:        #243352;
      --bg-question-chosen:       #1f3a24;
      --bg-question-header:       #2d3a55;
      --bg-dropzone-active:       #1b2a3f;
      --bg-mention:               #4a3d0e;
      --bg-self-mention:          #5a4a0e;
      --bg-task-pending:          #3a2f0a;
      --bg-task-in-progress:      #44380c;
      --bg-task-done:             #1f3a24;
      --bg-perm-approved:         #1f3a24;
      --bg-published:             #1f3a24;
      --bg-tool-post:             #1c2a1c;
      --bg-rollup-post:           #25422a;
      --bg-rollup-pre:            #2a2b32;
      --bg-rollup-fail:           #3a1c1c;
      --bg-presence-ring:         #1f3a24;
      --bg-toggle-on:             #1f4a2c;
      --bg-error-tint:            #3a1c1c;
      --bg-error-soft:            #3a1c1c;
      --bg-success-soft:          #1f3a24;
      --bg-danger-hover:          #3a1c1c;
      --bg-unread-hover:          #3a1c1c;
      --bg-diff-add:              #102818;
      --bg-diff-del:              #2a1416;
      --bg-file-event:            #1b2a3f;
      --bg-pending-chip-hover:    #2a3a52;
      --bg-details-danger:        #2a1414;
      --bg-hover-grey-1:          #2c2d34;
      --bg-hover-grey-2:          #2c2d34;
      --bg-hover-pale:            #2c2d34;
      --bg-tool-rollup-hover:     #2f3038;
      --bg-inbound-cmd:           #0f0f12;

      /* Text — primary stays high-contrast on the warm dark bg */
      --text-primary:        #e8e8ec;
      --text-strong:         #f3f3f7;
      --text-secondary:      #c8c8d0;
      --text-secondary-2:    #b8b8c0;
      --text-muted-3:        #a0a0a8;
      --text-muted-2:        #9a9aa3;
      --text-muted:          #9a9aa3;
      --text-meta:           #7a7a82;
      --text-faint:          #6a6a72;
      --text-black:          #f3f3f7;       /* used as the "darkest" — flip in dark */
      --text-success-bright: #4ad94a;
      --text-error-bright:   #ff6464;

      /* Borders — clear separators on dark, no muddy mid-greys */
      --border-default:           #3a3a44;
      --border-strong:            #4a4a55;
      --border-soft:              #2f2f38;
      --border-pale:              #2f2f38;
      --border-pre:                #2f2f38;
      --border-diff:              #2f2f38;
      --border-tool-rollup:       #3a3a44;
      --border-tool-rollup-2:     #3a3a44;
      --border-op-msg:            #3a3a44;
      --border-op-msg-soft:       #3a3a44;
      --border-file-event:        #3a4a5c;
      --border-question:          #3a4a5c;
      --border-question-hover:    #5a7ab5;

      /* Accent — light-mode #0066cc reads too dim on dark; use a
         brighter Tailwind-blue-500-ish hue */
      --accent:                #3b82f6;
      --accent-hover:          #60a5fa;
      --accent-hover-2:        #60a5fa;
      --accent-disabled:       #2a3a55;
      --accent-deep:           #9bb8e0;   /* "deep" labels invert in dark */
      --accent-deep-2:         #9bb8e0;
      --accent-deep-3:         #c0d0e8;
      --accent-op-msg:         #a8c4e8;

      /* Status — slightly brighter saturated versions */
      --ok:               #4ad94a;
      --ok-strong:        #5ee05e;
      --ok-strong-2:      #5ee05e;
      --ok-status:        #4ad96a;
      --ok-deep:          #a0e8a0;
      --ok-deep-2:        #a0e8a0;
      --ok-soft:          #3a6b3a;
      --ok-medium:        #4a8a4a;
      --ok-pale:          #2a4a2a;
      --ok-pale-2:        #2a4a2a;
      --ok-pale-3:        #2a4a2a;
      --ok-text-diff:     #a0e8a0;
      --ok-rollup-text:   #6ee06e;

      --danger:            #ef4444;
      --danger-deep:       #ff6464;
      --danger-text:       #ff8585;
      --danger-hover:      #dc2626;
      --danger-text-deep:  #ff9090;
      --danger-rollup-text:#ff9090;
      --danger-text-diff:  #ff9090;
      --danger-border:     #6b2a2a;
      --danger-border-soft:#5a2424;
      --danger-border-soft-2:#5a2424;
      --code-inline-text:  #ff7a9a;

      --warn-border:          #6b5a14;
      --warn-text:            #f0d878;
      --warn-status:          #f0d878;
      --warn-mention-border:  #f0c000;

      /* Translucent overlays — boost opacity so they read on the
         already-dark surfaces */
      --shadow-row-hover:        rgba(255, 255, 255, 0.04);
      --shadow-code-bg:          rgba(255, 255, 255, 0.06);
      --shadow-popover:          rgba(0, 0, 0, 0.5);
      --shadow-popover-2:        rgba(0, 0, 0, 0.5);
      --shadow-menu:             rgba(0, 0, 0, 0.6);
      --shadow-pill:             rgba(0, 0, 0, 0.6);
      --shadow-modal:            rgba(0, 0, 0, 0.7);
      --shadow-backdrop:         rgba(0, 0, 0, 0.6);
      --shadow-pip:              rgba(0, 0, 0, 0.6);
      --shadow-modal-backdrop:   rgba(0, 0, 0, 0.7);
      --task-recap-bg:           rgba(255, 255, 255, 0.04);
      --shadow-pip-focused:      rgba(96, 165, 250, 0.45);
    }

    @media (prefers-color-scheme: dark) {
      :root:not([data-theme="light"]) {
        color-scheme: dark;
        --bg-surface:               #1a1a1d;
        --bg-surface-2:             #1a1a1d;
        --bg-input:                 #2a2b32;
        --bg-header:                #1f2025;
        --bg-sidebar:               #22232a;
        --bg-user-bar:              #1f2025;
        --bg-row-quiet:             #1f2025;
        --bg-recap:                 #1f2025;
        --bg-code-inline:           #2a2b32;
        --bg-pill-soft:             #2a2b32;
        --bg-tool-rollup:           #25262d;
        --bg-tail-task:             #25262d;
        --bg-diff:                  #0f0f12;
        --bg-diff-edit-sep:         #25262d;
        --bg-share-gh-hover:        #2a3340;
        --bg-ac-hover:              #2a3340;
        --bg-details-action-hover:  #233248;
        --bg-question:              #1b2538;
        --bg-question-hover:        #243352;
        --bg-question-chosen:       #1f3a24;
        --bg-question-header:       #2d3a55;
        --bg-dropzone-active:       #1b2a3f;
        --bg-mention:               #4a3d0e;
        --bg-self-mention:          #5a4a0e;
        --bg-task-pending:          #3a2f0a;
        --bg-task-in-progress:      #44380c;
        --bg-task-done:             #1f3a24;
        --bg-perm-approved:         #1f3a24;
        --bg-published:             #1f3a24;
        --bg-tool-post:             #1c2a1c;
        --bg-rollup-post:           #25422a;
        --bg-rollup-pre:            #2a2b32;
        --bg-rollup-fail:           #3a1c1c;
        --bg-presence-ring:         #1f3a24;
        --bg-toggle-on:             #1f4a2c;
        --bg-error-tint:            #3a1c1c;
        --bg-error-soft:            #3a1c1c;
        --bg-success-soft:          #1f3a24;
        --bg-danger-hover:          #3a1c1c;
        --bg-unread-hover:          #3a1c1c;
        --bg-diff-add:              #102818;
        --bg-diff-del:              #2a1416;
        --bg-file-event:            #1b2a3f;
        --bg-pending-chip-hover:    #2a3a52;
        --bg-details-danger:        #2a1414;
        --bg-hover-grey-1:          #2c2d34;
        --bg-hover-grey-2:          #2c2d34;
        --bg-hover-pale:            #2c2d34;
        --bg-tool-rollup-hover:     #2f3038;
        --bg-inbound-cmd:           #0f0f12;

        --text-primary:        #e8e8ec;
        --text-strong:         #f3f3f7;
        --text-secondary:      #c8c8d0;
        --text-secondary-2:    #b8b8c0;
        --text-muted-3:        #a0a0a8;
        --text-muted-2:        #9a9aa3;
        --text-muted:          #9a9aa3;
        --text-meta:           #7a7a82;
        --text-faint:          #6a6a72;
        --text-black:          #f3f3f7;
        --text-success-bright: #4ad94a;
        --text-error-bright:   #ff6464;

        --border-default:           #3a3a44;
        --border-strong:            #4a4a55;
        --border-soft:              #2f2f38;
        --border-pale:              #2f2f38;
        --border-pre:               #2f2f38;
        --border-diff:              #2f2f38;
        --border-tool-rollup:       #3a3a44;
        --border-tool-rollup-2:     #3a3a44;
        --border-op-msg:            #3a3a44;
        --border-op-msg-soft:       #3a3a44;
        --border-file-event:        #3a4a5c;
        --border-question:          #3a4a5c;
        --border-question-hover:    #5a7ab5;

        --accent:                #3b82f6;
        --accent-hover:          #60a5fa;
        --accent-hover-2:        #60a5fa;
        --accent-disabled:       #2a3a55;
        --accent-deep:           #9bb8e0;
        --accent-deep-2:         #9bb8e0;
        --accent-deep-3:         #c0d0e8;
        --accent-op-msg:         #a8c4e8;

        --ok:               #4ad94a;
        --ok-strong:        #5ee05e;
        --ok-strong-2:      #5ee05e;
        --ok-status:        #4ad96a;
        --ok-deep:          #a0e8a0;
        --ok-deep-2:        #a0e8a0;
        --ok-soft:          #3a6b3a;
        --ok-medium:        #4a8a4a;
        --ok-pale:          #2a4a2a;
        --ok-pale-2:        #2a4a2a;
        --ok-pale-3:        #2a4a2a;
        --ok-text-diff:     #a0e8a0;
        --ok-rollup-text:   #6ee06e;

        --danger:            #ef4444;
        --danger-deep:       #ff6464;
        --danger-text:       #ff8585;
        --danger-hover:      #dc2626;
        --danger-text-deep:  #ff9090;
        --danger-rollup-text:#ff9090;
        --danger-text-diff:  #ff9090;
        --danger-border:     #6b2a2a;
        --danger-border-soft:#5a2424;
        --danger-border-soft-2:#5a2424;
        --code-inline-text:  #ff7a9a;

        --warn-border:          #6b5a14;
        --warn-text:            #f0d878;
        --warn-status:          #f0d878;
        --warn-mention-border:  #f0c000;

        --shadow-row-hover:        rgba(255, 255, 255, 0.04);
        --shadow-code-bg:          rgba(255, 255, 255, 0.06);
        --shadow-popover:          rgba(0, 0, 0, 0.5);
        --shadow-popover-2:        rgba(0, 0, 0, 0.5);
        --shadow-menu:             rgba(0, 0, 0, 0.6);
        --shadow-pill:             rgba(0, 0, 0, 0.6);
        --shadow-modal:            rgba(0, 0, 0, 0.7);
        --shadow-backdrop:         rgba(0, 0, 0, 0.6);
        --shadow-pip:              rgba(0, 0, 0, 0.6);
        --shadow-modal-backdrop:   rgba(0, 0, 0, 0.7);
        --task-recap-bg:           rgba(255, 255, 255, 0.04);
        --shadow-pip-focused:      rgba(96, 165, 250, 0.45);
      }
    }

    /* Theme toggle button — sits in the header next to Actions */
    .theme-toggle {
      flex-shrink: 0;
      width: 28px;
      height: 28px;
      padding: 0;
      background: transparent;
      border: 1px solid var(--border-strong);
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
      line-height: 1;
      color: var(--text-primary);
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
    .theme-toggle:hover { background: var(--bg-hover-pale); }

    * {
      box-sizing: border-box;
    }

    html, body { height: 100%; }
    body {
      margin: 0;
      padding: 0;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
      background: var(--bg-surface);
      color: var(--text-primary);
      display: flex;
      /* 100vh on iOS Safari includes the URL bar and bottom nav even
         when they're rendered, pushing the input area below the
         visible viewport. 100dvh tracks the dynamic viewport and
         shrinks when the keyboard / URL bar is shown. Fallback to
         100vh on browsers that don't support dvh (rare in 2025). */
      height: 100vh;
      height: 100dvh;
      overflow: hidden;
    }

    nav {
      width: var(--nav-width, 240px);
      background: var(--bg-sidebar);
      border-right: 1px solid var(--border-default);
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      padding: 0;
      margin: 0;
      flex-shrink: 0;
      /* Width transition disabled while the user is dragging the
         resizer (body.nav-resizing) so the rail tracks the cursor
         in real time. Collapse + restore still animate. */
      transition: width 0.18s ease, border-right-color 0.18s ease;
    }
    body.nav-resizing nav,
    body.nav-resizing .nav-divider-toggle {
      transition: none;
    }

    /* Drag handle on the nav's right edge. Lets the operator widen
       the sidebar to fit long routing names / hostnames / chip rows
       that overflow at the default 240px. Persists to localStorage.
       Hidden on mobile (off-canvas nav) and when the sidebar is
       collapsed. */
    .nav-resizer {
      position: fixed;
      top: 0;
      bottom: 0;
      left: var(--nav-width, 240px);
      width: 6px;
      margin-left: -3px;            /* center on the nav's right border */
      cursor: col-resize;
      background: transparent;
      z-index: 6;                    /* above nav, below the chevron toggle */
      transition: background 0.12s ease;
    }
    .nav-resizer:hover,
    body.nav-resizing .nav-resizer {
      background: var(--accent, #0066cc);
      opacity: 0.35;
    }
    /* Desktop collapse — `body.nav-collapsed` hides the rail in-flow
       so chat reclaims the freed width. Mobile uses the off-canvas
       `nav-open` mechanism instead (see the media query below) so
       this rule is scoped to the desktop breakpoint. The hamburger
       in the main header restores it. */
    @media (min-width: 721px) {
      body.nav-collapsed nav {
        width: 0;
        border-right-width: 0;
        overflow: hidden;
      }
    }

    .nav-header {
      padding: 16px;
      border-bottom: 1px solid var(--border-default);
      font-weight: 600;
      font-size: 14px;
      color: var(--text-primary);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
    }
    /* Mobile-only in-nav close button — desktop uses the chevron
       toggle on the divider instead, so this stays hidden above the
       breakpoint. Touch target stays at 28px for thumb taps. */
    .nav-close {
      display: none;
      width: 28px;
      height: 28px;
      padding: 0;
      background: transparent;
      color: var(--text-muted-3);
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 22px;
      line-height: 1;
    }
    .nav-close:hover { background: var(--bg-hover-grey-1); color: var(--text-primary); }

    /* User bar at the bottom of the sidebar — shows the signed-in
       login + a "log out" button that clears the token and re-flows
       OAuth. mt-auto pushes it to the bottom regardless of how many
       sessions are listed. */
    .user-bar {
      margin-top: auto;
      padding: 12px 16px;
      border-top: 1px solid var(--border-default);
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
      background: var(--bg-user-bar);
    }
    .user-bar-login {
      flex: 1;
      color: var(--text-secondary);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .user-bar-logout {
      padding: 4px 10px;
      background: var(--bg-surface);
      border: 1px solid var(--border-soft);
      border-radius: 3px;
      cursor: pointer;
      font-size: 12px;
      color: var(--text-primary);
    }
    .user-bar-logout:hover { background: var(--bg-hover-pale); }

    .channels-list {
      flex: 1;
      overflow-y: auto;
      list-style: none;
      padding: 8px;
      margin: 0;
    }

    /* People section — secondary nav block under the Sessions list.
       Hydrated by loadPresence() against the currently-selected
       session. Shows the member roster (owner + sharees) with a
       presence dot reflecting whose /cli WS is currently attached. */
    .nav-section-header {
      padding: 12px 16px 6px;
      border-top: 1px solid var(--border-default);
      font-weight: 600;
      font-size: 11px;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: var(--text-muted);
    }
    .people-list {
      flex: 0 1 auto;
      max-height: 35vh;
      overflow-y: auto;
      list-style: none;
      padding: 0 8px 8px;
      margin: 0;
    }
    .person {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 8px;
      border-radius: 4px;
      font-size: 12px;
      color: var(--text-secondary-2);
    }
    .person:hover { background: var(--bg-hover-grey-2); }
    .person.online .person-dot {
      background: var(--ok);
      box-shadow: 0 0 0 2px var(--bg-presence-ring);
    }
    .person.offline .person-dot { background: var(--border-soft); }
    .person.offline { color: var(--text-muted); }
    .person-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .person-login { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .person-role  { font-size: 10px; color: var(--text-meta); text-transform: uppercase; letter-spacing: 0.04em; }
    .people-empty {
      padding: 8px 12px;
      font-size: 12px;
      color: var(--text-meta);
      font-style: italic;
      list-style: none;
    }

    .channel-item {
      padding: 8px 12px;
      margin: 4px 0;
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      color: var(--text-primary);
      transition: background-color 0.15s;
      word-break: break-word;
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .channel-content { flex: 1; min-width: 0; }

    /* Per-row 👁 PIP toggle — opens / closes a screenshot PIP for
       this session without changing the chat selection. Always-visible
       when this row already has an open PIP (so the close affordance
       is discoverable); hover-only otherwise to keep idle rows quiet.
       Only rendered for managed sessions whose daemon is online. */
    .session-pip-toggle {
      visibility: hidden;
      flex-shrink: 0;
      width: 22px;
      height: 22px;
      padding: 0;
      background: transparent;
      border: none;
      color: var(--text-muted);
      cursor: pointer;
      font-size: 13px;
      line-height: 1;
      border-radius: 3px;
    }
    .channel-item:hover .session-pip-toggle    { visibility: visible; }
    .session-pip-toggle.session-pip-toggle-open {
      visibility: visible;
      color: var(--accent-pip-focused-border);
    }
    .session-pip-toggle:hover { background: var(--bg-hover-pale); color: var(--text-primary); }

    .channel-item:hover {
      background: var(--bg-hover-grey-1);
    }

    /* Header action buttons (share + purge) — owner-only, surfaced
       only when a session is selected and the operator owns it. The
       PURGE pill used to live on every row of the sessions list; it
       was loud, easy to mis-click, and duplicated per row. Promoted
       to the active-session header so it's a single, deliberate
       click. The share button sits next to it for symmetry. */
    .header-action {
      padding: 4px 10px;
      border: 1px solid var(--border-strong);
      border-radius: 4px;
      background: var(--bg-surface);
      color: var(--text-primary);
      font-size: 12px;
      cursor: pointer;
      flex-shrink: 0;
    }
    .header-action:hover { background: var(--bg-hover-pale); }
    .header-action[hidden] { display: none; }
    /* All session-management actions live in a single Actions
       dropdown on every viewport — desktop AND mobile use the same
       UI so there's one menu to maintain. The inbound pill stays
       visible on both since it's informational rather than a
       destructive verb. */
    .desktop-only { display: inline-block; }
    .mobile-only  { display: none; }
    @media (max-width: 720px) {
      .desktop-only { display: none !important; }
      .mobile-only  { display: inline-block; }
    }
    /* Section separator inside the Actions menu — labels the
       MANAGED group of verbs (Screenshot, Restart, Kill, Delete)
       which are only meaningful when the session is daemon-managed. */
    .actions-menu-separator {
      padding: 8px 14px 4px;
      font-size: 9px;
      letter-spacing: 0.08em;
      color: var(--text-faint);
      font-weight: 600;
      border-top: 1px solid var(--bg-hover-pale);
      margin-top: 4px;
      user-select: none;
    }
    .actions-menu-separator[hidden] { display: none; }
    .actions-wrap { position: relative; }
    .actions-menu {
      position: absolute;
      top: calc(100% + 4px);
      right: 0;
      min-width: 160px;
      background: var(--bg-surface);
      border: 1px solid var(--border-strong);
      border-radius: 4px;
      box-shadow: 0 4px 12px var(--shadow-menu);
      /* Above the floating PIP (z-index 40) so an operator who drags
         the PIP up to the top-right doesn't lose the menu behind it.
         Still below modals (z-index 50). */
      z-index: 45;
      padding: 4px 0;
    }
    .actions-menu[hidden] { display: none; }
    .actions-menu-item {
      display: block;
      width: 100%;
      text-align: left;
      padding: 8px 14px;
      background: transparent;
      border: none;
      cursor: pointer;
      font-size: 14px;
      color: var(--text-primary);
      font-family: inherit;
    }
    .actions-menu-item:hover { background: var(--bg-user-bar); }
    .actions-menu-item.danger { color: var(--danger); font-weight: 600; letter-spacing: 0.06em; }
    .actions-menu-item.danger:hover { background: var(--bg-danger-hover); }
    .actions-menu-item[hidden] { display: none; }

    .inbound-pill {
      font-size: 11px;
      font-family: ui-monospace, 'SF Mono', monospace;
      color: var(--text-secondary-2);
      background: var(--bg-tool-rollup);
      border-color: var(--border-tool-rollup);
    }
    .inbound-pill:hover { background: var(--bg-tool-rollup-hover); }
    @media (max-width: 720px) {
      /* On mobile the pill compresses to icon-only — dispatch text
         is hidden via #inboundPillText[hidden] in JS, but a CSS guard
         here gives a tighter hit-target regardless. */
      .inbound-pill { padding: 4px 8px; }
      .inbound-pill #inboundPillText { display: none; }
    }
    .header-action.danger {
      background: var(--danger);
      color: var(--bg-surface);
      border-color: var(--danger-deep);
      font-weight: 700;
      letter-spacing: 0.06em;
    }
    .header-action.danger:hover { background: var(--danger-hover); }

    .channel-item.active {
      background: var(--accent);
      color: var(--bg-surface);
      font-weight: 500;
    }

    /* Unread badge — pulsing red dot on a non-active session that
       got a new event from the cross-poll. Cleared the moment the
       operator selects that session. */
    .channel-item.unread {
      font-weight: 600;
      color: var(--danger-text);
    }
    .channel-item.unread:hover { background: var(--bg-unread-hover); }
    .agent-chip {
      display: inline-block;
      margin-left: 4px;
      font-size: 11px;
      vertical-align: middle;
      cursor: help;
    }
    .managed-chip {
      display: inline-block;
      margin-left: 4px;
      font-size: 10px;
      vertical-align: middle;
      cursor: help;
      opacity: 1;
    }
    .managed-chip.offline { opacity: 0.4; }

    /* +Session button in the sidebar header */
    .nav-new-session {
      padding: 2px 8px;
      background: var(--accent);
      color: var(--bg-surface);
      border: none;
      border-radius: 3px;
      font-size: 11px;
      font-weight: 500;
      cursor: pointer;
    }
    .nav-new-session:hover { background: var(--accent-hover); }
    .nav-new-session[hidden] { display: none; }

    /* + Folder button — same shape as + Session but outlined, so the
       primary "create a real session" action stays visually dominant
       over the secondary "organize" action. */
    .nav-new-folder {
      padding: 2px 8px;
      background: transparent;
      color: var(--text-secondary);
      border: 1px solid var(--border-default);
      border-radius: 3px;
      font-size: 11px;
      font-weight: 500;
      cursor: pointer;
      margin-left: auto;
    }
    .nav-new-folder:hover {
      background: var(--bg-hover-grey-1);
      color: var(--text-primary);
    }
    .nav-new-folder[hidden] { display: none; }

    /* === Session folders (desktop-only, client-side) ============== */

    /* Folder header row — collapsible group label in the sessions
       list. The "Uncategorized" pseudo-folder reuses this with the
       -uncat modifier (no disclosure triangle, no actions menu). */
    .folder-header {
      display: flex;
      align-items: center;
      gap: 4px;
      padding: 6px 8px 4px;
      margin: 6px 0 2px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.03em;
      text-transform: uppercase;
      color: var(--text-muted);
      cursor: pointer;
      user-select: none;
    }
    .folder-header-uncat { cursor: default; }
    .folder-header:hover:not(.folder-header-uncat) { color: var(--text-secondary); }
    .folder-disclosure { width: 12px; text-align: center; flex-shrink: 0; }
    .folder-name {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .folder-count { color: var(--text-muted); font-weight: 500; }
    .folder-menu-btn {
      margin-left: auto;
      background: transparent;
      border: none;
      color: var(--text-muted);
      cursor: pointer;
      font-size: 13px;
      line-height: 1;
      padding: 0 4px;
      visibility: hidden;
    }
    .folder-header:hover .folder-menu-btn { visibility: visible; }
    .folder-menu-btn:hover { color: var(--text-primary); }

    /* Per-row folder-move affordance — appears on row hover. */
    .session-move-btn {
      background: transparent;
      border: none;
      color: var(--text-muted);
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      padding: 0 2px;
      visibility: hidden;
    }
    .channel-item:hover .session-move-btn { visibility: visible; }
    .session-move-btn:hover { color: var(--text-primary); }

    /* Folder popovers — "Move to…" picker and the folder ⋮ menu.
       Appended to <body>, fixed-positioned by JS near their anchor. */
    .folder-popover {
      position: fixed;
      z-index: 1000;
      list-style: none;
      margin: 0;
      padding: 4px 0;
      min-width: 160px;
      background: var(--bg-surface);
      border: 1px solid var(--border-default);
      border-radius: 5px;
      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
      font-size: 12px;
    }
    .folder-popover .fp-title {
      padding: 4px 12px;
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      color: var(--text-muted);
    }
    .folder-popover .fp-item {
      padding: 6px 12px;
      cursor: pointer;
      color: var(--text-primary);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .folder-popover .fp-item:hover { background: var(--bg-hover-grey-1); }
    .folder-popover .fp-current { font-weight: 600; }
    .folder-popover .fp-current::before { content: '• '; }
    .folder-popover .fp-new { color: var(--accent); }
    .folder-popover .fp-danger { color: var(--danger-text); }
    .folder-popover .fp-sep {
      height: 1px;
      margin: 4px 0;
      padding: 0;
      background: var(--border-default);
      cursor: default;
    }

    /* Desktops sidebar group */
    .desktops-list {
      flex: 0 1 auto;
      max-height: 25vh;
      overflow-y: auto;
      list-style: none;
      padding: 0 8px 8px;
      margin: 0;
    }
    .desktop-row {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 4px 8px;
      border-radius: 3px;
      font-size: 12px;
      color: var(--text-secondary-2);
    }
    .desktop-row:hover { background: var(--bg-hover-grey-2); }
    .desktop-row.online .desktop-dot   { background: var(--ok); box-shadow: 0 0 0 2px var(--bg-presence-ring); }
    .desktop-row.offline .desktop-dot  { background: var(--border-soft); }
    .desktop-row.offline               { color: var(--text-muted); }
    .desktop-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .desktop-host {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .desktop-version {
      font-size: 10px;
      color: var(--text-meta);
      font-family: ui-monospace, monospace;
    }
    /* Owner attribution on a desktop shared WITH the caller. */
    .desktop-shared-by {
      font-size: 10px;
      color: var(--text-muted);
      white-space: nowrap;
    }
    /* 👥 share button on an OWNED desktop row. */
    .desktop-share-btn {
      flex-shrink: 0;
      padding: 0 4px;
      background: transparent;
      border: none;
      cursor: pointer;
      font-size: 12px;
      line-height: 1;
      opacity: 0.55;
    }
    .desktop-share-btn:hover { opacity: 1; }
    /* Per-sharee live-session count in the desktop-share modal. */
    .share-row-count {
      font-size: 10px;
      color: var(--text-meta);
      white-space: nowrap;
    }
    .desktops-empty {
      padding: 4px 8px;
      list-style: none;
    }
    /* Sibling of .desktops-empty for when at least one desktop is
       already registered. Same layout, just smaller margin so it
       sits closer to the existing desktop rows above it. The button
       label adapts (renderDesktopsList in js/render/desktops.js):
       "Set up supervisor →" when empty, "+ Set up another
       supervisor" when at least one desktop exists. */
    .desktops-add {
      padding: 6px 8px 4px 8px;
      list-style: none;
    }
    /* "Set up supervisor →" entry point — surfaced when zero desktops
       are registered (.desktops-empty) AND when one or more are
       (.desktops-add) so operators can add another machine without
       hunting for the wizard. Borderless link-button blends into
       the sidebar. */
    .desktops-setup-link {
      width: 100%;
      padding: 6px 8px;
      background: transparent;
      border: 1px dashed var(--border-default);
      color: var(--text-secondary);
      font-size: 12px;
      cursor: pointer;
      border-radius: 4px;
      text-align: left;
      font-family: inherit;
    }
    .desktops-setup-link:hover {
      background: var(--bg-hover-pale);
      color: var(--text-primary);
      border-color: var(--border-strong);
    }

    /* === Setup-supervisor wizard ============================== */
    /* Multi-step modal triggered from the sidebar "Set up supervisor"
       link. Reuses the .modal / .modal-backdrop scaffold; adds a
       stepper + platform tiles + command-row + verify-grid. */
    .supv-stepper {
      display: flex;
      align-items: center;
      gap: 4px;
      flex-wrap: wrap;
      padding: 10px 16px 12px;
      border-bottom: 1px solid var(--bg-hover-pale);
      font-size: 11px;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      color: var(--text-muted);
    }
    .supv-step             { color: var(--text-muted); }
    .supv-step.active      { color: var(--text-primary); font-weight: 600; }
    .supv-step.done        { color: var(--accent-pip-focused-border); }
    .supv-step-mark        { display: inline-block; min-width: 12px; margin-right: 2px; }
    .supv-step-sep         { color: var(--text-faint); margin: 0 4px; }

    .supv-prose            { margin: 6px 0; font-size: 13px; color: var(--text-secondary); line-height: 1.5; }

    .supv-platform-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 10px;
    }
    .supv-platform {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 6px;
      padding: 14px 10px;
      border: 1px solid var(--border-default);
      border-radius: 6px;
      text-decoration: none;
      color: var(--text-primary);
      background: var(--bg-surface);
      cursor: pointer;
      transition: background 0.12s ease, border-color 0.12s ease;
    }
    .supv-platform:hover {
      background: var(--bg-hover-pale);
      border-color: var(--border-strong);
    }
    .supv-platform-name { font-size: 13px; font-weight: 600; }
    .supv-platform-cta  { font-size: 11px; color: var(--text-secondary); text-decoration: underline; }
    /* Currently-selected platform (mirrors `_os`) — drives the
       commands shown in steps 2 + 3. Filled accent border so the
       operator's pick is obvious. */
    .supv-platform-active {
      border-color: var(--accent-pip-focused-border);
      box-shadow: 0 0 0 1px var(--accent-pip-focused-border) inset;
    }

    /* Per-step OS selector — three-tab pill rendered above each
       command. Lets the operator switch OS context mid-wizard. */
    .supv-os-selector {
      display: inline-flex;
      gap: 0;
      margin-bottom: 8px;
      border: 1px solid var(--border-default);
      border-radius: 4px;
      overflow: hidden;
      background: var(--bg-surface);
    }
    .supv-os-tab {
      padding: 4px 12px;
      background: transparent;
      border: none;
      border-right: 1px solid var(--border-default);
      color: var(--text-secondary);
      font-size: 11px;
      font-family: inherit;
      cursor: pointer;
      letter-spacing: 0.02em;
    }
    .supv-os-tab:last-child { border-right: none; }
    .supv-os-tab:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    .supv-os-tab-active {
      background: var(--accent-pip-focused-border);
      color: var(--bg-surface);
      font-weight: 600;
    }
    .supv-os-tab-active:hover {
      background: var(--accent-pip-focused-border);
      color: var(--bg-surface);
    }

    .supv-cmd-row {
      display: flex;
      align-items: stretch;
      gap: 6px;
    }
    .supv-cmd {
      flex: 1;
      padding: 8px 12px;
      background: var(--pip-bg);
      color: var(--pip-fg);
      border: 1px solid var(--pip-border);
      border-radius: 4px;
      font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
      font-size: 12px;
      white-space: nowrap;
      overflow-x: auto;
    }

    .supv-confirm-row .publish-checkbox {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px;
    }

    .supv-actions {
      display: flex;
      gap: 8px;
      justify-content: flex-end;
    }
    .supv-actions [data-supv-skip],
    .supv-actions [data-supv-back],
    .supv-actions [data-supv-done] {
      margin-right: auto;
    }

    .supv-detect {
      padding: 10px 12px;
      border-radius: 4px;
      font-size: 12px;
      line-height: 1.5;
      border: 1px solid transparent;
    }
    .supv-detect-pending {
      background: var(--bg-hover-pale);
      color: var(--text-secondary);
      border-color: var(--border-soft);
    }
    .supv-detect-ok {
      background: var(--bg-hover-pale);
      color: var(--accent-pip-focused-border);
      border-color: var(--accent-pip-focused-border);
    }
    .supv-detect-warn {
      background: var(--bg-hover-pale);
      color: var(--text-primary);
      border-color: var(--border-strong);
    }
    .supv-detect-warn .supv-detect-line { font-weight: 600; margin-bottom: 6px; }
    .supv-detect-hints { margin: 6px 0 10px 18px; padding: 0; }
    .supv-detect-hints li { margin: 3px 0; }
    .supv-detect-idle { color: var(--text-muted); font-style: italic; }

    .supv-verify-grid {
      display: grid;
      grid-template-columns: 110px 1fr;
      gap: 6px 12px;
      font-size: 13px;
    }
    .supv-verify-grid code {
      font-size: 12px;
      background: var(--bg-hover-pale);
      padding: 1px 6px;
      border-radius: 3px;
    }

    /* Terminal screenshot — desktop renders as a draggable PIP
       panel (no backdrop, no focus-trap), mobile renders as a
       full-screen modal (the existing modal-backdrop scaffold).
       The two share the inner .terminal-pre rendering; only the
       outer container differs.

       Desktop PIP:
         - position: fixed, default lower-right
         - transparent header with grab cursor for dragging
         - close (×) button
         - z-index above chat so it always floats on top

       Mobile (≤720px): falls through to the modal-backdrop styles
       which already handle the full-screen path. */
    .terminal-pre {
      margin: 0;
      padding: 10px 12px;
      background: var(--pip-bg);
      color: var(--pip-fg);
      font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
      font-size: 11px;
      line-height: 1.3;
      white-space: pre;
      overflow: auto;
      border-radius: 0 0 4px 4px;
      width: max-content;
      max-width: 100%;
      max-height: 70vh;
    }
    .terminal-meta {
      font-size: 10px;
      color: var(--text-muted);
      padding: 4px 10px;
      display: flex;
      justify-content: space-between;
      background: var(--pip-meta-bg);
      color: var(--text-faint);
      border-radius: 0 0 4px 4px;
    }
    .terminal-loading {
      padding: 24px;
      color: var(--text-muted);
      font-style: italic;
      text-align: center;
    }
    /* Shown when the daemon returned the legacy text-shape (no
       `lines` field — supervisor pre-0.2.14). Renders inline as a
       hint to upgrade rather than blanking the PIP entirely. */
    .terminal-stale {
      color: #d6b85a;
      font-style: italic;
      padding: 12px;
      display: inline-block;
    }

    /* PIP panel — desktop floating UI. */
    .pip-panel {
      position: fixed;
      bottom: 20px;
      right:  20px;
      background: var(--pip-bg);
      border: 1px solid var(--pip-border);
      border-radius: 6px;
      box-shadow: 0 8px 32px var(--shadow-pip);
      z-index: 40;                    /* above the chat, below modals */
      display: flex;
      flex-direction: column;
      max-width: calc(100vw - 16px);
      max-height: calc(100vh - 16px);
      user-select: none;              /* avoid text-selecting while dragging */
    }
    /* Focused PIP — when the operator has clicked the panel and
       their keystrokes are now being forwarded to the PTY. The
       header lights up so it's obvious where their typing is going. */
    .pip-panel.pip-focused { border-color: var(--accent-pip-focused-border); box-shadow: 0 0 0 2px var(--shadow-pip-focused), 0 8px 32px var(--shadow-pip); }
    .pip-panel.pip-focused .pip-header { background: var(--accent-pip-focused-bg); color: var(--accent-pip-focused-text); }
    .pip-panel[hidden] { display: none; }
    .pip-header {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px 8px 4px 10px;
      background: var(--pip-header-bg);
      color: var(--pip-fg);
      font-size: 11px;
      cursor: grab;
      border-radius: 6px 6px 0 0;
      border-bottom: 1px solid var(--pip-border);
    }
    .pip-header:active { cursor: grabbing; }
    .pip-title {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-family: ui-monospace, monospace;
    }
    .pip-close, .pip-minimize {
      background: transparent;
      border: none;
      color: var(--text-faint);
      cursor: pointer;
      font-size: 16px;
      line-height: 1;
      padding: 2px 6px;
      border-radius: 3px;
    }
    .pip-close:hover, .pip-minimize:hover { background: var(--pip-border); color: var(--bg-surface); }
    /* Minimize collapses the panel to its header strip — body is
       hidden but polling continues so a fresh frame is ready on
       restore. The min-content width prevents the header from
       widening to whatever the previous body's max-content width
       was; the title still ellipses cleanly. */
    .pip-panel.pip-minimized .pip-body  { display: none; }
    .pip-panel.pip-minimized            { width: min-content; }
    .pip-body {
      overflow: hidden;
      user-select: text;              /* let the operator copy text from the screen */
      position: relative;             /* anchor for the keyboard-target input */
    }
    /* Hidden focus target — invisible to the user but focusable so
       tapping the body brings up the mobile virtual keyboard. iOS
       Safari refuses 0×0 inputs, so we use 1×1 with opacity 0. */
    .pip-keyboard-target {
      position: absolute;
      left: 0;
      top: 0;
      width: 1px;
      height: 1px;
      opacity: 0;
      border: none;
      background: transparent;
      caret-color: transparent;
      outline: none;
      pointer-events: none;           /* taps go to .pip-body; we focus programmatically */
    }
    /* On mobile the PIP is always full-screen — single PIP at a time
       (JS gate prevents multi-PIP below 720px). The `!important` here
       overrides any inline left/top/right/bottom/z-index that the
       desktop multi-PIP path applied to this card before the viewport
       was resized down — without that, a desktop-positioned card
       would render half-off-screen on mobile. */
    @media (max-width: 720px) {
      .pip-panel {
        position: fixed     !important;
        inset:    0         !important;
        left:     0         !important;
        top:      0         !important;
        right:    auto      !important;
        bottom:   auto      !important;
        width:    100vw     !important;
        height:   100dvh    !important;
        max-width:  none    !important;
        max-height: none    !important;
        border-radius: 0;
        z-index:  40        !important;
      }
      .pip-header { border-radius: 0; cursor: default; }
      /* Sidebar 👁 toggle — same behavior on mobile (single-PIP).
         Force it always-visible so the operator can find the close
         affordance without a hover gesture that mobile lacks. */
      .session-pip-toggle { visibility: visible; }
      .terminal-pre { max-height: calc(100dvh - 80px); border-radius: 0; }
      /* Minimize is meaningless on mobile — the PIP already takes
         the full viewport. Hide the button so the header only
         carries the Close affordance. */
      .pip-minimize { display: none; }
      /* Suppress text selection on the body so a tap focuses the
         hidden input → keyboard, instead of launching iOS's text-
         selection UI which dismisses any focused input. Desktop
         keeps user-select:text for copy-paste; the affordance
         doesn't exist on mobile anyway (no system "copy" menu
         reachable without a long-press, which here would trigger
         the selection UI we're suppressing). */
      .pip-body, .terminal-pre {
        user-select: none;
        -webkit-user-select: none;
        -webkit-touch-callout: none;
      }
      /* Make the hidden focus-target physically tappable across the
         entire body. Programmatic input.focus() on iOS is finicky
         (gesture-window timing, viewport shifts, selection UI all
         conflict); a native tap that LANDS on an <input> is the one
         path Safari honors unconditionally. Visually still invisible
         (opacity 0, no caret), just promoted to pointer-events:auto
         and full-size. The terminal-pre underneath is non-interactive
         (user-select:none above + no other listeners), so absorbing
         taps here costs nothing. */
      .pip-keyboard-target {
        width:  100%;
        height: 100%;
        left:   0;
        top:    0;
        pointer-events: auto;
      }
    }
    .terminal-meta {
      font-size: 11px;
      color: var(--text-muted);
      margin-top: 6px;
      display: flex;
      justify-content: space-between;
    }
    .terminal-loading {
      padding: 24px;
      color: var(--text-muted);
      font-style: italic;
      text-align: center;
    }

    /* === PIP pinned-right dock (desktop) =======================
       When the operator clicks the pin (📌) button on ANY floating
       PIP, every open PIP becomes a tab in #pipDock — a column inside
       .content-row sandwiched between .chat-column and #detailsPane.
       The chat shrinks (flex: 1, min-width: 0) to make room. A tab
       strip at the top of the dock lets the operator switch which
       session's terminal is visible; the per-tab × closes that PIP
       (last tab close → dock hides); the strip-level ⇱ unpins all
       (back to floating cards).

       Width: content-driven via `width: max-content`. The terminal
       claims its full natural width (typically ~120 cols ≈ 800px at
       11px mono), and chat takes whatever's left. This is the
       operator's contract — never truncate the terminal horizontally
       when pinned.

       Mobile (≤720px): the dock is hidden via media query; pinning
       is a no-op below the breakpoint because the floating PIP is
       already full-screen there. */
    .pip-dock {
      width: max-content;
      max-width: none;
      flex-shrink: 0;
      border-left: 1px solid var(--border-default);
      background: var(--bg-surface);
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    .pip-dock[hidden] { display: none; }

    /* Tab strip — sits at the top of the dock. Tabs are horizontal,
       active one is filled, inactive ones are muted. Per-tab × is
       always-visible on the active tab and hover-only on inactives
       (less visual noise when 5+ tabs are open). The strip-level
       unpin button (⇱) sits at the right edge of the strip. */
    .pip-tab-strip {
      display: flex;
      align-items: stretch;
      gap: 1px;
      background: var(--pip-header-bg);
      border-bottom: 1px solid var(--pip-border);
      padding: 0;
      min-height: 28px;
      overflow: hidden;
    }
    .pip-tab-list {
      display: flex;
      flex: 1;
      min-width: 0;
      overflow-x: auto;
      overflow-y: hidden;
      gap: 1px;
    }
    .pip-tab {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 4px 6px 4px 10px;
      background: var(--pip-meta-bg);
      color: var(--text-muted);
      font-size: 11px;
      font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
      cursor: pointer;
      border-right: 1px solid var(--pip-border);
      max-width: 200px;
      flex-shrink: 0;
      user-select: none;
    }
    .pip-tab:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    .pip-tab-active {
      background: var(--pip-bg);
      color: var(--pip-fg);
      cursor: default;
    }
    .pip-tab-active:hover { background: var(--pip-bg); color: var(--pip-fg); }
    .pip-tab-label {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      flex: 1;
      min-width: 0;
    }
    .pip-tab-close {
      visibility: hidden;
      width: 18px;
      height: 18px;
      padding: 0;
      background: transparent;
      border: none;
      color: inherit;
      cursor: pointer;
      font-size: 14px;
      line-height: 1;
      border-radius: 3px;
      flex-shrink: 0;
    }
    /* × always visible on the active tab (most-likely-to-close target),
       hover-only on inactives — browser-tab convention. */
    .pip-tab-active .pip-tab-close { visibility: visible; }
    .pip-tab:hover .pip-tab-close   { visibility: visible; }
    .pip-tab-close:hover { background: var(--pip-border); }

    .pip-unpin-all {
      flex-shrink: 0;
      width: 32px;
      padding: 0;
      background: transparent;
      border: none;
      border-left: 1px solid var(--pip-border);
      color: var(--text-muted);
      cursor: pointer;
      font-size: 14px;
      line-height: 1;
    }
    .pip-unpin-all:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    /* Inside the dock, the PIP panel is in-flow (not floating).
       Strips position/shadow/border-radius so it looks like a column,
       not a card. Width follows the terminal-pre's natural max-content.

       z-index reset: the base `.pip-panel` rule sets `z-index: 40`
       (needed so the floating panel stacks above chat). As a flex
       item, a non-auto z-index creates a stacking context REGARDLESS
       of `position: static`, which would put the pinned panel above
       the header's actions menu (z-index 30) and hide the dropdown
       when it opens over the dock. Forcing `z-index: auto` keeps the
       pinned panel in its parent's stacking context. */
    .pip-dock .pip-panel {
      position: static;
      z-index: auto;
      width: 100%;
      max-width: none;
      max-height: none;
      height: 100%;
      box-shadow: none;
      border: none;
      border-radius: 0;
      flex: 1;
    }
    .pip-dock .pip-panel.pip-focused {
      box-shadow: inset 0 0 0 2px var(--shadow-pip-focused);
      border-color: transparent;
    }
    .pip-dock .pip-header {
      cursor: default;        /* not draggable when pinned */
      border-radius: 0;
    }
    .pip-dock .pip-body {
      flex: 1;
      overflow: auto;
    }
    /* Inside the dock, the terminal-pre keeps its natural max-content
       width — the dock inherits that width via the chain. We DROP
       max-height so the pre fills the available vertical space and
       scrolls internally instead of overflowing the viewport. */
    .pip-dock .terminal-pre {
      width: max-content;
      max-width: none;
      max-height: none;
      flex: 1;
    }

    /* PIP header — pin button is the new affordance. It swaps icon
       and aria-label based on `[data-pip-mode]` so one element drives
       both pin→pinned and unpin→floating. */
    .pip-pin {
      background: transparent;
      border: none;
      color: var(--text-faint);
      cursor: pointer;
      font-size: 13px;
      line-height: 1;
      padding: 2px 6px;
      border-radius: 3px;
    }
    .pip-pin:hover { background: var(--pip-border); color: var(--bg-surface); }

    /* On mobile, the dock is meaningless (floating PIP is already
       full-screen). Force-hide it and the pin button. */
    @media (max-width: 720px) {
      .pip-dock { display: none !important; }
      .pip-pin  { display: none; }
    }

    /* Create-session modal */
    .create-session-help {
      font-size: 11px;
      color: var(--text-muted);
      line-height: 1.45;
    }
    .create-session-help a { color: var(--accent); }
    .create-flags-list {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .create-flag-row {
      display: flex;
      gap: 6px;
    }
    .create-flag-row input { flex: 1; min-width: 0; }
    .create-flag-add,
    .create-flag-remove {
      width: 28px;
      flex-shrink: 0;
      padding: 0;
      background: var(--bg-surface);
      border: 1px solid var(--border-soft);
      border-radius: 4px;
      cursor: pointer;
      font-size: 16px;
      line-height: 1;
      color: var(--text-primary);
      font-family: inherit;
    }
    .create-flag-add:hover,
    .create-flag-remove:hover { background: var(--bg-hover-pale); }
    .create-flag-remove { color: var(--danger); border-color: var(--danger); }
    .create-flag-remove:hover { background: var(--bg-danger-hover); }
    .channel-item .unread-dot {
      display: inline-block;
      width: 7px;
      height: 7px;
      margin-left: 6px;
      border-radius: 50%;
      background: var(--danger);
      vertical-align: middle;
      animation: busy-pulse 1.4s ease-in-out infinite;
    }

    .main-container {
      flex: 1;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    /* Chat + details share the area under the single .header. On
       desktop they sit side-by-side; the details pane is a fixed-
       width right column that opens/closes via the `hidden` attr on
       #detailsPane. On narrow screens it floats over the chat as a
       full-screen drawer instead of crowding the conversation. */
    .content-row {
      flex: 1;
      display: flex;
      flex-direction: row;
      overflow: hidden;
      min-height: 0;
    }
    .chat-column {
      flex: 1;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      min-width: 0;
    }
    .details-pane {
      width: 686px;
      flex-shrink: 0;
      border-left: 1px solid var(--border-default);
      background: var(--bg-surface);
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    .details-pane[hidden] { display: none; }

    .header {
      padding: 16px;
      border-bottom: 1px solid var(--border-default);
      background: var(--bg-header);
      font-weight: 600;
      font-size: 16px;
      color: var(--text-primary);
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .header-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Modal scaffolding for the share panel. Centered over the chat
       pane via a fixed-position backdrop. Body scroll is not locked —
       the modal is short and the operator can dismiss with Esc / X /
       backdrop click. */
    .modal-backdrop {
      position: fixed;
      inset: 0;
      background: var(--shadow-modal-backdrop);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 50;
    }
    .modal {
      background: var(--bg-surface);
      border-radius: 6px;
      width: min(440px, calc(100vw - 32px));
      max-height: calc(100vh - 64px);
      overflow-y: auto;
      box-shadow: 0 8px 32px var(--shadow-modal);
      padding: 0;
    }
    .modal-header {
      padding: 14px 16px;
      border-bottom: 1px solid var(--bg-hover-pale);
      display: flex;
      align-items: center;
      gap: 8px;
      font-weight: 600;
      font-size: 14px;
    }
    .modal-header-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .modal-close {
      width: 28px;
      height: 28px;
      padding: 0;
      background: transparent;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 22px;
      line-height: 1;
      color: var(--text-muted-3);
    }
    .modal-close:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    .modal-body { padding: 14px 16px; }
    .modal-section { margin-bottom: 16px; }
    .modal-section:last-child { margin-bottom: 0; }
    .modal-label {
      font-size: 11px;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: var(--text-muted);
      margin-bottom: 6px;
    }
    .share-add-row {
      display: flex;
      gap: 6px;
      align-items: stretch;
    }
    .share-add-row input,
    .share-add-row select {
      padding: 6px 8px;
      border: 1px solid var(--border-soft);
      border-radius: 4px;
      font-size: 13px;
      font-family: inherit;
    }
    .share-add-row input { flex: 1; min-width: 0; }
    .share-login-wrap { flex: 1; min-width: 0; position: relative; }
    .share-login-wrap input { width: 100%; }
    .share-gh-popover {
      position: absolute;
      top: calc(100% + 4px);
      left: 0;
      right: 0;
      max-height: 240px;
      overflow-y: auto;
      background: var(--bg-surface);
      border: 1px solid var(--border-strong);
      border-radius: 4px;
      box-shadow: 0 4px 12px var(--shadow-popover-2);
      z-index: 60;
      padding: 4px 0;
    }
    .share-gh-popover[hidden] { display: none; }
    .share-gh-item {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 10px;
      cursor: pointer;
      font-size: 13px;
      color: var(--text-primary);
    }
    .share-gh-item:hover,
    .share-gh-item.active { background: var(--bg-share-gh-hover); }
    .share-gh-avatar {
      width: 22px;
      height: 22px;
      border-radius: 50%;
      flex-shrink: 0;
      background: var(--bg-hover-pale);
    }
    .share-gh-login { font-weight: 500; }
    .share-gh-name  { color: var(--text-muted); font-size: 12px; }
    .share-gh-empty {
      padding: 8px 10px;
      color: var(--text-faint);
      font-style: italic;
      font-size: 12px;
    }
    .share-add-btn {
      padding: 6px 12px;
      background: var(--accent);
      color: var(--bg-surface);
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
    }
    .share-add-btn:hover { background: var(--accent-hover); }
    .share-add-btn:disabled { background: var(--accent-disabled); cursor: not-allowed; }

    .share-list {
      list-style: none;
      padding: 0;
      margin: 0;
    }
    .share-row {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 0;
      border-bottom: 1px solid var(--bg-user-bar);
      font-size: 13px;
    }
    .share-row:last-child { border-bottom: none; }
    .share-row-login { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .share-row-role {
      font-size: 11px;
      letter-spacing: 0.04em;
      color: var(--text-muted-3);
      text-transform: uppercase;
    }
    .share-row-revoke {
      padding: 2px 8px;
      background: var(--bg-surface);
      border: 1px solid var(--danger);
      color: var(--danger);
      border-radius: 3px;
      cursor: pointer;
      font-size: 11px;
    }
    .share-row-revoke:hover { background: var(--bg-danger-hover); }
    .share-empty { padding: 8px 0; color: var(--text-muted); font-style: italic; font-size: 12px; }
    .share-roles-help {
      font-size: 11px;
      color: var(--text-muted);
      line-height: 1.5;
    }
    .share-roles-help code {
      background: var(--bg-pill-soft);
      padding: 0 4px;
      border-radius: 2px;
      color: var(--text-secondary);
    }

    /* Publish / Unpublish modal — shares modal-shell styles, adds
       form-field affordances. publish-actions is a footer row; the
       Cancel button mirrors share's neutral styling, Submit picks up
       the same blue as share-add-btn. */
    .publish-field {
      width: 100%;
      padding: 6px 8px;
      border: 1px solid var(--border-soft);
      border-radius: 4px;
      font-size: 13px;
      font-family: inherit;
    }
    .publish-textarea {
      resize: vertical;
      min-height: 80px;
      font-family: ui-monospace, 'SF Mono', monospace;
    }
    .publish-hint {
      margin-top: 4px;
      font-size: 11px;
      color: var(--text-muted);
      line-height: 1.4;
    }
    .publish-hint code {
      background: var(--bg-pill-soft);
      padding: 0 4px;
      border-radius: 2px;
      color: var(--text-secondary);
    }
    .publish-checkbox {
      display: flex;
      align-items: flex-start;
      gap: 8px;
      font-size: 12px;
      color: var(--text-secondary-2);
      line-height: 1.45;
      cursor: pointer;
    }
    .publish-checkbox input { margin-top: 3px; flex-shrink: 0; }
    .publish-advanced summary {
      cursor: pointer;
      font-size: 11px;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: var(--text-muted);
      padding: 4px 0;
    }
    .publish-advanced[open] summary { color: var(--text-secondary); }
    .publish-advanced-row {
      display: flex;
      gap: 12px;
      margin-top: 8px;
    }
    .publish-advanced-row label { flex: 1; min-width: 0; }
    /* Live-view disclosure: warning-toned hint that appears when the
       live-view checkbox is checked. Yellow background to match the
       publish-form's other "you're about to do something with public
       consequences" callouts. */
    .live-view-disclosure {
      margin-top: 8px;
      padding: 8px 10px;
      background: var(--warn-bg, #fff8e0);
      color: var(--warn-fg, #8a6800);
      border-radius: 4px;
      font-size: 12px;
      line-height: 1.4;
    }
    /* Suggested-prompts repeater: stacked rows of one text input plus
       a small × remove button. Mirrors the look of publish-advanced
       rows so it visually belongs to the same form. */
    .publish-suggested-prompts-row {
      display: flex;
      gap: 6px;
      align-items: center;
      margin-top: 6px;
    }
    .publish-suggested-prompts-row .publish-field { flex: 1; }
    .publish-suggested-prompts-remove {
      width: 26px;
      height: 26px;
      padding: 0;
      background: transparent;
      border: 1px solid var(--border-soft);
      border-radius: 3px;
      color: var(--text-secondary);
      cursor: pointer;
      font-size: 14px;
      line-height: 1;
    }
    .publish-suggested-prompts-remove:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    .publish-suggested-prompts-add {
      margin-top: 8px;
      padding: 4px 10px;
      background: transparent;
      border: 1px dashed var(--border-soft);
      border-radius: 3px;
      color: var(--text-secondary);
      cursor: pointer;
      font-size: 12px;
    }
    .publish-suggested-prompts-add:hover:not(:disabled) {
      border-style: solid;
      color: var(--text-primary);
    }
    .publish-suggested-prompts-add:disabled { opacity: 0.5; cursor: not-allowed; }
    .publish-actions {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 4px;
    }
    .publish-cancel {
      padding: 6px 14px;
      background: var(--bg-surface);
      border: 1px solid var(--border-soft);
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      color: var(--text-primary);
    }
    .publish-cancel:hover { background: var(--bg-hover-pale); }
    .publish-submit {
      padding: 6px 14px;
      background: var(--accent);
      color: var(--bg-surface);
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
    }
    .publish-submit:hover { background: var(--accent-hover); }
    .publish-submit:disabled { background: var(--accent-disabled); cursor: not-allowed; }
    .publish-submit.danger { background: var(--danger); }
    .publish-submit.danger:hover { background: var(--danger-hover); }

    .unpublish-warning {
      font-size: 14px;
      font-weight: 500;
      color: var(--danger-text-deep);
    }
    .unpublish-bullets {
      margin: 0 0 12px 0;
      padding-left: 20px;
      font-size: 12px;
      color: var(--text-secondary);
      line-height: 1.55;
    }
    .unpublish-bullets li { margin-bottom: 2px; }

    /* Inbound stats modal — summary tiles, all-time block, top
       askers, recent table. */
    .inbound-window {
      display: flex;
      gap: 6px;
      margin-bottom: 16px;
    }
    .inbound-window-btn {
      padding: 4px 12px;
      background: var(--bg-surface);
      border: 1px solid var(--border-soft);
      border-radius: 999px;
      cursor: pointer;
      font-size: 11px;
      color: var(--text-secondary);
      letter-spacing: 0.04em;
    }
    .inbound-window-btn:hover { background: var(--bg-user-bar); }
    .inbound-window-btn.active {
      background: var(--accent);
      border-color: var(--accent);
      color: var(--bg-surface);
    }
    .inbound-tiles {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 8px;
      margin-bottom: 16px;
    }
    @media (max-width: 720px) {
      .inbound-tiles { grid-template-columns: repeat(2, 1fr); }
    }
    .inbound-tile {
      padding: 10px 8px;
      background: var(--bg-tool-rollup);
      border: 1px solid var(--border-tool-rollup-2);
      border-radius: 4px;
      text-align: center;
    }
    .inbound-tile-value {
      font-size: 20px;
      font-weight: 600;
      color: var(--text-strong);
      font-family: ui-monospace, 'SF Mono', monospace;
    }
    .inbound-tile-label {
      font-size: 10px;
      color: var(--text-muted);
      letter-spacing: 0.05em;
      text-transform: uppercase;
      margin-top: 2px;
    }
    .inbound-section-title {
      font-size: 11px;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--text-muted);
      font-weight: 600;
      margin-bottom: 6px;
    }
    .inbound-alltime {
      font-size: 12px;
      color: var(--text-secondary-2);
      line-height: 1.6;
      margin-bottom: 16px;
    }
    .inbound-alltime .at-limit { color: var(--danger-deep); font-weight: 600; }
    .inbound-askers, .inbound-recent {
      list-style: none;
      margin: 0 0 16px 0;
      padding: 0;
    }
    .inbound-asker-row {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px 0;
      font-size: 12px;
      border-bottom: 1px solid var(--bg-user-bar);
    }
    .inbound-asker-row:last-child { border-bottom: none; }
    .inbound-asker-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .inbound-asker-dot.recent { background: var(--ok); }
    .inbound-asker-dot.idle   { background: var(--border-soft); }
    .inbound-asker-login { flex: 1; color: var(--text-primary); }
    .inbound-asker-count { font-family: ui-monospace, 'SF Mono', monospace; color: var(--text-secondary); }
    .inbound-asker-time  { color: var(--text-muted); min-width: 60px; text-align: right; font-size: 11px; }

    .inbound-recent-row {
      display: grid;
      grid-template-columns: 14px 56px 1fr 56px 1fr;
      gap: 6px;
      padding: 3px 0;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 11px;
      color: var(--text-secondary-2);
      border-bottom: 1px solid var(--bg-sidebar);
      align-items: baseline;
    }
    .inbound-recent-row:last-child { border-bottom: none; }
    .inbound-recent-row.ok   .inbound-recent-icon { color: var(--ok); }
    .inbound-recent-row.fail .inbound-recent-icon { color: var(--danger-deep); }
    .inbound-recent-row.fail .inbound-recent-q { color: var(--danger-deep); }
    .inbound-recent-q {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .inbound-recent-asker {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: var(--text-secondary);
    }
    .inbound-empty {
      text-align: center;
      padding: 32px 16px;
      color: var(--text-muted);
    }
    .inbound-empty-icon { font-size: 36px; opacity: 0.6; margin-bottom: 12px; }
    .inbound-empty-handle {
      display: inline-block;
      margin: 6px 0;
      padding: 2px 8px;
      background: var(--bg-pill-soft);
      border-radius: 3px;
      font-family: ui-monospace, monospace;
      color: var(--text-primary);
    }
    .inbound-empty-cmd {
      display: block;
      margin: 8px auto;
      padding: 6px 10px;
      background: var(--bg-inbound-cmd);
      color: var(--accent-pip-banner-text);
      border-radius: 3px;
      font-family: ui-monospace, monospace;
      font-size: 11px;
      max-width: fit-content;
    }
    .inbound-loading {
      padding: 24px;
      text-align: center;
      color: var(--text-muted);
      font-style: italic;
    }

    /* Inline diff rendering for Edit / MultiEdit / Write tool calls.
       Sits inside the existing tool detail panel so it inherits the
       same expand/collapse plumbing. Code-style monospace, +/- gutter,
       light tinted backgrounds so red/green doesn't dominate the row. */
    .tool-diff {
      margin-top: 6px;
      border: 1px solid var(--border-diff);
      border-radius: 4px;
      background: var(--bg-diff);
      font-family: 'SFMono-Regular', Menlo, Consolas, monospace;
      font-size: 12px;
      overflow-x: auto;
    }
    .tool-diff-header {
      padding: 4px 8px;
      border-bottom: 1px solid var(--border-diff);
      font-size: 11px;
      color: var(--text-muted-3);
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .tool-diff-header-path { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-secondary-2); }
    .tool-diff-header-stats { flex-shrink: 0; }
    .tool-diff-header-stats .add { color: var(--ok-deep); }
    .tool-diff-header-stats .del { color: var(--danger-hover); }
    .tool-diff-edit-sep {
      padding: 3px 8px;
      font-size: 10px;
      color: var(--text-muted);
      background: var(--bg-diff-edit-sep);
      border-top: 1px dashed var(--border-diff);
      border-bottom: 1px dashed var(--border-diff);
      letter-spacing: 0.04em;
    }
    .tool-diff-line {
      display: block;
      padding: 0 8px;
      white-space: pre;
      line-height: 1.5;
    }
    .tool-diff-line.add { background: var(--bg-diff-add); color: var(--ok-text-diff); }
    .tool-diff-line.add::before { content: '+ '; color: var(--ok); }
    .tool-diff-line.del { background: var(--bg-diff-del); color: var(--danger-text-diff); }
    .tool-diff-line.del::before { content: '- '; color: var(--danger-hover); }
    .tool-diff-truncated {
      padding: 4px 8px;
      font-size: 11px;
      color: var(--text-muted-3);
      background: var(--bg-pill-soft);
      cursor: pointer;
      text-align: center;
    }
    .tool-diff-truncated:hover { background: var(--bg-hover-grey-1); }

    /* Busy indicator — ephemeral inline row appended to the bottom
       of the chat while CC is mid-turn. Pulled in/out by
       renderMessages based on the heartbeat state; not part of the
       persisted timeline. */
    .busy-row {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px 10px;
      color: var(--text-muted);
      font-size: 12px;
      font-style: italic;
    }
    .busy-row .busy-dot {
      width: 9px;
      height: 9px;
      border-radius: 50%;
      background: var(--danger);
      flex-shrink: 0;
      animation: busy-pulse 1s ease-in-out infinite;
    }
    @keyframes busy-pulse {
      0%, 100% { opacity: 0.35; transform: scale(0.85); }
      50%      { opacity: 1;    transform: scale(1.15); }
    }

    /* Top-of-scroll marker — appears as the operator scrolls toward
       the top of the chat. Two states: "loading older…" (with a
       reusing busy-dot) or "— start of history —" (no dot, dimmer). */
    .history-top-row {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      padding: 8px 10px;
      color: var(--text-muted);
      font-size: 11px;
      font-style: italic;
    }
    .history-top-row .busy-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: var(--text-muted);
      animation: busy-pulse 1.2s ease-in-out infinite;
    }
    .history-top-row.history-exhausted {
      color: var(--text-faint);
      letter-spacing: 0.05em;
    }

    /* Mobile-only hamburger — opens the off-canvas overlay. Desktop
       uses the chevron divider toggle below instead, so this stays
       hidden above the breakpoint. */
    .nav-toggle {
      display: none;
      padding: 4px 10px;
      background: transparent;
      color: var(--text-primary);
      border: 1px solid var(--border-strong);
      border-radius: 4px;
      cursor: pointer;
      font-size: 18px;
      line-height: 1;
    }
    .nav-toggle:hover { background: var(--bg-hover-pale); }

    /* Chevron toggle on the nav/chat divider (desktop only). Replaces
       the `×` + `☰` pair — one affordance for collapse + restore,
       always visible at the divider's vertical midpoint. Points `‹`
       when the nav is expanded (clicking collapses) and `›` when
       collapsed (clicking expands). Driven via `body.nav-collapsed`
       to keep state + visual in one place. */
    .nav-divider-toggle {
      display: none;                /* hidden on mobile (overridden in min-width MQ) */
      position: fixed;
      left: var(--nav-width, 240px); /* tracks the resizable nav width */
      top: 50%;
      transform: translate(-50%, -50%);
      width: 18px;
      height: 36px;
      padding: 0;
      background: var(--bg-surface);
      color: var(--text-secondary);
      border: 1px solid var(--border-default);
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      line-height: 1;
      z-index: 20;
      transition: left 0.18s ease, background 0.15s ease, color 0.15s ease;
    }
    .nav-divider-toggle:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    /* Collapsed: chevron sits flush at the viewport's left edge as a
       peek-tab; transform drops the negative-X offset so the whole
       button stays on-screen. */
    body.nav-collapsed .nav-divider-toggle {
      left: 0;
      transform: translate(0, -50%);
      border-left: none;
      border-radius: 0 4px 4px 0;
    }
    .nav-divider-toggle-icon {
      display: inline-block;
      line-height: 1;
      transition: transform 0.18s ease;
    }
    body.nav-collapsed .nav-divider-toggle-icon { transform: rotate(180deg); }

    @media (min-width: 721px) {
      .nav-divider-toggle { display: inline-flex; align-items: center; justify-content: center; }
    }

    /* Resizer mirrors the divider-toggle's visibility: desktop-only,
       hidden when the nav is collapsed (no width to drag). */
    .nav-resizer { display: none; }
    @media (min-width: 721px) {
      .nav-resizer { display: block; }
      body.nav-collapsed .nav-resizer { display: none; }
    }

    /* Click-anywhere-else dismiss for the mobile nav overlay. */
    .nav-backdrop {
      display: none;
      position: fixed;
      inset: 0;
      background: var(--shadow-backdrop);
      z-index: 9;
    }

    /* Empty-state placeholder for the main pane before a session is
       selected. Replaces the "Loading sessions..." text once /sessions
       has resolved. */
    .empty-state {
      flex: 1;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 12px;
      color: var(--text-muted);
      font-size: 14px;
      text-align: center;
      padding: 24px;
    }
    .empty-state-icon { font-size: 36px; opacity: 0.6; }
    .empty-state-hint { font-size: 12px; opacity: 0.8; }

    main {
      flex: 1;
      overflow-y: auto;
      overflow-x: hidden;       /* row content scrolls inside .message-content, not the page */
      padding: 16px;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .message {
      display: flex;
      gap: 8px;
      margin-bottom: 4px;
    }

    .message-author {
      font-weight: 600;
      font-size: 13px;
      color: var(--accent);
      min-width: 80px;
    }

    .message-time {
      font-size: 11px;
      color: var(--text-meta);
      min-width: 80px;
      display: flex;
      flex-direction: column;
      gap: 2px;
      align-items: flex-start;
    }

    .message-content {
      flex: 1;
      min-width: 0;            /* allow flex shrink so overflow-x kicks in */
      max-width: 100%;
      overflow-x: auto;        /* per-row horizontal scroll for long content */
      font-size: 13px;
      color: var(--text-primary);
      /* No `white-space: pre-wrap` — markdown rendering produces real
         block elements (<p>, <pre>, <ul>, etc.) that handle their own
         line breaks. marked is configured with `breaks: true` so a
         single newline outside a block becomes <br>, matching the
         pre-markdown plain-text behavior.
         No `word-wrap: break-word` either — keep formatting intact;
         a long URL or sha or code block scrolls inside its row
         instead of wrapping mid-token. The parent is min-width:0 so
         it shrinks rather than blowing out the chat width. */
    }
    /* Flex parents need min-width:0 too — otherwise their flex
       children's intrinsic widths (long words, code blocks) ratchet
       up the entire row and break the per-row scroll model. */
    .message-wrapper { min-width: 0; }
    .message         { min-width: 0; max-width: 100%; }
    /* === Markdown content styles ============================== */
    .message-content > :first-child { margin-top: 0; }
    .message-content > :last-child  { margin-bottom: 0; }
    .message-content p { margin: 0 0 6px; }
    .message-content ul, .message-content ol { margin: 4px 0; padding-left: 22px; }
    .message-content li { margin: 2px 0; }
    .message-content blockquote {
      margin: 4px 0;
      padding: 4px 10px;
      border-left: 3px solid var(--border-default);
      color: var(--text-muted-3);
      background: var(--bg-row-quiet);
    }
    .message-content a { color: var(--accent); text-decoration: none; }
    .message-content a:hover { text-decoration: underline; }
    .message-content code {
      background: var(--bg-code-inline);
      padding: 1px 4px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      color: var(--code-inline-text);
    }
    .message-content pre {
      background: var(--bg-code-inline);
      border: 1px solid var(--bg-hover-grey-1);
      border-radius: 4px;
      padding: 8px 10px;
      margin: 6px 0;
      overflow-x: auto;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      line-height: 1.5;
    }
    .message-content pre code {
      background: transparent;
      padding: 0;
      color: var(--text-primary);
    }
    .message-content h1, .message-content h2, .message-content h3,
    .message-content h4, .message-content h5, .message-content h6 {
      margin: 8px 0 4px;
      font-weight: 600;
      line-height: 1.25;
    }
    .message-content h1 { font-size: 16px; }
    .message-content h2 { font-size: 15px; }
    .message-content h3 { font-size: 14px; }
    .message-content h4, .message-content h5, .message-content h6 { font-size: 13px; }
    .message-content table { border-collapse: collapse; margin: 6px 0; font-size: 12px; }
    .message-content th, .message-content td { border: 1px solid var(--border-default); padding: 4px 8px; }
    .message-content th { background: var(--bg-sidebar); font-weight: 600; }
    .message-content hr { border: none; border-top: 1px solid var(--border-default); margin: 8px 0; }

    .message-wrapper {
      display: flex;
      gap: 8px;
    }

    .mention {
      background: var(--bg-mention);
      padding: 1px 2px;
      border-radius: 2px;
      font-weight: 500;
    }

    /* Permission gate bubbles — inline lifecycle markers so the
       operator sees CC tool requests + their resolutions interleaved
       with chat without a separate panel. Color-coded by status:
       yellow = pending request, green = approved, red = denied,
       grey = expired/system-resolved. */
    .permission {
      flex: 1;
      padding: 8px 10px;
      border-radius: 4px;
      font-size: 12px;
      border: 1px solid;
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 6px;
    }
    .permission-pending  { background: var(--bg-task-in-progress); border-color: var(--warn-border); color: var(--warn-text); }
    .perm-actions {
      flex-basis: 100%;
      display: flex;
      gap: 6px;
      margin-top: 6px;
    }
    .perm-actions button {
      padding: 4px 12px;
      font-size: 12px;
      font-weight: 600;
      border-radius: 3px;
      cursor: pointer;
      border: 1px solid;
    }
    .perm-allow {
      background: var(--ok);
      color: var(--bg-surface);
      border-color: var(--ok);
    }
    .perm-allow:hover { background: var(--ok-strong); }
    .perm-deny {
      background: var(--bg-surface);
      color: var(--danger-text);
      border-color: var(--danger-text);
    }
    .perm-deny:hover { background: var(--bg-error-tint); }
    .perm-actions button:disabled {
      opacity: 0.5;
      cursor: wait;
    }
    .permission-approved { background: var(--bg-perm-approved); border-color: var(--ok-soft); color: var(--ok-deep); }
    .permission-denied   { background: var(--bg-error-tint); border-color: var(--danger-border); color: var(--danger-text-deep); }
    .permission-expired  { background: var(--bg-user-bar); border-color: var(--border-soft);    color: var(--text-secondary); }
    .perm-icon   { font-size: 14px; }
    .perm-tool   { font-weight: 600; font-family: ui-monospace, 'SF Mono', monospace; }
    .perm-status { color: inherit; opacity: 0.75; }
    .perm-preview {
      flex-basis: 100%;
      margin-top: 4px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 11px;
      color: inherit;
      opacity: 0.8;
      white-space: pre-wrap;
      word-break: break-all;
    }

    /* Intermediate "thinking" bubbles for chat/assistant_text events
       — pre-reply commentary Claude emits while a turn is in flight.
       Visually distinct from the consolidated reply so the operator
       can tell live narration from the final answer. */
    .message-intermediate .message-content {
      font-style: italic;
      color: var(--text-secondary);
    }
    .message-intermediate .message-author {
      color: var(--text-muted);
    }

    /* Tail-event rows — small inline activity-lane markers for CC
       lifecycle (tool use, turn boundaries, system notifications,
       session/task lifecycle). Distinct from chat bubbles so the
       operator can scan past them. */
    .tail-row {
      flex: 1;
      padding: 4px 8px;
      border-radius: 3px;
      font-size: 11px;
      font-family: ui-monospace, 'SF Mono', monospace;
      color: var(--text-muted-3);
      background: var(--bg-row-quiet);
      border: 1px solid var(--bg-hover-pale);
      display: inline-flex;
      align-items: baseline;
      gap: 6px;
      flex-wrap: wrap;
    }
    .tail-icon { font-weight: 700; }
    .tail-tool-pre  { color: var(--text-secondary);    background: var(--bg-sidebar); }
    .tail-tool-post { color: var(--ok-deep); background: var(--bg-tool-post); border-color: var(--ok-pale-3); }
    .tail-tool-fail { color: var(--danger-text-deep); background: var(--bg-error-tint); border-color: var(--danger-border-soft); }
    .tail-tool-name { font-weight: 600; }

    /* Tool rollup — consecutive tool calls collapse into a single
       strip. Click to expand into a one-line-per-tool list; click
       any tool inside to see its detail panel. */
    .tool-rollup {
      flex: 1;
      font-size: 12px;
    }
    .tool-rollup-collapsed {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      padding: 5px 10px;
      background: var(--bg-tool-rollup);
      border: 1px solid var(--border-tool-rollup-2);
      border-radius: 4px;
      cursor: pointer;
      flex-wrap: wrap;
      color: var(--text-secondary-2);
    }
    .tool-rollup-collapsed:hover { background: var(--bg-tool-rollup-hover); }
    .tool-rollup-expanded {
      display: flex;
      flex-direction: column;
      gap: 4px;
      padding: 8px 10px;
      background: var(--bg-tool-rollup);
      border: 1px solid var(--border-tool-rollup-2);
      border-radius: 4px;
    }
    .tool-rollup-header {
      display: flex;
      align-items: center;
      gap: 8px;
      cursor: pointer;
      padding-bottom: 4px;
      border-bottom: 1px dashed var(--border-tool-rollup);
      margin-bottom: 2px;
    }
    .tool-rollup-header:hover { color: var(--text-black); }
    .rollup-wrench { font-size: 13px; }
    .rollup-count  { font-weight: 600; }
    .rollup-summary { font-family: ui-monospace, 'SF Mono', monospace; color: var(--text-muted-3); }
    .rollup-icons { display: inline-flex; gap: 3px; align-items: center; flex-wrap: wrap; }
    .rollup-icon {
      width: 14px;
      height: 14px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      border-radius: 3px;
      font-size: 10px;
      font-weight: 700;
    }
    .rollup-icon.tail-tool-pre  { background: var(--bg-rollup-pre); color: var(--text-secondary); }
    .rollup-icon.tail-tool-post { background: var(--bg-rollup-post); color: var(--ok-strong); }
    .rollup-icon.tail-tool-fail { background: var(--bg-rollup-fail); color: var(--danger-rollup-text); }
    .rollup-overflow {
      font-size: 11px;
      color: var(--text-muted);
      padding: 0 4px;
    }
    .rollup-caret { margin-left: auto; color: var(--text-muted); }

    .rollup-tool-row {
      display: flex;
      flex-direction: column;
      padding: 3px 4px;
      border-radius: 3px;
      cursor: pointer;
    }
    .rollup-tool-row:hover { background: var(--shadow-row-hover); }
    .rollup-tool-line {
      display: flex;
      align-items: baseline;
      gap: 6px;
      flex-wrap: wrap;
    }
    .rollup-tool-toggle {
      margin-left: auto;
      color: var(--text-muted);
      font-size: 11px;
    }

    /* Detail panel revealed when a tool row is expanded. Two-column
       grid keyed by field name; values render as code chips or plain
       text per field. */
    .tool-detail {
      margin-top: 6px;
      padding: 8px 10px;
      background: var(--bg-surface);
      border: 1px solid var(--border-tool-rollup);
      border-radius: 3px;
      font-size: 11px;
      line-height: 1.5;
    }
    .tool-detail-row {
      display: grid;
      grid-template-columns: 100px 1fr;
      gap: 8px;
      padding: 2px 0;
    }
    .tool-detail-key {
      font-weight: 600;
      color: var(--text-muted);
      text-transform: lowercase;
    }
    .tool-detail-row code {
      background: var(--shadow-code-bg);
      padding: 1px 4px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      word-break: break-all;
    }
    .tail-tool-path {
      background: var(--shadow-code-bg);
      padding: 1px 5px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 11px;
      font-weight: 500;
      color: inherit;
      word-break: break-all;
    }
    .tail-tool-desc {
      color: var(--text-muted);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size: 11px;
      font-weight: 400;
      word-break: break-word;
    }
    .tail-error { color: var(--danger-text); }

    /* Task widget — one inline row per tracked task_id. Replaces
       the four separate tail-task rows that used to fire per
       lifecycle event. Status icon + subject + state label; click
       to expand the SubagentStop recap. */
    .task-widget {
      flex: 1;
      display: flex;
      flex-direction: column;
      gap: 4px;
      padding: 6px 10px;
      border-radius: 4px;
      font-size: 12px;
      border: 1px solid;
      cursor: pointer;
    }
    .task-widget:hover { filter: brightness(0.97); }
    .task-widget-line {
      display: flex;
      align-items: baseline;
      gap: 8px;
      flex-wrap: wrap;
    }
    .task-pending     { background: var(--bg-task-pending); border-color: var(--warn-border); color: var(--warn-text); }
    .task-in-progress { background: var(--bg-task-in-progress); border-color: var(--warn-border); color: var(--warn-text); }
    .task-done        { background: var(--bg-task-done); border-color: var(--ok-pale); color: var(--ok-deep); }
    .task-icon    { font-size: 13px; }
    .task-subject { font-weight: 600; flex: 1; min-width: 0; word-break: break-word; }
    .task-status  { font-size: 11px; opacity: 0.75; text-transform: lowercase; }
    .task-caret   { color: inherit; opacity: 0.6; }

    /* Play button on a finished subagent — loads the subagent's
       recap into the prompt input and sends it. Sits to the right
       of the task-widget line / tail-task header. */
    .subagent-play-btn {
      margin-left: auto;
      flex-shrink: 0;
      background: transparent;
      border: 1px solid currentColor;
      border-radius: 3px;
      color: inherit;
      cursor: pointer;
      font-size: 10px;
      line-height: 1;
      padding: 3px 7px;
      opacity: 0.65;
      transition: opacity 0.15s, background-color 0.15s;
    }
    .subagent-play-btn:hover {
      opacity: 1;
      background: var(--bg-hover-grey-1);
    }
    .task-recap-block {
      margin-top: 4px;
      padding: 6px 8px;
      background: var(--task-recap-bg);
      border-left: 3px solid currentColor;
      color: var(--text-secondary);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size: 12px;
      line-height: 1.45;
      white-space: pre-wrap;
      word-break: break-word;
    }
    /* "show N more / show less" toggle for long subagent recaps.
       Lightweight inline button; delegated click handler above. */
    .task-recap-toggle {
      margin-top: 4px;
      padding: 2px 8px;
      background: transparent;
      border: 1px solid var(--border-default);
      border-radius: 3px;
      cursor: pointer;
      font-size: 11px;
      color: var(--text-muted-3);
      font-family: inherit;
    }
    .task-recap-toggle:hover { background: var(--bg-user-bar); color: var(--text-primary); }

    /* Pinned task strip above the input area. Auto-hides when no
       tasks are tracked. Surfaces aggregate status so the operator
       sees ongoing work without scrolling. */
    .task-strip {
      padding: 8px 12px;
      border-top: 1px solid var(--border-default);
      background: var(--bg-row-quiet);
      max-height: 30vh;
      overflow-y: auto;
    }
    .task-strip-header {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      color: var(--text-muted-3);
      cursor: pointer;
      user-select: none;
    }
    .task-strip-header:hover { color: var(--text-black); }
    .task-strip-summary { flex: 1; }
    .task-strip-caret   { color: var(--text-meta); font-size: 12px; }
    /* Spacing only when the list is expanded — collapsed strip is a
       single-line header. */
    .task-strip-header:not(:last-child) { margin-bottom: 6px; }
    .task-strip-icon { font-size: 13px; }
    /* Per-session "clear strip" affordance. Hover-only on the header
       to keep idle strips quiet; full opacity while hovered. Click
       stamps localStorage `orchard.taskStripDismissedUntil.{sid}` to
       now() — tasks reappear as fresh activity lands. */
    .task-strip-clear {
      width: 20px;
      height: 20px;
      padding: 0;
      background: transparent;
      border: none;
      color: var(--text-meta);
      cursor: pointer;
      font-size: 16px;
      line-height: 1;
      border-radius: 3px;
      opacity: 0;
      transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
    }
    .task-strip-header:hover .task-strip-clear { opacity: 1; }
    .task-strip-clear:hover { background: var(--bg-hover-pale); color: var(--text-primary); }
    .task-strip-list {
      list-style: none;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      gap: 4px;
    }
    .task-strip-item {
      display: flex;
      align-items: baseline;
      gap: 6px;
      padding: 4px 8px;
      border-radius: 3px;
      font-size: 12px;
      border: 1px solid;
    }
    .task-strip-item.task-pending     { background: var(--bg-task-pending); border-color: var(--warn-border); color: var(--warn-text); }
    .task-strip-item.task-in-progress { background: var(--bg-task-in-progress); border-color: var(--warn-border); color: var(--warn-text); }
    .task-strip-item.task-done        { background: var(--bg-task-done); border-color: var(--ok-pale); color: var(--ok-deep); }
    .task-strip-more {
      font-size: 11px;
      color: var(--text-muted);
      padding: 2px 8px;
      list-style: none;
    }

    /* Subagent recap — multi-line dim block under a tail-task row.
       Light gray so it reads as commentary, not part of the work. */
    .task-recap {
      flex-basis: 100%;
      margin-top: 4px;
      padding: 6px 8px;
      background: var(--bg-recap);
      border-left: 3px solid var(--border-strong);
      color: var(--text-muted-3);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size: 12px;
      font-weight: 400;
      line-height: 1.45;
      white-space: pre-wrap;
      word-break: break-word;
    }

    /* Operator-prompt author label — same row layout as claude/reply
       rows; differentiated only by the @login prefix and a slightly
       different label color. */
    .message-author-prompt { color: var(--accent-deep); }

    /* @author label rendered under the timestamp on BOTH desktop
       and mobile. We emit the label twice (once inside .message-time
       as `.message-author-meta`, once inline next to the content as
       `.message > .message-author`); CSS hides the inline copy
       universally so the meta-column variant is what shows. */
    .message-author-meta {
      display: inline-block;
      font-size: 12px;
      font-weight: 600;
      color: var(--accent);
      line-height: 1.2;
      word-break: break-word;
    }
    .message-author-meta.message-author-prompt { color: var(--accent-deep); }
    .message > .message-author { display: none; }

    /* In-flight prompts (pre-server-ack). Dimmed so the operator can
       tell which row is still pending vs already persisted. */
    .message-pending .message-content { opacity: 0.7; }
    .message-pending .message-author  { opacity: 0.85; }
    .prompt-pending-sending {
      margin-left: 4px;
      color: var(--text-muted);
      font-style: italic;
      font-size: 11px;
      font-weight: 400;
    }
    .prompt-pending-failed {
      margin-left: 4px;
      color: var(--danger-text);
      font-style: italic;
      font-size: 11px;
      font-weight: 400;
    }
    .message-failed .message-content {
      border-left: 3px solid var(--danger-border);
      padding-left: 8px;
    }

    /* Send-ack checkmark on the most-recent prompt bubble. Negative
       animation-delay (set inline) keeps the fade smooth across
       re-renders during the 2s window. */
    .sent-ack {
      display: inline-block;
      margin-left: 6px;
      color: var(--ok);
      font-weight: 700;
      animation: sent-ack-fade 2s linear forwards;
    }
    @keyframes sent-ack-fade {
      0%   { opacity: 1; }
      70%  { opacity: 1; }
      100% { opacity: 0; }
    }
    .tail-turn-end {
      color: var(--text-meta);
      background: transparent;
      border: none;
      border-top: 1px dashed var(--border-default);
      border-radius: 0;
      justify-content: center;
      text-align: center;
      padding: 4px 0;
    }
    .tail-notification {
      background: var(--bg-task-in-progress);
      border-color: var(--warn-border);
      color: var(--warn-text);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size: 12px;
    }
    .tail-notification-title  { font-weight: 600; }
    .tail-notification-body   { opacity: 0.85; word-break: break-word; }
    .tail-session  { color: var(--ok-deep); background: var(--bg-tool-post); border-color: var(--ok-pale-3); }
    .tail-task     { color: var(--text-secondary-2);    background: var(--bg-tail-task); border-color: var(--border-default);   }
    .tail-unknown  { color: var(--danger-text); background: var(--bg-error-tint); border-color: var(--danger-border-soft); }

    /* Handoff card. Status-coloured collapsible <details> block from
       mcp__clawborrator__submit_handoff (channel_v1 >= 0.0.39). The
       <summary> line is the always-visible header; the body sections
       (completed / skipped / issues / commands / procedures / notes)
       expand on click. */
    .tail-handoff {
      flex:           1;
      padding:        0;
      font-family:    -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size:      12px;
      display:        block;
    }
    .tail-handoff-ok      { background: var(--bg-tool-post);   border-color: var(--ok-pale-3);          color: var(--ok-deep); }
    .tail-handoff-partial { background: var(--bg-task-in-progress); border-color: var(--warn-border); color: var(--warn-text); }
    .tail-handoff-fail    { background: var(--bg-error-tint);  border-color: var(--danger-border-soft); color: var(--danger-text-deep); }
    .tail-handoff-summary {
      cursor:         pointer;
      padding:        6px 10px;
      display:        flex;
      align-items:    baseline;
      gap:            8px;
      flex-wrap:      wrap;
      user-select:    none;
      list-style:     none;
    }
    .tail-handoff-summary::-webkit-details-marker { display: none; }
    .tail-handoff[open] .tail-handoff-summary { border-bottom: 1px solid var(--bg-hover-pale); }
    .tail-handoff-role { font-weight: 600; text-transform: lowercase; }
    .tail-handoff-status { font-weight: 600; font-size: 11px; padding: 1px 6px; border-radius: 3px; background: rgba(0,0,0,0.08); }
    .tail-handoff-counts { color: var(--text-muted-3); font-size: 11px; }
    .tail-handoff-mission { margin-left: auto; color: var(--text-muted-3); font-size: 11px; }
    .tail-handoff-body { padding: 8px 12px 10px 12px; display: flex; flex-direction: column; gap: 10px; }
    .tail-handoff-section { display: flex; flex-direction: column; gap: 3px; }
    .tail-handoff-h {
      font-size:      10px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      font-weight:    700;
      opacity:        0.7;
    }
    .tail-handoff-issues .tail-handoff-h { color: var(--danger-text-deep); opacity: 1; }
    .tail-handoff-list {
      margin:         0;
      padding-left:   18px;
      font-size:      12px;
      color:          var(--text-secondary);
    }
    .tail-handoff-list li { line-height: 1.5; }
    .tail-handoff-cmds {
      margin:         0;
      padding-left:   18px;
      font-size:      12px;
      font-family:    ui-monospace, 'SF Mono', monospace;
    }
    .tail-handoff-cmds li { line-height: 1.5; margin-bottom: 3px; }
    .tail-handoff-exit-ok  { color: var(--ok-deep);         font-weight: 600; font-size: 11px; }
    .tail-handoff-exit-bad { color: var(--danger-text-deep); font-weight: 600; font-size: 11px; }
    .tail-handoff-stdout {
      margin:         3px 0 0 0;
      padding:        4px 6px;
      background:     rgba(0,0,0,0.05);
      border-radius:  3px;
      font-size:      11px;
      white-space:    pre-wrap;
      max-height:     200px;
      overflow-y:     auto;
    }
    .tail-handoff-procs { display: flex; flex-wrap: wrap; gap: 4px; font-size: 11px; }
    .tail-handoff-procs code { padding: 1px 5px; background: rgba(0,0,0,0.06); border-radius: 3px; }
    .tail-handoff-notes { font-size: 12px; color: var(--text-secondary); white-space: pre-wrap; }

    /* AskUserQuestion card. Pending phase shows clickable option
       buttons; answered phase shows the chosen label highlighted and
       buttons disabled. */
    .tail-question {
      flex: 1;
      padding: 10px 12px;
      border: 1px solid var(--border-question);
      background: var(--bg-question);
      border-radius: 6px;
      font-size: 13px;
      color: var(--accent-deep-3);
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .tail-question-answered {
      background: var(--bg-tool-rollup);
      border-color: var(--border-tool-rollup);
      color: var(--text-secondary-2);
    }
    .tail-question-status {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      opacity: 0.75;
    }
    .tail-question-block {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .tail-question-q {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
      align-items: baseline;
    }
    .tail-question-header {
      display: inline-block;
      padding: 1px 6px;
      background: var(--bg-question-header);
      color: var(--accent-deep-2);
      border-radius: 3px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.02em;
    }
    .tail-question-text { font-weight: 500; }
    .tail-question-multi { font-size: 11px; opacity: 0.7; }
    .tail-question-options {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
    }
    .tail-question-option {
      flex: 1 1 200px;
      min-width: 160px;
      padding: 6px 10px;
      background: var(--bg-surface-2);
      border: 1px solid var(--border-question);
      border-radius: 5px;
      cursor: pointer;
      font: inherit;
      color: inherit;
      text-align: left;
      display: flex;
      flex-direction: column;
      gap: 2px;
    }
    .tail-question-option:hover:not(:disabled) {
      background: var(--bg-question-hover);
      border-color: var(--border-question-hover);
    }
    .tail-question-option:disabled {
      cursor: default;
    }
    .tail-question-option-chosen {
      background: var(--bg-question-chosen);
      border-color: var(--ok-medium);
      color: var(--ok-strong);
    }
    .tail-question-option-muted {
      background: var(--bg-tool-rollup);
      border-color: var(--border-tool-rollup);
      color: var(--text-muted);
      opacity: 0.7;
    }
    .tail-question-label { font-weight: 600; }
    .tail-question-desc {
      font-size: 11px;
      opacity: 0.8;
      line-height: 1.3;
    }

    /* File lifecycle rows — uploads and deletions surface as their
       own inline event so the operator can see file movement even
       when no chat message references the fileId. The chip-under-
       message rendering for `fileId=N` tokens stays unchanged. */
    .file-event {
      flex: 1;
      padding: 6px 10px;
      border-radius: 4px;
      font-size: 12px;
      display: flex;
      gap: 6px;
      align-items: baseline;
      flex-wrap: wrap;
    }
    .file-event-uploaded {
      background: var(--bg-file-event);
      border: 1px solid var(--border-file-event);
      color: var(--accent-deep);
    }
    .file-event-uploaded a {
      color: var(--accent);
      text-decoration: none;
      font-weight: 600;
    }
    .file-event-uploaded a:hover { text-decoration: underline; }
    .file-event-deleted {
      background: var(--bg-error-tint);
      border: 1px solid var(--danger-border);
      color: var(--danger-text-deep);
    }
    .file-event-by   { font-weight: 600; }
    .file-event-meta { color: var(--text-muted-2); font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px; }

    /* Op-message rows — operator-to-operator side-channel. CC does
       not see these, so render with a distinct "(op)" badge to make
       the difference obvious. */
    .op-message {
      flex: 1;
      padding: 6px 10px;
      border-radius: 4px;
      background: var(--bg-ac-hover);
      border: 1px solid var(--border-op-msg-soft);
      color: var(--accent-op-msg);
      font-size: 12px;
      display: flex;
      gap: 6px;
      align-items: baseline;
      flex-wrap: wrap;
    }
    .op-message-author { font-weight: 600; }
    .op-message-badge {
      background: var(--accent-op-msg);
      color: var(--bg-surface);
      font-size: 10px;
      font-weight: 600;
      padding: 1px 5px;
      border-radius: 2px;
      text-transform: uppercase;
    }
    .op-message-text { flex: 1; word-break: break-word; }

    /* Self-mention highlight — amber tint on op-messages that
       include the operator's login in payload.mentions. Pairs with
       the system notification fired by maybeNotifyOpMention. */
    .op-message.mentions-me {
      background: var(--bg-self-mention);
      border-color: var(--warn-mention-border);
      box-shadow: 0 0 0 1px var(--warn-mention-border);
    }

    /* peer-timeout: terse inline warning when a cross-session route
       expired without the peer replying. Matches basic-chat's
       `⚠ <peer> did not reply` line. */
    .peer-timeout {
      flex: 1;
      padding: 6px 10px;
      border-radius: 4px;
      font-size: 12px;
      background: var(--bg-error-tint);
      border: 1px solid var(--danger-border);
      color: var(--danger-text-deep);
    }

    .file-chip {
      display: inline-block;
      background: var(--bg-file-event);
      border: 1px solid var(--border-file-event);
      border-radius: 4px;
      padding: 6px 10px;
      margin: 4px 0;
      font-size: 12px;
      cursor: pointer;
    }

    .file-chip a {
      color: var(--accent);
      text-decoration: none;
    }

    .file-chip a:hover {
      text-decoration: underline;
    }

    .file-size {
      color: var(--text-meta);
      font-size: 11px;
      margin-left: 4px;
    }

    .input-area {
      padding: 16px;
      border-top: 1px solid var(--border-default);
      background: var(--bg-surface);
      position: relative;                          /* anchor for autocomplete popover */
    }

    /* "↓ new messages" pill — surfaced when the operator is scrolled
       up and new content arrives. Anchored to the input-area so it
       always sits just above it, regardless of task strip + input
       area heights. Click → snap to bottom. */
    .unread-pill {
      position: absolute;
      bottom: calc(100% + 8px);
      left: 50%;
      transform: translateX(-50%);
      padding: 6px 14px;
      background: var(--accent);
      color: var(--bg-surface);
      border: none;
      border-radius: 999px;
      font-size: 12px;
      font-weight: 500;
      cursor: pointer;
      box-shadow: 0 2px 8px var(--shadow-pill);
      z-index: 8;
      white-space: nowrap;
    }
    .unread-pill:hover { background: var(--accent-hover); }
    .unread-pill[hidden] { display: none; }

    /* @-mention autocomplete popover. Anchored above the input area
       (positions absolute relative to .input-area). Two sections:
       peers (operator's own/shared sessions) and agents (public
       expert agents with tagline + composable/isolated flag). */
    .autocomplete {
      position: absolute;
      bottom: calc(100% - 1px);                    /* sit on top edge of input-area */
      left: 16px;
      right: 16px;
      max-height: 50vh;
      overflow-y: auto;
      background: var(--bg-surface);
      border: 1px solid var(--border-strong);
      border-radius: 6px;
      box-shadow: 0 -4px 16px var(--shadow-popover);
      z-index: 5;
      font-size: 13px;
    }
    .ac-section {
      padding: 6px 12px;
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--text-muted);
      background: var(--bg-sidebar);
      border-bottom: 1px solid var(--bg-hover-grey-1);
    }
    .ac-list {
      list-style: none;
      margin: 0;
      padding: 4px 0;
    }
    .ac-item {
      display: flex;
      flex-direction: column;
      gap: 2px;
      padding: 6px 12px;
      cursor: pointer;
    }
    .ac-item:hover, .ac-item.ac-selected {
      background: var(--bg-ac-hover);
    }
    .ac-line1 {
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .ac-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .ac-online  { background: var(--ok); box-shadow: 0 0 0 2px var(--bg-presence-ring); }
    .ac-offline { background: var(--border-soft); }
    .ac-handle {
      font-weight: 600;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      color: var(--accent-deep);
    }
    .ac-meta {
      margin-left: auto;
      color: var(--text-muted);
      font-size: 11px;
    }
    .ac-tagline {
      color: var(--text-secondary);
      font-size: 11px;
      margin-left: 15px;
    }
    .ac-flag {
      font-size: 10px;
      font-weight: 600;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      padding: 1px 6px;
      border-radius: 3px;
      margin-left: auto;
    }
    .ac-flag-composable {
      background: var(--bg-perm-approved);
      color: var(--ok-deep);
      border: 1px solid var(--ok-pale);
    }
    .ac-flag-isolated {
      background: var(--bg-task-in-progress);
      color: var(--warn-text);
      border: 1px solid var(--warn-border);
    }

    /* Pending-attachments tray — chip strip above the input showing
       files queued to attach to the NEXT prompt. Each chip has an ×
       to drop it from the queue without deleting the upload. */
    .pending-attachments {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
      margin-bottom: 8px;
    }
    .pending-chip {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 4px 8px;
      background: var(--bg-file-event);
      border: 1px solid var(--border-file-event);
      border-radius: 4px;
      font-size: 12px;
      color: var(--accent-deep);
    }
    .pending-chip-name { font-weight: 600; }
    .pending-chip-size { color: var(--text-muted-2); font-size: 11px; }
    .pending-chip-remove {
      width: 18px;
      height: 18px;
      padding: 0;
      background: transparent;
      color: var(--text-secondary);
      border: none;
      border-radius: 50%;
      cursor: pointer;
      font-size: 16px;
      line-height: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
    }
    .pending-chip-remove:hover { background: var(--bg-pending-chip-hover); color: var(--text-black); }

    .input-form {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .input-row {
      display: flex;
      gap: 8px;
      align-items: flex-start;
    }

    /* Theme-aware defaults for every form control. Checkbox / radio /
       file stay native (their OS rendering is driven by color-scheme
       on :root, applied in the dark-mode override blocks). The
       per-class rules below (.publish-field, .share-add-row input,
       etc.) layer their own padding/border/sizing on top. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
    select,
    textarea {
      background: var(--bg-input);
      color: var(--text-primary);
    }

    input[type="text"],
    textarea {
      padding: 8px 12px;
      border: 1px solid var(--border-default);
      border-radius: 4px;
      font-family: inherit;
      font-size: 13px;
      resize: none;
      flex: 1;
    }

    textarea {
      height: 60px;
      line-height: 1.4;
    }

    button {
      padding: 8px 12px;
      background: var(--accent);
      color: var(--bg-surface);
      border: none;
      border-radius: 4px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
      transition: background-color 0.15s;
    }

    button:hover {
      background: var(--accent-hover);
    }

    button:disabled {
      background: var(--border-strong);
      cursor: not-allowed;
    }

    /* Op-message button — secondary action, distinct from the
       primary blue Prompt button. Goes to the operator-to-operator
       lane, not Claude. */
    #opMessageBtn {
      background: var(--bg-surface);
      color: var(--accent-op-msg);
      border: 1px solid var(--border-op-msg);
    }
    #opMessageBtn:hover { background: var(--bg-ac-hover); }
    #opMessageBtn:disabled {
      background: var(--bg-sidebar);
      color: var(--text-meta);
      border-color: var(--border-default);
      cursor: not-allowed;
    }

    .file-input-wrapper {
      position: relative;
      display: inline-block;
    }

    .file-input-wrapper input[type="file"] {
      display: none;
    }

    .drop-zone {
      position: absolute;
      inset: 0;
      border: 2px dashed transparent;
      border-radius: 4px;
      pointer-events: none;
    }

    .drop-zone.active {
      border-color: var(--accent);
      background: var(--bg-dropzone-active);
    }

    .help-text {
      font-size: 11px;
      color: var(--text-meta);
      margin-top: 4px;
    }

    .loading {
      color: var(--text-meta);
      font-style: italic;
      font-size: 12px;
    }

    .error-message {
      background: var(--bg-error-soft);
      color: var(--text-error-bright);
      padding: 8px;
      border-radius: 4px;
      margin: 4px 0;
      font-size: 12px;
    }

    .success-message {
      background: var(--bg-success-soft);
      color: var(--text-success-bright);
      padding: 8px;
      border-radius: 4px;
      margin: 4px 0;
      font-size: 12px;
    }

    main::-webkit-scrollbar {
      width: 8px;
    }

    main::-webkit-scrollbar-track {
      background: transparent;
    }

    main::-webkit-scrollbar-thumb {
      background: var(--border-strong);
      border-radius: 4px;
    }

    main::-webkit-scrollbar-thumb:hover {
      background: var(--text-meta);
    }

    /* === Session-details page ================================== */
    /* Reuses the existing main column but rendered as a static
       document layout rather than the chat scroll. Sections stack
       vertically, each with a label + a definition-list-ish grid of
       key/value rows. Visual idiom borrowed from .modal-section so
       it feels native to the SPA without introducing a new system. */
    .details-container {
      flex: 1;
      overflow-y: auto;
      overflow-x: hidden;
      padding: 20px 28px 40px;
      display: flex;
      flex-direction: column;
      gap: 22px;
      color: var(--text-primary);
    }
    .details-close {
      margin-left: auto;
      background: transparent;
      color: var(--text-muted-3);
      border: none;
      padding: 0 6px;
      font-size: 22px;
      line-height: 1;
      cursor: pointer;
      font-weight: 400;
    }
    .details-close:hover { color: var(--text-black); background: transparent; }
    .details-header {
      display: flex;
      flex-direction: column;
      gap: 4px;
      padding-bottom: 12px;
      border-bottom: 1px solid var(--bg-hover-pale);
    }
    .details-title-row {
      display: flex;
      align-items: baseline;
      gap: 10px;
      flex-wrap: wrap;
    }
    .details-title {
      font-size: 20px;
      font-weight: 600;
      color: var(--text-strong);
    }
    .details-status {
      font-size: 12px;
      color: var(--text-muted-3);
      font-weight: 400;
    }
    .details-status.connected { color: var(--ok-status); }
    .details-status.offline   { color: var(--text-muted); }
    .details-status.preflight { color: var(--warn-status); }
    .details-id-row {
      display: flex;
      align-items: center;
      gap: 6px;
      font-size: 12px;
      color: var(--text-muted);
      font-family: ui-monospace, 'SF Mono', monospace;
    }
    .details-copy-btn {
      padding: 1px 6px;
      background: transparent;
      border: 1px solid var(--border-strong);
      color: var(--text-secondary);
      font-size: 11px;
      border-radius: 3px;
      cursor: pointer;
      font-family: inherit;
    }
    .details-copy-btn:hover { background: var(--bg-user-bar); color: var(--text-primary); }
    .details-section {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .details-section-header {
      display: flex;
      align-items: baseline;
      gap: 10px;
      justify-content: space-between;
      padding-bottom: 4px;
      border-bottom: 1px solid var(--bg-hover-pale);
    }
    .details-section-title {
      font-size: 14px;
      font-weight: 600;
      color: var(--text-primary);
      text-transform: none;
      letter-spacing: 0;
    }
    .details-section-action {
      padding: 2px 10px;
      font-size: 12px;
      background: var(--bg-surface);
      color: var(--accent);
      border: 1px solid var(--accent-disabled);
      border-radius: 3px;
      cursor: pointer;
    }
    .details-section-action:hover { background: var(--bg-details-action-hover); }
    .details-section-body { padding: 4px 0; }
    .details-empty {
      color: var(--text-meta);
      font-style: italic;
      font-size: 12px;
      padding: 4px 0;
    }
    .details-grid {
      display: grid;
      grid-template-columns: 160px 1fr;
      gap: 4px 16px;
      font-size: 13px;
    }
    .details-grid .label {
      color: var(--text-muted-2);
      font-weight: 500;
      padding: 2px 0;
    }
    .details-grid .value {
      color: var(--text-strong);
      padding: 2px 0;
      display: flex;
      align-items: center;
      gap: 8px;
      flex-wrap: wrap;
      word-break: break-word;
    }
    .details-grid .value .muted {
      color: var(--text-muted);
      font-size: 12px;
    }
    .details-grid .value code {
      background: var(--bg-code-inline);
      padding: 1px 6px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      color: var(--text-secondary);
    }
    .details-toggle-pill {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 2px 8px;
      border-radius: 10px;
      background: var(--bg-hover-pale);
      color: var(--text-secondary);
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.04em;
      text-transform: uppercase;
    }
    .details-toggle-pill.on  { background: var(--bg-toggle-on); color: var(--ok-strong-2); }
    .details-toggle-pill.off { background: var(--bg-hover-pale);    color: var(--text-muted-2);    }
    .details-edit-link {
      padding: 1px 8px;
      background: transparent;
      color: var(--accent);
      border: 1px solid transparent;
      font-size: 11px;
      border-radius: 3px;
      cursor: pointer;
    }
    .details-edit-link:hover { background: var(--bg-details-action-hover); border-color: var(--accent-disabled); }
    .details-subhead {
      display: flex;
      align-items: baseline;
      gap: 8px;
      margin-top: 12px;
      font-size: 12px;
      font-weight: 600;
      color: var(--text-muted-2);
    }
    .details-usage-row {
      display: grid;
      grid-template-columns: 1fr repeat(4, minmax(58px, max-content));
      gap: 4px 14px;
      align-items: baseline;
      padding: 5px 0;
      font-size: 13px;
      border-bottom: 1px solid var(--bg-pill-soft);
    }
    .details-usage-row:last-child { border-bottom: none; }
    .details-usage-row span:not(:first-child) { text-align: right; }
    .details-usage-row.head {
      color: var(--text-muted);
      font-size: 11px;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      border-bottom: 1px solid var(--bg-hover-pale);
    }
    .details-usage-row code {
      background: var(--bg-code-inline);
      padding: 1px 6px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      color: var(--text-secondary);
    }
    .details-row {
      display: flex;
      gap: 12px;
      align-items: center;
      padding: 6px 0;
      border-bottom: 1px solid var(--bg-pill-soft);
      font-size: 13px;
    }
    .details-row:last-child { border-bottom: none; }
    .details-row .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
    .details-row .row-muted { color: var(--text-muted); font-size: 12px; }
    .details-row code {
      background: var(--bg-code-inline);
      padding: 1px 6px;
      border-radius: 3px;
      font-family: ui-monospace, 'SF Mono', monospace;
      font-size: 12px;
      color: var(--text-secondary);
    }
    .details-row-btn {
      padding: 2px 8px;
      background: var(--bg-surface);
      border: 1px solid var(--border-strong);
      color: var(--text-secondary-2);
      border-radius: 3px;
      cursor: pointer;
      font-size: 11px;
    }
    .details-row-btn:hover { background: var(--bg-code-inline); }
    .details-row-btn.danger {
      border-color: var(--danger);
      color: var(--danger);
    }
    .details-row-btn.danger:hover { background: var(--bg-danger-hover); }
    .details-danger {
      background: var(--bg-details-danger);
      border: 1px solid var(--danger-border-soft-2);
      border-radius: 4px;
      padding: 10px 14px;
    }
    .details-danger-row {
      display: flex;
      gap: 14px;
      align-items: center;
      padding: 6px 0;
    }
    .details-danger-row .label-block {
      flex: 1;
      min-width: 0;
    }
    .details-danger-row .label-block .name {
      font-size: 13px;
      color: var(--text-primary);
      font-weight: 500;
    }
    .details-danger-row .label-block .hint {
      font-size: 12px;
      color: var(--text-muted);
      margin-top: 2px;
    }
    .details-danger-btn {
      padding: 4px 12px;
      background: var(--bg-surface);
      border: 1px solid var(--danger);
      color: var(--danger);
      border-radius: 3px;
      cursor: pointer;
      font-size: 12px;
      font-weight: 500;
    }
    .details-danger-btn:hover { background: var(--bg-danger-hover); }
    .details-danger-btn.severe {
      background: var(--danger);
      color: var(--bg-surface);
    }
    .details-danger-btn.severe:hover { background: var(--danger-hover); }

    /* Agents section — published-as-public-agent surface. */
    .details-section-pill {
      display: inline-block;
      padding: 1px 8px;
      margin-left: 8px;
      font-size: 11px;
      font-weight: 500;
      border-radius: 9px;
      vertical-align: middle;
    }
    .details-section-pill.published {
      background: var(--bg-published);
      color: var(--ok-deep-2);
      border: 1px solid var(--ok-pale-2);
    }
    .details-section-actions {
      display: flex;
      justify-content: flex-end;
      gap: 10px;
      margin-top: 14px;
      padding-top: 12px;
      border-top: 1px solid var(--bg-hover-pale);
    }
    .details-action-btn {
      padding: 5px 14px;
      background: var(--bg-surface);
      border: 1px solid var(--border-pale);
      color: var(--text-primary);
      border-radius: 3px;
      cursor: pointer;
      font-size: 13px;
      font-weight: 500;
    }
    .details-action-btn:hover { background: var(--bg-code-inline); }
    .details-action-btn.primary {
      background: var(--accent);
      border-color: var(--accent);
      color: var(--bg-surface);
    }
    .details-action-btn.primary:hover { background: var(--accent-hover-2); }
    .details-agents-cta {
      padding: 10px 0;
    }
    .details-agents-cta p {
      margin: 0 0 8px 0;
      font-size: 13px;
      color: var(--text-primary);
    }
    .details-agents-cta .details-hint {
      color: var(--text-muted-3);
      font-size: 12.5px;
      line-height: 1.5;
    }
    .details-agent {
      padding: 4px 0;
    }
    .details-agent-handle {
      font-size: 14px;
      margin-bottom: 8px;
      display: flex;
      align-items: center;
      gap: 4px;
    }
    .details-agent-handle code {
      background: var(--bg-sidebar);
      padding: 2px 6px;
      border-radius: 3px;
      font-size: 13px;
    }
    .details-agent-name {
      font-size: 16px;
      font-weight: 600;
      color: var(--text-strong);
      margin-bottom: 4px;
    }
    .details-agent-tagline {
      font-style: italic;
      color: var(--text-secondary);
      margin-bottom: 8px;
      line-height: 1.4;
    }
    .details-agent-description {
      color: var(--text-secondary-2);
      font-size: 13px;
      line-height: 1.5;
      margin-bottom: 12px;
      padding-bottom: 12px;
      border-bottom: 1px solid var(--bg-hover-pale);
    }
    /* Public-live suggested-prompt chips rendered in the Agents
       section of the details pane. Mirror the pill shape of the
       publish-form chips but tuned for the details grid's tighter
       vertical rhythm. */
    .details-agent-chips {
      display: flex;
      flex-wrap: wrap;
      gap: 4px;
    }
    .details-agent-chip {
      display: inline-block;
      padding: 2px 8px;
      background: var(--bg-surface);
      border: 1px solid var(--border-soft);
      border-radius: 10px;
      font-size: 11px;
      color: var(--text-secondary);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: 100%;
    }

    .channel-item { display: flex; align-items: center; gap: 4px; }
    .channel-item .channel-content { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Visibility swaps for desktop vs mobile renders inside the
       details pane. Used by the Managed-session config row to show
       a short machine UUID on mobile and hide descriptive sub-text
       under each pill. Inline default so the desktop layout doesn't
       care about the swap. */
    .details-on-desktop { display: inline; }
    .details-on-mobile  { display: none; }

    @media (max-width: 720px) {
      .details-container { padding: 12px 14px 28px; }
      .details-grid { grid-template-columns: 110px 1fr; gap: 4px 10px; }
      .details-on-desktop { display: none; }
      .details-on-mobile  { display: inline; }
    }

    /* === Mobile / narrow viewport ============================== */
    /* On narrow screens the sidebar collapses to an off-canvas
       overlay. Body adds .nav-open while the user has it pulled out;
       the backdrop captures taps to dismiss. Above the breakpoint
       the styles revert to the desktop two-column flex layout. */
    @media (max-width: 720px) {
      .nav-toggle { display: inline-block; }
      .nav-close  { display: inline-flex; align-items: center; justify-content: center; }
      nav {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        height: 100dvh;
        width: 80vw;
        max-width: 320px;
        z-index: 10;
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        box-shadow: 2px 0 8px var(--shadow-menu);
      }
      body.nav-open nav { transform: translateX(0); }
      body.nav-open .nav-backdrop { display: block; }

      /* Details pane overlays the chat on narrow viewports (instead
         of squeezing it). Same idiom as the nav — fixed-position
         drawer that slides in from the right when #detailsPane is
         shown. The chat stays mounted underneath so toggling the
         pane closed lands right back where the operator was. */
      .details-pane {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        height: 100dvh;
        width: 100vw;
        max-width: 100%;
        z-index: 9;
        border-left: none;
        box-shadow: -2px 0 8px var(--shadow-menu);
      }

      /* Tighten everything else for small screens. */
      .header             { padding: 10px 12px; font-size: 15px; }
      main                { padding: 12px; }
      .input-area         { padding: 10px 12px; }
      /* Tighter meta column on small viewports; the meta-column
         layout itself (timestamp on top, @author below) is the
         default everywhere — see .message-time + .message-author-meta
         in the base styles. */
      .message-time       { min-width: 64px; font-size: 10px; }
      .message-author-meta { font-size: 11px; word-break: break-all; }
      .message-content    { font-size: 13px; }
      textarea            { font-size: 14px; height: 44px; }     /* iOS won't auto-zoom at ≥16px; 14 is fine for landscape */
      .input-row          { flex-wrap: nowrap; gap: 6px; }
      .help-text          { display: none; }                     /* free up vertical space */
      .perm-actions button { padding: 6px 14px; font-size: 13px; } /* easier tap targets */

      /* Mobile send/upload layout: upload first (small), then the
         secondary Message button, then the primary Prompt button
         taking the remaining width as the big right-side tap target.
         Order: upload | message | prompt via flex order. */
      .input-row .file-input-wrapper { order: 1; }
      .input-row #opMessageBtn       { order: 2; padding: 10px 12px; font-size: 13px; }
      .input-row #sendBtn            { order: 3; flex: 1; padding: 10px 14px; font-size: 14px; }
      .input-row #uploadBtn          { padding: 10px 14px; font-size: 14px; }
    }
