        /* Custom scrollbar — global */
        *::-webkit-scrollbar {
            width: 6px;
            height: 6px;
        }
        *::-webkit-scrollbar-track {
            background: transparent;
        }
        *::-webkit-scrollbar-thumb {
            background: #404040;
            border-radius: 3px;
        }
        *::-webkit-scrollbar-thumb:hover {
            background: #525252;
        }
        /* Firefox */
        * {
            scrollbar-width: thin;
            scrollbar-color: #404040 transparent;
        }

        /* Alpine x-cloak: hide elements until Alpine processes them */
        [x-cloak] { display: none !important; }
        /* Multi-view container: hidden by default, shown via class toggle */
        .mv-container { display: none !important; }
        .mv-container.mv-active { display: flex !important; }

        /* Spinning gradient angle property */
        @property --gradient-angle {
            syntax: "<angle>";
            initial-value: 0deg;
            inherits: false;
        }
        @keyframes spin-gradient {
            to { --gradient-angle: 360deg; }
        }

        /* ── Multi-view phone card gradient borders ── */
        .mv-phone-cell {
            position: relative;
            padding: 2px;
            background: rgba(255, 255, 255, 0.04);
            border-radius: 13px;
            transition:
                background 220ms cubic-bezier(0.16, 1, 0.3, 1),
                box-shadow 220ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 160ms ease;
        }
        .mv-phone-cell::before {
            content: '';
            position: absolute;
            inset: 0;
            border-radius: inherit;
            opacity: 0;
            transition: opacity 0.5s ease;
            pointer-events: none;
        }
        .mv-phone-idle {
            background: rgba(255, 255, 255, 0.04);
            box-shadow:
                inset 0 0.5px 0 rgba(255, 255, 255, 0.08),
                inset 0 -0.5px 0 rgba(0, 0, 0, 0.18);
        }
        /* Selected — Liquid Glass blue rim with refraction (no flat white) */
        .mv-phone-selected {
            background: rgba(var(--sw-blue-rgb), 0.42);
            box-shadow:
                inset 0 0.5px 0 rgba(255, 255, 255, 0.42),
                inset 0 -0.5px 0 rgba(0, 0, 0, 0.18),
                inset 1px 0 0 rgba(255, 255, 255, 0.18),
                0 0 0 1px rgba(var(--sw-blue-rgb), 0.45),
                0 0 28px -4px rgba(var(--sw-blue-rgb), 0.40),
                0 4px 14px -4px rgba(var(--sw-blue-rgb), 0.30);
        }
        /* Waiting: amber spinning gradient */
        .mv-phone-waiting::before {
            background: conic-gradient(from var(--gradient-angle), rgba(251,146,60,0.9), rgba(245,158,11,0.3), rgba(252,211,77,0.9), rgba(245,158,11,0.3), rgba(251,146,60,0.9));
            animation: spin-gradient 3s linear infinite;
            opacity: 1;
        }
        .mv-phone-waiting {
            box-shadow: 0 0 20px rgba(251,146,60,0.2), 0 0 40px rgba(251,146,60,0.08);
        }
        /* Running: bright amber spinning gradient */
        .mv-phone-running::before {
            background: conic-gradient(from var(--gradient-angle), rgba(251,146,60,1), rgba(245,158,11,0.5), rgba(252,211,77,1), rgba(245,158,11,0.5), rgba(251,146,60,1));
            animation: spin-gradient 2s linear infinite;
            opacity: 1;
        }
        .mv-phone-running {
            box-shadow: 0 0 25px rgba(251,146,60,0.3), 0 0 50px rgba(251,146,60,0.1);
        }
        /* Done: green spinning gradient that fades out */
        .mv-phone-done::before {
            background: conic-gradient(from var(--gradient-angle), rgba(52,211,153,1), rgba(16,185,129,0.6), rgba(110,231,183,1), rgba(16,185,129,0.6), rgba(52,211,153,1));
            animation: spin-gradient 2s linear infinite;
            opacity: 1;
        }
        .mv-phone-done {
            box-shadow: 0 0 30px rgba(52,211,153,0.3), 0 0 60px rgba(52,211,153,0.1);
        }

        /* Phone preview container - takes full available space */
        .phone-preview-container {
            position: relative;
            overflow: hidden;
            width: 100%;
            height: 100%;
        }
        /* Phone preview wrapper - centers the scaled content */
        .phone-preview {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
            background: var(--sw-window-bg);
        }
        /* Iframe is sized dynamically by phoneControl.scalePhonePreview()
           to the largest iPhone-aspect (430:932) box that fits the container.
           noVNC inside the iframe auto-fits its 1290x2796 canvas via the
           ?resize=scale URL param — we do NOT apply CSS transform here,
           which would otherwise double-scale and clip the top of the screen. */
        .phone-preview iframe {
            flex-shrink: 0;
            border: none;
            border-radius: 8px;
            display: block;
            background: #000;
        }
        /* Loading overlay — covers the whole .phone-preview panel with a
           black surface + centered looping video. Stays visible until the
           iframe's `load` event fires. On ERR_CONNECTION_REFUSED the load
           event never fires, so the overlay (and only the overlay) is what
           the user sees — the browser's "127.0.0.1 refused to connect"
           page is hidden behind it forever. */
        .phone-preview-overlay {
            position: absolute;
            inset: 0;
            background: var(--sw-window-bg);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 5;
            pointer-events: none;
            border-radius: 8px;
        }
        .phone-preview-overlay video {
            width: 100%;
            height: auto;
            object-fit: contain;
            /* Crop the bottom 8% to hide the Veo 3 watermark in the source clip */
            clip-path: inset(0 0 8% 0);
        }
        .phone-preview.iframe-loaded .phone-preview-overlay {
            display: none;
        }

        /* Chat message animations */
        .chat-message {
            animation: fadeIn 0.3s ease-in;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(10px); }
            to { opacity: 1; transform: translateY(0); }
        }

        /* ── App toast system (shared across pages) ── */
        .app-toast-host {
            position: fixed;
            z-index: 9998;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            justify-content: center;
            align-items: flex-end;
            pointer-events: none;
            padding: 0 max(0.75rem, env(safe-area-inset-right)) max(1rem, env(safe-area-inset-bottom)) max(0.75rem, env(safe-area-inset-left));
        }
        .app-toast {
            position: relative;
            width: min(100%, 22rem);
            max-width: calc(100vw - 1.5rem);
            border-radius: 14px;
            overflow: hidden;
            pointer-events: auto;
            background: rgba(14, 14, 17, 0.92);
            backdrop-filter: blur(22px) saturate(1.15);
            -webkit-backdrop-filter: blur(22px) saturate(1.15);
            border: 1px solid rgba(255, 255, 255, 0.07);
            box-shadow:
                0 16px 48px rgba(0, 0, 0, 0.45),
                0 0 0 1px rgba(255, 255, 255, 0.04) inset,
                0 1px 0 rgba(255, 255, 255, 0.06) inset;
        }
        .app-toast__glow {
            position: absolute;
            inset: 0;
            opacity: 0.14;
            pointer-events: none;
            border-radius: inherit;
        }
        .app-toast--success .app-toast__glow {
            background: radial-gradient(130% 100% at 0% 0%, rgb(52, 211, 153), transparent 58%);
        }
        .app-toast--error .app-toast__glow {
            background: radial-gradient(130% 100% at 0% 0%, rgb(251, 113, 133), transparent 58%);
        }
        .app-toast--info .app-toast__glow {
            background: radial-gradient(130% 100% at 0% 0%, rgb(125, 211, 252), transparent 58%);
        }
        .app-toast__body {
            position: relative;
            z-index: 1;
            display: flex;
            align-items: flex-start;
            gap: 11px;
            padding: 13px 12px 13px 14px;
        }
        .app-toast__icon {
            flex-shrink: 0;
            width: 36px;
            height: 36px;
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.15rem;
        }
        .app-toast--success .app-toast__icon {
            background: rgba(52, 211, 153, 0.14);
            color: rgb(167, 243, 208);
        }
        .app-toast--error .app-toast__icon {
            background: rgba(251, 113, 133, 0.14);
            color: rgb(254, 202, 202);
        }
        .app-toast--info .app-toast__icon {
            background: rgba(125, 211, 252, 0.12);
            color: rgb(186, 230, 253);
        }
        .app-toast__text {
            flex: 1;
            min-width: 0;
            margin: 0;
            padding-top: 6px;
            font-size: 13px;
            line-height: 1.45;
            font-weight: 500;
            letter-spacing: -0.015em;
            color: rgba(255, 255, 255, 0.92);
            overflow-wrap: anywhere;
            word-break: break-word;
        }
        .app-toast__dismiss {
            flex-shrink: 0;
            margin-top: 3px;
            width: 30px;
            height: 30px;
            border-radius: 9px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: rgba(255, 255, 255, 0.32);
            border: none;
            background: transparent;
            cursor: pointer;
            transition: color 0.15s ease, background 0.15s ease;
        }
        .app-toast__dismiss:hover {
            color: rgba(255, 255, 255, 0.9);
            background: rgba(255, 255, 255, 0.07);
        }
        .app-toast__dismiss:focus-visible {
            outline: 2px solid rgba(255, 255, 255, 0.35);
            outline-offset: 2px;
        }
        .app-toast__meter {
            position: relative;
            z-index: 1;
            height: 3px;
            background: rgba(255, 255, 255, 0.05);
        }
        .app-toast__meter-fill {
            display: block;
            height: 100%;
            width: 100%;
            transform-origin: left center;
            animation: app-toast-meter 3.9s linear forwards;
        }
        .app-toast--success .app-toast__meter-fill {
            background: linear-gradient(90deg, rgb(45, 212, 191), rgb(16, 185, 129));
        }
        .app-toast--error .app-toast__meter-fill {
            background: linear-gradient(90deg, rgb(251, 113, 133), rgb(244, 63, 94));
        }
        .app-toast--info .app-toast__meter-fill {
            background: linear-gradient(90deg, rgb(125, 211, 252), rgb(14, 165, 233));
        }
        @keyframes app-toast-meter {
            from { transform: scaleX(1); }
            to { transform: scaleX(0); }
        }
        @media (prefers-reduced-motion: reduce) {
            .app-toast__meter-fill {
                animation: none;
                opacity: 0.5;
            }
        }

        /* Sidebar backdrop progressive blur */
        @keyframes backdrop-blur-in {
            from { backdrop-filter: blur(0px); background: rgba(0,0,0,0); }
            to   { backdrop-filter: blur(12px); background: rgba(0,0,0,0.35); }
        }

        /* Sidebar nav item staggered entrance */
        @keyframes sidebar-item-in {
            from { opacity: 0; transform: translateX(12px); }
            to   { opacity: 1; transform: translateX(0); }
        }

        /* Typing indicator */
        .typing-indicator span {
            animation: blink 1.4s infinite both;
        }
        .typing-indicator span:nth-child(2) {
            animation-delay: 0.2s;
        }
        .typing-indicator span:nth-child(3) {
            animation-delay: 0.4s;
        }
        @keyframes blink {
            0%, 80%, 100% { opacity: 0; }
            40% { opacity: 1; }
        }

        /* Code blocks in chat */
        .chat-code {
            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
            font-size: 0.85rem;
        }

        /* Multi-view grid phone cells */
        .mv-phone-cell:hover {
            transform: translateY(-1px);
        }
        .mv-phone-idle:hover {
            background: rgba(255, 255, 255, 0.08);
            box-shadow:
                inset 0 0.5px 0 rgba(255, 255, 255, 0.14),
                inset 0 -0.5px 0 rgba(0, 0, 0, 0.18),
                0 6px 18px -6px rgba(0, 0, 0, 0.45);
        }
        /* Inner card — subtle top-rim Liquid Glass refraction.
           Reads as light entering the glass surface at the top edge. */
        .mv-phone-inner {
            box-shadow:
                inset 0 0.5px 0 rgba(255, 255, 255, 0.10),
                inset 0 -0.5px 0 rgba(0, 0, 0, 0.16);
        }

        /* Typography tightening */
        h1, h2, h3, h4, h5, h6 { line-height: 1.1; letter-spacing: -0.025em; }

        /* Tab horizontal scroll — hidden scrollbar */
        .tabs-scroll { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; -ms-overflow-style: none; }
        .tabs-scroll::-webkit-scrollbar { display: none; }

        /* Semantic button glow states */
        .btn-glow-blue:hover { box-shadow: 0 0 12px rgba(59,130,246,0.35); }
        .btn-glow-red:hover { box-shadow: 0 0 12px rgba(239,68,68,0.35); }
        .btn-glow-brand:hover { box-shadow: 0 0 12px rgba(168,85,247,0.3); }

        /* Glass button system — transparent default */
        .glass-btn {
            position: relative;
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 14px;
            font-size: 12px;
            font-weight: 500;
            color: rgba(255,255,255,0.55);
            background: transparent;
            border: 1px solid transparent;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.2s ease;
            white-space: nowrap;
            flex-shrink: 0;
            letter-spacing: -0.01em;
        }
        .glass-btn:hover {
            color: rgba(255,255,255,0.9);
            background: rgba(255,255,255,0.06);
            border-color: rgba(255,255,255,0.08);
        }
        .glass-btn:active {
            background: rgba(255,255,255,0.03);
        }
        .glass-btn svg { flex-shrink: 0; }
        /* Keyboard focus ring (WCAG 2.4.7) — glass buttons only had :hover/:active. */
        .glass-btn:focus-visible {
            outline: none;
            border-color: rgba(255,255,255,0.35);
            box-shadow: 0 0 0 3px var(--sw-keyboard-focus-ring);
        }
        .glass-btn.glass-primary {
            color: #fff;
            background: rgba(59,130,246,0.15);
            border-color: rgba(59,130,246,0.2);
        }
        .glass-btn.glass-primary:hover {
            background: rgba(59,130,246,0.25);
            border-color: rgba(59,130,246,0.35);
            box-shadow: 0 0 10px rgba(59,130,246,0.12);
        }

        /* Prompt bar gradient ring — kept as a celebration overlay on top
           of the new sw-input-bar Liquid Glass body. Activates on focus,
           while a task is running (warm orange), and briefly when a task
           finishes successfully (green). */
        .prompt-bar::before {
            content: '';
            position: absolute;
            inset: -1px;
            border-radius: inherit;
            padding: 1px;
            background: transparent;
            -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
            -webkit-mask-composite: xor;
            mask-composite: exclude;
            pointer-events: none;
            opacity: 0;
            transition: background 0.3s ease, opacity 0.3s ease;
        }
        .prompt-bar:focus-within::before,
        .prompt-bar.task-running::before,
        .prompt-bar.task-complete::before {
            opacity: 1;
            background: conic-gradient(from var(--gradient-angle),
                rgba(var(--sw-blue-rgb), 0.55),
                rgba(var(--sw-blue-rgb), 0.10),
                rgba(var(--sw-blue-rgb), 0.55));
            animation: spin-gradient 3s linear infinite;
        }
        .prompt-bar.task-running::before {
            background: conic-gradient(from var(--gradient-angle),
                rgba(var(--sw-orange-rgb), 0.55),
                rgba(var(--sw-orange-rgb), 0.10),
                rgba(var(--sw-orange-rgb), 0.55));
        }
        .prompt-bar.task-complete::before {
            background: conic-gradient(from var(--gradient-angle),
                rgba(var(--sw-green-rgb), 0.85),
                rgba(var(--sw-green-rgb), 0.30),
                rgba(var(--sw-green-rgb), 0.85));
            animation: spin-gradient 2s linear infinite;
        }
        .prompt-bar.task-complete {
            box-shadow:
                inset 0 0.5px 0 rgba(255,255,255,0.32),
                inset 0 -0.5px 0 rgba(0,0,0,0.18),
                0 0 0 1px rgba(var(--sw-green-rgb), 0.35),
                0 0 28px -4px rgba(var(--sw-green-rgb), 0.30),
                0 8px 24px -6px rgba(0,0,0,0.20) !important;
        }
        @keyframes pulse-dot {
            0%, 100% { opacity: 1; box-shadow: 0 0 6px rgba(74,222,128,0.5); }
            50% { opacity: 0.55; box-shadow: 0 0 2px rgba(74,222,128,0.15); }
        }

        /* ── Single-workspace prompt bar — borderless / floating ──────
           Strip the card-like border, fill, and inset rims from the
           default .sw-input-bar so the input feels like it's just
           hovering above the chat. A soft drop shadow keeps the lift,
           and the backdrop blur gives the buttons something to sit
           against without painting an outlined box. */
        .sw-input-bar.prompt-bar {
            background: transparent;
            border: none;
            box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.45);
            -webkit-backdrop-filter: none;
                    backdrop-filter: none;
        }
        :root[data-theme="light"] .sw-input-bar.prompt-bar {
            background: transparent;
            border: none;
            box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.10);
        }
        .sw-input-bar.prompt-bar:focus-within {
            border: none;
            box-shadow: 0 14px 36px -8px rgba(0, 0, 0, 0.55);
        }
        :root[data-theme="light"] .sw-input-bar.prompt-bar:focus-within {
            box-shadow: 0 14px 36px -8px rgba(0, 0, 0, 0.14);
        }

        /* ── Global Top Bar — deep black override ─────────────────────
           The default .sw-glass-bar uses a brightening backdrop-filter
           that pulls a blue cast through from whatever sits behind it.
           For the topbar specifically we want a flat near-black surface
           so the workspace tabs read cleanly. We strip the brightness
           filter and lay an opaque dark fill on top. */
        .sw-glass-bar-topbar {
            background: #050507;
            -webkit-backdrop-filter: none;
                    backdrop-filter: none;
            border-bottom: 1px solid rgba(255, 255, 255, 0.06);
            box-shadow:
                inset 0 1px 0 rgba(255, 255, 255, 0.03),
                inset 0 -1px 0 rgba(0, 0, 0, 0.50),
                0 4px 16px rgba(0, 0, 0, 0.55);
        }
        :root[data-theme="light"] .sw-glass-bar-topbar {
            background: rgba(245, 245, 247, 0.92);
            border-bottom: 1px solid rgba(0, 0, 0, 0.08);
            box-shadow:
                inset 0 1px 0 rgba(255, 255, 255, 0.85),
                inset 0 -1px 0 rgba(0, 0, 0, 0.05),
                0 4px 16px rgba(0, 0, 0, 0.06);
        }

        /* ── Workspace Tabs — Chrome-style ─────────────────────────── */
        /* Active tab uses a solid surface that feels connected to the
           content below the bar; pseudo-elements draw quarter-circle
           "wings" at each bottom corner so the tab flows outward into
           the bar (the iconic Chrome shape). Inactive tabs are quiet
           rounded-top placeholders that lift on hover. */
        :root {
            /* Solid opaque fills so tabs read as stacked sheets, not
               translucent washes. The bar (#050507) stays underneath;
               each tab is a flat slab of its own color. */
            --ws-tab-active-bg: #34343a;
            --ws-tab-active-fg: rgba(255, 255, 255, 0.98);
            --ws-tab-inactive-bg: #1c1c20;
            --ws-tab-inactive-fg: rgba(255, 255, 255, 0.62);
            --ws-tab-hover-bg: #26262b;
            --ws-tab-radius: 12px;
            --ws-tab-wing: 12px;
            /* gap = 2 × wing + ample breathing room so each tab's
               wings fully reach the baseline with clear space between. */
            --ws-tab-gap: 48px;
        }
        :root[data-theme="light"] {
            --ws-tab-active-bg: #ffffff;
            --ws-tab-active-fg: rgba(0, 0, 0, 0.92);
            --ws-tab-inactive-bg: #d8d8dd;
            --ws-tab-inactive-fg: rgba(0, 0, 0, 0.55);
            --ws-tab-hover-bg: #e8e8ed;
        }

        .workspace-tabs {
            display: flex;
            align-items: flex-end;
            gap: var(--ws-tab-gap);
            overflow: visible;
        }
        /* Each tab is fully self-contained: its wings curve all the
           way down to the bar baseline, and the next tab only starts
           after a clear gap so the curves don't touch or overlap. */
        .ws-tab-add {
            margin-left: calc(var(--ws-tab-wing) + 4px) !important;
        }
        /* Active tab is always the top layer — it gets z-index above
           every inactive tab (and its wrapper) plus a subtle lift via
           translateY and a stronger drop shadow so it physically reads
           as the foreground sheet. */
        .workspace-tabs > div:has(.ws-tab-active) {
            z-index: 5;
            position: relative;
        }
        .workspace-tabs > div:has(.ws-tab-inactive) {
            z-index: 1;
            position: relative;
        }

        /* Base tab — Chrome shape: rounded-top trapezoid with bottom
           wings that flare outward into the bar. The wings of each tab
           reach exactly to the midpoint of the gap, so adjacent tabs'
           curves meet at a shared baseline (Apple's continuous-line
           feel). */
        .ws-tab {
            border: none;
            background: transparent;
            cursor: pointer;
            border-top-left-radius: var(--ws-tab-radius);
            border-top-right-radius: var(--ws-tab-radius);
            border-bottom-left-radius: 0;
            border-bottom-right-radius: 0;
            margin: 0;
            padding-left: 14px;
            padding-right: 6px;
            position: relative;
            color: var(--ws-tab-inactive-fg);
            transition:
                background-color 0.22s cubic-bezier(0.4, 0, 0.2, 1),
                color 0.22s cubic-bezier(0.4, 0, 0.2, 1),
                transform 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                box-shadow 0.22s cubic-bezier(0.4, 0, 0.2, 1);
        }
        /* Wings — every tab carries them. Each wing extends into the
           bar's gap and curves outward; with gap = 2 × wing, two
           neighbouring tabs' wings meet exactly at the midpoint, so
           the bottom baseline reads as a continuous wave of curves
           between filled tab silhouettes (the iconic Chrome strip). */
        .ws-tab::before,
        .ws-tab::after {
            content: '';
            position: absolute;
            bottom: 0;
            width: var(--ws-tab-wing);
            height: var(--ws-tab-wing);
            pointer-events: none;
            transition: background 0.22s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .ws-tab::before { left: calc(-1 * var(--ws-tab-wing)); }
        .ws-tab::after  { right: calc(-1 * var(--ws-tab-wing)); }

        /* Active tab — lighter fill, lifted off the bar as the top
           layer. The translateY + stronger shadow give it a clear
           foreground feel; z-index on the wrapper (above) keeps it on
           top of any neighbouring decoration. */
        .ws-tab-active {
            background: var(--ws-tab-active-bg);
            color: var(--ws-tab-active-fg);
            box-shadow:
                inset 0 0.5px 0 rgba(255, 255, 255, 0.36),
                inset 0 -0.5px 0 rgba(0, 0, 0, 0.20),
                0 4px 12px rgba(0, 0, 0, 0.45),
                0 1px 0 rgba(0, 0, 0, 0.30);
        }
        .ws-tab-active::before {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top left,
                transparent 100%,
                var(--ws-tab-active-bg) 100%
            );
        }
        .ws-tab-active::after {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top right,
                transparent 100%,
                var(--ws-tab-active-bg) 100%
            );
        }

        /* Inactive tab — quieter fill for the body and matching wings.
           No border: the silhouette is read entirely by the fill
           contrast against the bar. */
        .ws-tab-inactive {
            background: var(--ws-tab-inactive-bg);
        }
        .ws-tab-inactive::before {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top left,
                transparent 100%,
                var(--ws-tab-inactive-bg) 100%
            );
        }
        .ws-tab-inactive::after {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top right,
                transparent 100%,
                var(--ws-tab-inactive-bg) 100%
            );
        }
        .ws-tab-inactive:hover {
            background: var(--ws-tab-hover-bg);
            color: var(--ws-tab-active-fg);
        }
        .ws-tab-inactive:hover::before {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top left,
                transparent 100%,
                var(--ws-tab-hover-bg) 100%
            );
        }
        .ws-tab-inactive:hover::after {
            background: radial-gradient(
                circle var(--ws-tab-wing) at top right,
                transparent 100%,
                var(--ws-tab-hover-bg) 100%
            );
        }

        /* Three-dot menu trigger — fade in on hover/active */
        .ws-tab-menu {
            opacity: 0;
            transition: opacity 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                        background 0.15s ease;
        }
        .ws-tab:hover .ws-tab-menu,
        .ws-tab-active .ws-tab-menu {
            opacity: 1;
        }

        /* "+" tab — quiet circular button to the right of the trapezoid row */
        .ws-tab-add {
            border: none;
            border-radius: 50%;
            margin-left: 4px;
            color: var(--ws-tab-inactive-fg);
            transition:
                background 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                transform 0.12s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .ws-tab-add:hover {
            background: var(--ws-tab-hover-bg);
            color: var(--ws-tab-active-fg);
            transform: scale(1.06);
        }
        .ws-tab-add:active { transform: scale(0.94); }

        /* ── Phone controls popover — Apple-style transparent buttons ─────
           Buttons inside the collapsible controls popover wash out their
           glass background so the popover's blur reads through them. Hover
           gently brightens the surface and lifts the icon; press settles
           it back. Disabled state remains quiet. */
        .ws-controls-popover .sw-btn {
            background: rgba(255, 255, 255, 0.04);
            border: 0.5px solid rgba(255, 255, 255, 0.08);
            box-shadow: none;
            backdrop-filter: none;
            -webkit-backdrop-filter: none;
            transition:
                background-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                border-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                transform 0.14s cubic-bezier(0.4, 0, 0.2, 1);
        }
        :root[data-theme="light"] .ws-controls-popover .sw-btn {
            background: rgba(0, 0, 0, 0.03);
            border: 0.5px solid rgba(0, 0, 0, 0.08);
        }
        .ws-controls-popover .sw-btn:hover:not(:disabled) {
            background: rgba(255, 255, 255, 0.10);
            border-color: rgba(255, 255, 255, 0.16);
            transform: translateY(-1px);
        }
        :root[data-theme="light"] .ws-controls-popover .sw-btn:hover:not(:disabled) {
            background: rgba(0, 0, 0, 0.06);
            border-color: rgba(0, 0, 0, 0.14);
        }
        .ws-controls-popover .sw-btn:active:not(:disabled) {
            transform: translateY(0) scale(0.97);
            background: rgba(255, 255, 255, 0.06);
        }
        .ws-controls-popover .sw-btn:disabled {
            opacity: 0.35;
            cursor: not-allowed;
        }
        /* These buttons reset box-shadow to none (line ~721), which also wipes
           the inherited .sw-btn focus ring — restore it for keyboard users (WCAG 2.4.7). */
        .ws-controls-popover .sw-btn:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--sw-keyboard-focus-ring);
        }
        /* Run Workflow accent — keep pink tint readable but transparent */
        .ws-controls-popover .sw-btn.is-pink {
            background: rgba(var(--sw-pink-rgb, 255, 45, 85), 0.10);
            border-color: rgba(var(--sw-pink-rgb, 255, 45, 85), 0.28);
            color: var(--sw-pink, #ff2d55);
        }
        .ws-controls-popover .sw-btn.is-pink:hover:not(:disabled) {
            background: rgba(var(--sw-pink-rgb, 255, 45, 85), 0.18);
            border-color: rgba(var(--sw-pink-rgb, 255, 45, 85), 0.42);
        }
        /* Start "running" green tint stays visible but transparent */
        .ws-controls-popover .sw-btn.is-green {
            background: rgba(var(--sw-green-rgb, 52, 199, 89), 0.10);
            border-color: rgba(var(--sw-green-rgb, 52, 199, 89), 0.28);
            color: var(--sw-green, #34c759);
        }
        /* Cluster groups (HID + tools) — let the popover blur show through */
        .ws-controls-popover .sw-glass-group {
            background: rgba(255, 255, 255, 0.03);
            border: 0.5px solid rgba(255, 255, 255, 0.08);
            backdrop-filter: none;
            -webkit-backdrop-filter: none;
        }
        :root[data-theme="light"] .ws-controls-popover .sw-glass-group {
            background: rgba(0, 0, 0, 0.02);
            border: 0.5px solid rgba(0, 0, 0, 0.08);
        }
        /* Inside a cluster pill, individual icon buttons drop their own
           border so they read as a single segmented control. */
        .ws-controls-popover .sw-glass-group .sw-btn {
            background: transparent;
            border: none;
            border-radius: 8px;
        }
        .ws-controls-popover .sw-glass-group .sw-btn:hover:not(:disabled) {
            background: rgba(255, 255, 255, 0.10);
            border: none;
            transform: translateY(-1px) scale(1.04);
        }
        /* Cluster icon button in an "active/open" state — used by the
           camera-spoof icon when its inline panel is open. */
        .ws-controls-popover .sw-glass-group .sw-btn.is-active {
            background: rgba(255, 255, 255, 0.14);
            color: rgba(255, 255, 255, 0.98);
        }
        :root[data-theme="light"] .ws-controls-popover .sw-glass-group .sw-btn:hover:not(:disabled) {
            background: rgba(0, 0, 0, 0.06);
        }

        /* ── Hover tooltip (Apple-style pill above the button) ────────
           Any button or interactive element inside the controls popover
           with a [data-tip="..."] attribute gets a small dark pill that
           fades in after a short delay. Pure CSS, no JS. */
        .ws-controls-host [data-tip] {
            position: relative;
        }
        .ws-controls-host [data-tip]:hover::after {
            content: attr(data-tip);
            position: absolute;
            bottom: calc(100% + 6px);
            left: 50%;
            transform: translateX(-50%);
            background: rgba(20, 20, 22, 0.96);
            color: rgba(255, 255, 255, 0.95);
            padding: 4px 8px;
            border: 0.5px solid rgba(255, 255, 255, 0.10);
            border-radius: 6px;
            font-size: 11px;
            font-weight: 500;
            letter-spacing: 0.01em;
            white-space: nowrap;
            pointer-events: none;
            opacity: 0;
            animation: ws-tip-in 0.12s ease 0.4s forwards;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
            z-index: 60;
        }
        .ws-controls-host [data-tip]:active::after,
        .ws-controls-host [data-tip]:disabled::after,
        .ws-controls-host [data-tip][aria-disabled="true"]::after {
            display: none;
        }
        @keyframes ws-tip-in { to { opacity: 1; } }
        :root[data-theme="light"] .ws-controls-host [data-tip]:hover::after {
            background: rgba(245, 245, 247, 0.96);
            color: rgba(0, 0, 0, 0.92);
            border-color: rgba(0, 0, 0, 0.10);
        }

        /* ── Camera-spoof drop zone ───────────────────────────────────
           Compact dashed-border zone inside the controls popover that
           accepts a file drop and routes it to /api/phones/.../vcam/media.
           Sibling of the existing phone-preview drop pipeline; events
           are scoped via stopPropagation in handleVcamDrop so a drop on
           this zone does NOT bubble up to the document-level Photos
           camera-roll handler. */
        .ws-vcam-drop {
            border: 1px dashed var(--sw-fg-tertiary, rgba(255, 255, 255, 0.40));
            border-radius: 10px;
            padding: 14px 12px;
            text-align: center;
            cursor: pointer;
            transition:
                border-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                background-color 0.18s cubic-bezier(0.4, 0, 0.2, 1),
                color 0.18s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .ws-vcam-drop:hover {
            border-style: solid;
            border-color: rgba(255, 255, 255, 0.30);
            color: rgba(255, 255, 255, 0.85);
        }
        .ws-vcam-drop.is-dragover {
            border: 1px solid var(--sw-green, #34c759);
            background: rgba(var(--sw-green-rgb, 52, 199, 89), 0.10);
            color: var(--sw-green, #34c759);
        }
        .ws-vcam-drop.is-uploading {
            border-color: var(--sw-blue, #007aff);
            animation: ws-vcam-pulse 1.4s ease-in-out infinite;
        }
        .ws-vcam-drop[aria-disabled="true"] {
            opacity: 0.45;
            cursor: not-allowed;
            pointer-events: none;
        }
        @keyframes ws-vcam-pulse {
            0%, 100% { border-color: rgba(0, 122, 255, 0.50); }
            50%      { border-color: rgba(0, 122, 255, 1.00); }
        }
        .ws-vcam-progress {
            height: 3px;
            border-radius: 2px;
            background: rgba(255, 255, 255, 0.08);
            overflow: hidden;
            margin-top: 8px;
        }
        .ws-vcam-progress-fill {
            height: 100%;
            background: var(--sw-blue, #007aff);
            transition: width 0.15s linear;
        }

        /* Tab context menu items */
        .ws-tab-menu-item {
            width: 100%;
            text-align: left;
            padding: 7px 12px;
            font-size: 13px;
            color: rgba(255,255,255,0.6);
            display: flex;
            align-items: center;
            gap: 10px;
            transition: background 0.12s ease, color 0.12s ease;
            border: none;
            background: none;
            cursor: pointer;
        }
        .ws-tab-menu-item:hover {
            background: rgba(255,255,255,0.06);
            color: #fff;
        }
        .ws-tab-menu-item-danger {
            color: rgba(248,113,113,0.7);
        }
        .ws-tab-menu-item-danger:hover {
            color: #fca5a5;
            background: rgba(220,38,38,0.08);
        }

        /* ── Phone Start/Stop button animations (Apple-style) ── */
        .phone-btn-start, .phone-btn-stop {
            transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            transform: scale(1);
            position: relative;
            overflow: hidden;
        }
        .phone-btn-start::after, .phone-btn-stop::after {
            content: '';
            position: absolute;
            inset: 0;
            border-radius: inherit;
            opacity: 0;
            transition: opacity 0.3s ease;
            pointer-events: none;
        }

        /* Start: green hover */
        .phone-btn-start:not(:disabled):not(.phone-btn-active):hover {
            background: #22c55e !important;
            color: #fff !important;
            transform: scale(1.03);
            box-shadow: 0 0 20px rgba(34, 197, 94, 0.3), 0 4px 12px rgba(0,0,0,0.2);
        }
        .phone-btn-start:not(:disabled):not(.phone-btn-active):hover::after {
            opacity: 1;
            background: linear-gradient(135deg, rgba(255,255,255,0.15) 0%, transparent 50%);
        }
        .phone-btn-start:not(:disabled):not(.phone-btn-active):active {
            transform: scale(0.97);
            background: #16a34a !important;
            box-shadow: 0 0 8px rgba(34, 197, 94, 0.2);
        }

        /* Start: active/running state */
        .phone-btn-start.phone-btn-active {
            animation: activeGlow 2.5s ease-in-out infinite;
        }
        .phone-btn-start.phone-btn-active .phone-btn-icon {
            animation: iconBounce 2s ease-in-out infinite;
        }

        /* Stop: red hover */
        .phone-btn-stop:not(:disabled):hover {
            background: rgba(127, 29, 29, 0.8) !important;
            color: #fca5a5 !important;
            border: 1px solid rgba(239, 68, 68, 0.2);
            transform: scale(1.03);
            box-shadow: 0 0 16px rgba(239, 68, 68, 0.15), 0 4px 12px rgba(0,0,0,0.2);
        }
        .phone-btn-stop:not(:disabled):hover::after {
            opacity: 1;
            background: linear-gradient(135deg, rgba(255,255,255,0.08) 0%, transparent 50%);
        }
        .phone-btn-stop:not(:disabled):hover .phone-btn-icon {
            animation: iconShake 0.4s ease-in-out;
        }
        .phone-btn-stop:not(:disabled):active {
            transform: scale(0.97);
            background: rgba(127, 29, 29, 1) !important;
            box-shadow: 0 0 6px rgba(239, 68, 68, 0.1);
        }

        /* Disabled state */
        .phone-btn-start:disabled, .phone-btn-stop:disabled {
            opacity: 0.3;
            transform: scale(1) !important;
            box-shadow: none !important;
        }

        /* Pulse dot for running state */
        .phone-btn-pulse {
            animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
        }

        /* Open-in-window button consistency */
        .phone-btn-start:not(:disabled):hover ~ button,
        .phone-btn-stop:not(:disabled):hover ~ button {
            transition: all 0.2s ease;
        }

        @keyframes activeGlow {
            0%, 100% { box-shadow: 0 0 12px rgba(16, 185, 129, 0.15); }
            50% { box-shadow: 0 0 20px rgba(16, 185, 129, 0.3), 0 0 40px rgba(16, 185, 129, 0.1); }
        }
        @keyframes iconBounce {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.15); }
        }
        @keyframes iconShake {
            0%, 100% { transform: rotate(0deg); }
            25% { transform: rotate(-8deg); }
            75% { transform: rotate(8deg); }
        }
        @keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.4; }
        }

        /* ──────────────────────────────────────────────────────────────────
           Reduced-motion (WCAG 2.3.3 Animation from Interactions).
           When the OS "Reduce Motion" preference is on, neutralize the panel's
           decorative/looping motion — spinners, pulses, conic-gradient sweeps,
           marquees, slide-ins, the toast meter — while keeping the UI fully
           functional (state is still conveyed by colour/opacity, not motion).
           Near-instant durations (0.01ms) instead of `none` so JS that waits on
           `animationend`/`transitionend` still fires. A global catch-all plus a
           few explicit `animation: none` overrides for the infinite loops the
           catch-all can't fully tame (conic spinners that look static-but-busy).
           ────────────────────────────────────────────────────────────────── */
        @media (prefers-reduced-motion: reduce) {
            *, *::before, *::after {
                animation-duration: 0.01ms !important;
                animation-iteration-count: 1 !important;
                transition-duration: 0.01ms !important;
                scroll-behavior: auto !important;
            }
            /* Looping indeterminate indicators: stop entirely, hold a steady
               readable frame rather than a frozen mid-spin smear. */
            .mv-phone-waiting::before,
            .mv-phone-done::before,
            .app-toast__meter-fill,
            .ws-vcam-drop.is-uploading,
            [class*="spin"],
            .sw-pulse-dot,
            .sw-status-dot {
                animation: none !important;
            }
        }

        /* ══════════════════════════════════════════════════════════════
           PHONE-STAGE REDESIGN (2026-07)
           "Device on a lit stage": a static Console-Aurora ambient wash,
           a grounded device pedestal, functional gutter rails, and an
           opt-in gesture veil. Additive — the only structural change is
           the live PhonePreview box going transparent (set in the TSX) so
           this ambient shows through the gutters.
           ══════════════════════════════════════════════════════════════ */

        /* ── Console Aurora: static depth wash + masked micro-grid + spotlight.
           Zero runtime cost; the only moving pixel is the JS <ConsoleNebula/>
           which self-disables under prefers-reduced-motion. ── */
        .console-ambient {
            position: fixed; inset: 0; z-index: 0; pointer-events: none; contain: paint;
            overflow: hidden;
            background:
                radial-gradient(ellipse 70% 60% at 8% 0%,     rgba(var(--sw-blue-rgb), 0.10), transparent 60%),
                radial-gradient(ellipse 60% 55% at 100% 100%, rgba(var(--sw-blue-rgb), 0.06), transparent 55%),
                var(--sw-window-bg);
        }
        .console-ambient::before {
            content: ""; position: absolute; inset: 0;
            background-image:
                linear-gradient(rgba(255, 255, 255, 0.020) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255, 255, 255, 0.020) 1px, transparent 1px);
            background-size: 48px 48px;
            -webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 45%, #000 20%, transparent 85%);
                    mask-image: radial-gradient(ellipse 90% 80% at 50% 45%, #000 20%, transparent 85%);
        }
        .console-ambient::after {
            content: ""; position: absolute; inset: 0;
            background: radial-gradient(ellipse 34% 62% at 50% 50%,
                rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.015) 45%, transparent 70%);
        }

        /* ── Device stage: rails flank the preview; the preview grows + centers ── */
        .device-stage { display: flex; align-items: stretch; min-height: 0; position: relative; z-index: 1; }
        .device-stage > .phone-preview-container { flex: 1 1 auto; min-width: 0; }

        /* pedestal glow pooled under the phone (behind the iframe) */
        .phone-preview.is-live::after {
            content: ""; position: absolute; left: 50%; bottom: 6%;
            width: min(62%, 520px); height: 120px; transform: translateX(-50%);
            background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(var(--sw-blue-rgb), 0.16), transparent 70%);
            filter: blur(26px); pointer-events: none; z-index: 0;
        }
        /* device framing: hairline bezel + floor shadow + faint accent bloom */
        .phone-preview.is-live iframe {
            position: relative; z-index: 1;
            box-shadow:
                0 0 0 1px rgba(255, 255, 255, 0.06),
                0 0 0 6px rgba(255, 255, 255, 0.02),
                0 30px 80px -20px rgba(0, 0, 0, 0.65),
                0 0 60px -12px rgba(var(--sw-blue-rgb), 0.20);
        }

        /* ── Gutter rails — floating translucent glass islands ── */
        .stage-rail {
            display: flex; flex-direction: column; align-items: center; gap: 5px;
            flex-shrink: 0; width: 74px; padding: 12px 8px; margin: 14px 0; align-self: center;
            background: var(--sw-material-thin-bg);
            -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur);
            border: 0.5px solid var(--sw-separator); border-radius: 22px;
            box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.05), 0 22px 50px -24px rgba(0, 0, 0, 0.55);
        }
        .stage-rail--left { margin-left: 14px; }
        .stage-rail--right { margin-right: 14px; }
        .stage-rail-sep { width: 58%; height: 0.5px; background: var(--sw-separator); margin: 5px 0; }
        .stage-rail-label { font-size: 9px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sw-tertiary-label); }
        .rail-btn {
            display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
            width: 54px; padding: 8px 4px; border-radius: 14px; color: var(--sw-secondary-label);
            background: transparent; border: 0.5px solid transparent; cursor: pointer;
            transition: background 0.16s ease, color 0.16s ease, border-color 0.16s ease, transform 0.08s ease;
        }
        .rail-btn .ph { font-size: 18px; line-height: 1; }
        .rail-btn small { font-size: 9px; letter-spacing: -0.01em; line-height: 1.1; }
        .rail-btn:hover:not(:disabled) { background: var(--sw-fill-quaternary); color: var(--sw-label); }
        /* press feedback is driven by GSAP (see StageRails RailBtn), not CSS */
        .rail-btn:disabled { opacity: 0.32; cursor: default; }
        .rail-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--sw-keyboard-focus-ring); border-color: rgba(255, 255, 255, 0.25); }
        .rail-btn.is-on { background: rgba(var(--sw-blue-rgb), 0.16); color: #fff; border-color: rgba(var(--sw-blue-rgb), 0.42); }
        .rail-chip {
            display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center;
            font-size: 9px; letter-spacing: 0.01em; color: var(--sw-tertiary-label); max-width: 62px; line-height: 1.25;
        }
        .rail-chip .sw-status-dot { width: 7px; height: 7px; }

        /* ── Gesture veil over the iframe (opt-in; pointer-events off by default
           so normal VNC tapping is never blocked) ── */
        .gesture-overlay { position: absolute; z-index: 8; pointer-events: none; touch-action: none; border-radius: 8px; }
        .gesture-overlay.is-active {
            pointer-events: auto; cursor: grab;
            box-shadow: inset 0 0 0 2px rgba(var(--sw-blue-rgb), 0.55), inset 0 0 44px -8px rgba(var(--sw-blue-rgb), 0.35);
        }
        .gesture-overlay.is-active:active { cursor: grabbing; }
        .gesture-hint {
            position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
            display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
            font-size: 11px; font-weight: 600; letter-spacing: -0.01em; color: #fff; white-space: nowrap;
            background: rgba(var(--sw-blue-rgb), 0.92); box-shadow: 0 6px 20px -6px rgba(0, 0, 0, 0.6);
        }
        .gesture-ripple {
            position: absolute; width: 44px; height: 44px; margin: -22px 0 0 -22px; border-radius: 999px;
            border: 2px solid rgba(var(--sw-blue-rgb), 0.9); pointer-events: none;
            animation: gesture-ripple 0.5s ease-out forwards;
        }
        @keyframes gesture-ripple { from { transform: scale(0.3); opacity: 0.7; } to { transform: scale(1.6); opacity: 0; } }

        /* ── Responsive: condense rails, then hide; fix mobile toolbar overflow ── */
        @media (max-width: 1024px) {
            .stage-rail { width: 56px; padding: 10px 6px; }
            .rail-btn { width: 44px; }
            .rail-btn small, .stage-rail-label { display: none; }
        }
        @media (max-width: 680px) {
            .stage-rail { display: none; }
            /* stop the toolbar action row from eating the top third — single scroll strip */
            .ws-controls-host .flex-wrap { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
            .ws-controls-host .flex-wrap::-webkit-scrollbar { display: none; }
        }
        @media (prefers-reduced-motion: reduce) {
            .rail-btn, .gesture-overlay { transition: none; }
            .gesture-ripple { animation: none; opacity: 0; }
        }

        /* ══════════════════════════════════════════════════════════════
           DISPATCH DECK — unified left-rail app shell (2026-07)
           PanelShell becomes a 3-column grid: persistent left sidebar
           (device controls + section nav), always-mounted center stage,
           right agent-chat dock. Replaces the old top bar + hidden
           hamburger sheet (kept only for the <900px mobile fallback).
           ══════════════════════════════════════════════════════════════ */
        :root { --deck-rail-w: 288px; --deck-chat-w: 44px; }

        .deck-grid {
            display: grid;
            grid-template-columns: var(--deck-rail-w) minmax(0, 1fr) var(--deck-chat-w);
            height: 100dvh; position: relative; z-index: 10;
        }
        /* width driven by data-* attrs (not inline vars) so the responsive media
           queries below — at matching specificity — can still win. */
        .deck-grid[data-rail="collapsed"] { --deck-rail-w: 76px; }
        .deck-grid[data-chat="pinned"] { --deck-chat-w: clamp(320px, 24vw, 420px); }
        /* chat dock switched off (AI_FEATURES, lib/features.ts): no right track */
        .deck-grid[data-chat="off"] { --deck-chat-w: 0px; }

        /* ── Column 1 · left sidebar ── */
        .deck-rail {
            position: relative; display: flex; flex-direction: column; min-height: 0;
            background: var(--sw-material-thin-bg);
            -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur);
            border-right: 0.5px solid var(--sw-separator); overflow: hidden;
            transition: width 0.28s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .deck-rail-head { flex-shrink: 0; padding: 12px 12px 11px; border-bottom: 0.5px solid var(--sw-separator); display: flex; flex-direction: column; gap: 9px; }
        .deck-back { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; color: var(--sw-secondary-label); font-size: 12px; font-weight: 500; padding: 5px 8px; border-radius: 8px; transition: background 0.15s ease, color 0.15s ease; }
        .deck-back:hover { background: var(--sw-fill-quaternary); color: var(--sw-label); }
        .deck-identity { display: flex; align-items: center; gap: 8px; min-width: 0; }
        .deck-identity-label { font-size: 15px; font-weight: 700; letter-spacing: -0.02em; color: var(--sw-label); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .deck-identity-sub { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--sw-tertiary-label); }

        .deck-actions { flex-shrink: 0; display: flex; gap: 6px; padding: 12px 12px 4px; }
        .deck-actions .sw-btn { flex: 1 1 auto; justify-content: center; }
        .deck-actions .deck-reboot { flex: 0 0 auto; }

        .deck-cluster { flex-shrink: 0; margin: 12px; padding: 10px; border-radius: 12px; background: var(--sw-fill-quaternary); }
        .deck-eyebrow { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--sw-tertiary-label); }
        .deck-cluster .deck-eyebrow { display: block; margin: 0 2px 8px; }
        .deck-cluster-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
        .deck-cluster-btn { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 9px 3px; border-radius: 10px; background: transparent; color: var(--sw-secondary-label); border: 0.5px solid transparent; cursor: pointer; transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.08s ease; }
        .deck-cluster-btn .ph { font-size: 17px; line-height: 1; }
        .deck-cluster-btn small { font-size: 9.5px; letter-spacing: -0.01em; line-height: 1.1; text-align: center; }
        .deck-cluster-btn:hover:not(:disabled) { background: var(--sw-fill-tertiary); color: var(--sw-label); }
        .deck-cluster-btn:active:not(:disabled) { transform: scale(0.95); }
        .deck-cluster-btn:disabled { opacity: 0.34; cursor: default; }
        .deck-cluster-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--sw-keyboard-focus-ring); }
        .deck-cluster-btn.is-on { background: rgba(var(--sw-blue-rgb), 0.16); color: #fff; border-color: rgba(var(--sw-blue-rgb), 0.4); }
        .deck-cluster-btn.is-accent { color: var(--sw-blue); }

        /* telemetry, relocated into the sidebar (styled like the device cluster) */
        .deck-rail-telemetry { flex-shrink: 0; margin: 0 12px 12px; padding: 10px 11px; border-radius: 12px; background: var(--sw-fill-quaternary); }
        .deck-rail-telemetry .deck-eyebrow { display: block; margin: 0 1px 6px; }
        .deck-rail-telemetry .deck-stat { padding: 3px 0; }
        .deck-rail.is-collapsed .deck-rail-telemetry { display: none; }

        .deck-nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 6px 8px 10px; }
        .deck-nav .deck-eyebrow { padding: 8px 10px 4px; }
        .deck-nav-item { position: relative; display: flex; align-items: center; gap: 11px; padding: 8px 11px; border-radius: 9px; color: var(--sw-secondary-label); font-size: 13.5px; font-weight: 500; letter-spacing: -0.01em; white-space: nowrap; transition: background 0.15s ease, color 0.15s ease; }
        .deck-nav-item .ph { font-size: 18px; opacity: 0.9; flex-shrink: 0; }
        .deck-nav-item:hover { background: var(--sw-fill-quaternary); color: var(--sw-label); }
        .deck-nav-item.is-active { background: rgba(var(--sw-blue-rgb), 0.14); color: var(--sw-label); font-weight: 600; }
        .deck-nav-item.is-active .ph { color: var(--sw-blue); opacity: 1; }
        .deck-nav-item.is-active::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 18px; border-radius: 0 3px 3px 0; background: var(--sw-blue); }
        .deck-nav-item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--sw-keyboard-focus-ring); }
        .deck-nav-divider { height: 0.5px; background: var(--sw-separator); margin: 8px 10px; }
        .deck-rail-foot { flex-shrink: 0; border-top: 0.5px solid var(--sw-separator); padding: 8px; display: flex; align-items: center; gap: 6px; }
        .deck-foot-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 32px; padding: 0 10px; border-radius: 9px; color: var(--sw-secondary-label); background: var(--sw-fill-quaternary); border: 0.5px solid transparent; font-size: 12px; cursor: pointer; transition: background 0.15s ease, color 0.15s ease; }
        .deck-foot-btn:hover { background: var(--sw-fill-tertiary); color: var(--sw-label); }
        .deck-foot-btn.deck-collapse { flex: 1; }

        /* collapsed (icon-only) rail */
        .deck-rail.is-collapsed .deck-identity-label,
        .deck-rail.is-collapsed .deck-identity-sub,
        .deck-rail.is-collapsed .deck-back span,
        .deck-rail.is-collapsed .deck-cluster > .deck-eyebrow,
        .deck-rail.is-collapsed .deck-nav > .deck-eyebrow,
        .deck-rail.is-collapsed .deck-nav-item > span,
        .deck-rail.is-collapsed .deck-cluster-btn small,
        .deck-rail.is-collapsed .deck-actions .sw-btn span,
        .deck-rail.is-collapsed .deck-foot-btn span { display: none; }
        .deck-rail.is-collapsed .deck-cluster { padding: 8px 6px; }
        .deck-rail.is-collapsed .deck-cluster-grid { grid-template-columns: 1fr; }
        .deck-rail.is-collapsed .deck-nav-item { justify-content: center; gap: 0; padding: 9px 0; }
        .deck-rail.is-collapsed .deck-actions { flex-direction: column; }
        .deck-rail.is-collapsed .deck-foot-btn.deck-collapse { flex: 1; }

        /* ── Column 2 · center stage (always mounted; hidden under section) ── */
        .deck-center { position: relative; min-width: 0; height: 100dvh; }
        /* container-type: the gutters' min-width (below) must yield to the phone
           when the lane budget genuinely runs out — e.g. expanded rail AND open
           chat on a ~1200px window, where holding 2x212px shrinks the screen to
           ~90px. A viewport media query can't see that: the same viewport has
           very different stage space depending on rail/chat state. Querying the
           stage's own content box does (and the chat's reserved padding-right is
           excluded from it, so an open chat correctly reads as less room). */
        .deck-stage { position: relative; min-width: 0; height: 100%; container-type: inline-size; display: flex; align-items: stretch; transition: padding-right 0.16s ease; }
        /* while the chat floats over the right gutter, pad the stage so its
           content (incl. the bezel rail) sits clear of the chat panel. */
        @media (min-width: 901px) {
            .deck-grid[data-chat="overlay"] .deck-stage { padding-right: clamp(320px, 24vw, 420px); }
        }
        .deck-stage.is-hidden { visibility: hidden; }
        .deck-section-layer { position: absolute; inset: 0; z-index: 30; background: var(--sw-window-bg); }
        /* No z-index here: keeping the gutter a non-stacking-context lets a
           dragged island's own z-index (.is-floating 4 / dragging 50) escape it
           and paint above the phone AND the opposite gutter when carried across. */
        /* min-width is load-bearing: the phone column is `flex: 1 1 auto`, so it
           claims its content width FIRST and a basis-0 gutter gets whatever is
           left — which crushed the cards to ~90px (values ellipsised to "P…",
           container name gone, action buttons one-per-row). The floor makes the
           phone yield instead; it auto-fits its 430:932 box either way.
           container-type lets the cards restyle themselves off the GUTTER's
           width (queried below) rather than the viewport, so the compact form
           kicks in whenever the lane is genuinely narrow — expanded rail, open
           chat, small window, any combination. */
        .deck-gutter { flex: 1 1 0; min-width: 212px; max-width: 320px; container-type: inline-size; display: flex; flex-direction: column; justify-content: center; gap: 12px; padding: 16px 12px; position: relative; }
        /* gutters hug the phone (cards near the device, not the window edges) so
           the bezel rail stays reachable even under the chat overlay */
        .deck-gutter--left { align-items: flex-end; }
        .deck-gutter--right { align-items: flex-start; }
        .deck-gutter-card { width: 100%; max-width: 260px; border-radius: 16px; padding: 13px 14px; background: var(--sw-material-thin-bg); -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur); border: 0.5px solid var(--sw-separator); box-shadow: inset 0 1px 0 0 rgba(255,255,255,0.05), 0 18px 40px -22px rgba(0,0,0,0.55); }
        .deck-gutter-card .deck-eyebrow { display: block; margin-bottom: 9px; }
        .deck-stat { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 4px 0; font-size: 12px; }
        .deck-stat-k { display: inline-flex; align-items: center; gap: 6px; color: var(--sw-tertiary-label); }
        .deck-stat-k .ph { font-size: 13px; }
        .deck-stat-v { color: var(--sw-label); font-weight: 600; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 130px; text-align: right; }
        .deck-stage-center { flex: 1 1 auto; min-width: 0; position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; padding: 16px 4px; }
        .deck-stage-center .phone-preview-container { flex: 1 1 auto; align-self: stretch; }
        .deck-underbezel { flex-shrink: 0; display: flex; align-items: center; gap: 8px; padding-top: 10px; }
        /* Screen controls row above the phone (Reconnect / Reboot · quality
           picker). Width-matched to the phone's own max box so the buttons sit
           at the screen's top-left and the picker at its top-right — attached to
           the device, never on top of it. */
        .deck-stage-center .phone-topbar { flex-shrink: 0; display: flex; align-items: center; gap: 6px; width: min(100%, 430px); margin-bottom: 4px; }
        .deck-stage-center .phone-topbar .phone-topbar-gap { flex: 1 1 auto; }
        /* neutralize the picker's corner-absolute placement; keep it a
           positioning context so .vq-menu still anchors to the chip. */
        .deck-stage-center .phone-topbar .vq-picker { position: relative; top: auto; right: auto; }

        /* ── Compact stacked islands (Container / HAR cards) ──
           Share the glass .deck-gutter-card shell but pack a live current-state
           headline + a tight action row into the narrow gutter. */
        .deck-mini { max-width: 246px; }
        .deck-mini-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
        .deck-mini-head .deck-eyebrow { margin-bottom: 0; }
        .deck-mini-app { display: inline-flex; align-items: center; gap: 4px; max-width: 120px; padding: 2px 7px; border-radius: 999px; font-size: 10.5px; font-weight: 600; color: var(--sw-secondary-label); background: var(--sw-fill-quaternary); border: 0.5px solid var(--sw-separator); cursor: pointer; transition: background 0.15s ease, color 0.15s ease; }
        .deck-mini-app:hover:not(:disabled) { color: var(--sw-label); background: var(--sw-fill-tertiary); }
        .deck-mini-app:disabled { opacity: 0.5; cursor: default; }
        .deck-mini-app .ph { font-size: 12px; flex-shrink: 0; }
        .deck-mini-input { width: 100%; margin-bottom: 8px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; }
        .deck-mini-off { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--sw-tertiary-label); padding: 3px 0; }
        .deck-mini-hint { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--sw-tertiary-label); padding: 4px 1px; }

        /* Active-state headline (current container / recording status) */
        .deck-cur { display: flex; align-items: center; gap: 8px; padding: 7px 9px; margin-bottom: 8px; border-radius: 11px; background: var(--sw-fill-quaternary); border: 0.5px solid var(--sw-separator); }
        .deck-cur-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: var(--sw-green); box-shadow: 0 0 0 3px rgba(var(--sw-green-rgb), 0.16); }
        .deck-cur-dot.is-idle { background: var(--sw-gray2); box-shadow: none; }
        .deck-cur-dot.is-rec { background: var(--sw-red); box-shadow: 0 0 0 3px rgba(var(--sw-red-rgb), 0.18); animation: deck-rec-pulse 1.4s ease-in-out infinite; }
        @keyframes deck-rec-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
        .deck-cur-name { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 700; color: var(--sw-label); letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
        .deck-cur-tag { flex-shrink: 0; font-size: 9.5px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--sw-tertiary-label); }

        /* Switch list */
        .deck-cx-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; max-height: 168px; overflow-y: auto; }
        .deck-cx-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 9px; background: transparent; border: none; cursor: pointer; text-align: left; color: var(--sw-secondary-label); transition: background 0.13s ease, color 0.13s ease; }
        .deck-cx-row:hover:not(:disabled) { background: var(--sw-fill-tertiary); color: var(--sw-label); }
        .deck-cx-row:disabled { opacity: 0.5; cursor: default; }
        .deck-cx-bullet { font-size: 11px; color: var(--sw-quaternary-label); flex-shrink: 0; }
        .deck-cx-name { flex: 1 1 auto; min-width: 0; font-size: 12px; }
        .deck-cx-go { font-size: 13px; color: var(--sw-tertiary-label); flex-shrink: 0; }
        .deck-cx-row:hover:not(:disabled) .deck-cx-go { color: var(--sw-blue); }

        /* Action row (New / Reset / Snap  |  Start / Stop / Download) */
        .deck-cx-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; }
        .deck-cx-mini { display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 8px; font-size: 11px; font-weight: 600; color: var(--sw-secondary-label); background: var(--sw-fill-quaternary); border: 0.5px solid var(--sw-separator); cursor: pointer; transition: background 0.13s ease, color 0.13s ease, opacity 0.13s ease; text-decoration: none; }
        .deck-cx-mini:hover:not(:disabled):not(.is-disabled) { color: var(--sw-label); background: var(--sw-fill-tertiary); }
        .deck-cx-mini:disabled, .deck-cx-mini.is-disabled { opacity: 0.45; cursor: default; pointer-events: none; }
        .deck-cx-mini .ph { font-size: 12px; }
        .deck-cx-mini.is-accent { color: #fff; background: var(--sw-blue); border-color: transparent; }
        .deck-cx-mini.is-accent:hover:not(:disabled) { filter: brightness(1.08); color: #fff; }
        .deck-cx-mini.is-destructive { color: var(--sw-red); }
        .deck-cx-mini.is-destructive:hover:not(:disabled) { background: rgba(var(--sw-red-rgb), 0.12); color: var(--sw-red); }
        .deck-cx-icononly { padding: 4px 6px; margin-left: auto; }
        .deck-cx-add { display: flex; align-items: center; gap: 5px; width: 100%; }
        .deck-cx-add .sw-input { flex: 1 1 auto; min-width: 0; font-size: 11px; }

        /* ── Compact island form for a narrow gutter lane ──
           Keyed to the GUTTER's width (container query), not the viewport, so it
           engages on any combination that squeezes the lane. Side-by-side
           label/value only fits while the card is wide; below that the value
           gets its own line at full width instead of being ellipsised down to
           one character. */
        @container (max-width: 236px) {
            .deck-gutter-card { padding: 11px 12px; }
            .deck-stat { flex-direction: column; align-items: flex-start; gap: 0; padding: 3px 0; }
            .deck-stat-v { max-width: 100%; text-align: left; font-size: 11.5px; }
            /* Wrap the headline instead of hiding the tag: the tag is redundant
               on the Container card ("active", already conveyed by the dot) but
               is real data on the HAR card ("0 entries"), so it has to survive.
               Name takes the first line, tag drops below it. */
            .deck-cur { padding: 6px 8px; flex-wrap: wrap; row-gap: 1px; }
            .deck-cur-name { flex: 1 1 100%; }
            /* let the refresh button pack inline instead of being pushed to a
               row of its own by margin-left:auto after the row wraps. */
            .deck-cx-icononly { margin-left: 0; }
        }

        /* ── Floating / dockable islands (Session card + bezel rail) ──
           The island stays IN FLOW inside its gutter (so it scales + re-centers
           with the layout on every viewport); useDockable only offsets it with a
           transform, clamped to its own side lane. `position: relative` + z-index
           lets it paint above the phone while dragging without leaving flow. */
        .is-floating { position: relative; z-index: 4; will-change: transform; cursor: grab; touch-action: none; transition: box-shadow 0.2s ease, opacity 0.2s ease; }
        /* soft lift while a drag is live — floats above the phone (z-index) so it
           can be carried across to the other side. */
        .is-floating.is-dragging { z-index: 50; cursor: grabbing; opacity: 0.92; box-shadow: 0 30px 60px -24px rgba(0,0,0,0.7); border-radius: 16px; }
        .is-floating.will-dock { outline: 1px solid rgba(var(--sw-red-rgb), 0.55); outline-offset: 3px; }

        /* Drop-zone: red faded band across the bottom of the stage. Fades in on
           drag, brightens + pulses once the island is over the dock. Never eats
           pointer events. */
        .deck-dropzone {
            position: absolute; left: 0; right: 0; bottom: 0; height: 168px; z-index: 2;
            pointer-events: none; opacity: 0; transform: translateY(12px);
            display: flex; align-items: flex-end; justify-content: center; padding-bottom: 22px;
            background: linear-gradient(to top, rgba(var(--sw-red-rgb), 0.26), rgba(var(--sw-red-rgb), 0.08) 46%, transparent 82%);
            transition: opacity 0.34s ease, transform 0.34s ease, background 0.3s ease;
        }
        .deck-dropzone.is-visible { opacity: 1; transform: translateY(0); }
        .deck-dropzone.is-armed {
            background: linear-gradient(to top, rgba(var(--sw-red-rgb), 0.5), rgba(var(--sw-red-rgb), 0.18) 52%, transparent 88%);
            animation: dz-pulse 1.15s ease-in-out infinite;
        }
        .deck-dropzone-hint {
            display: inline-flex; align-items: center; gap: 7px;
            font-size: 11px; font-weight: 650; letter-spacing: 0.05em; text-transform: uppercase;
            color: rgba(var(--sw-red-rgb), 0.95); padding: 7px 15px; border-radius: 999px;
            border: 1px dashed rgba(var(--sw-red-rgb), 0.55); background: rgba(var(--sw-red-rgb), 0.10);
            -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
            transform: translateY(6px); opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease, color 0.25s ease, background 0.25s ease, border-color 0.25s ease;
        }
        .deck-dropzone-hint .ph { font-size: 14px; }
        .deck-dropzone.is-visible .deck-dropzone-hint { opacity: 1; transform: translateY(0); }
        .deck-dropzone.is-armed .deck-dropzone-hint {
            color: #fff; background: rgba(var(--sw-red-rgb), 0.28); border-color: rgba(var(--sw-red-rgb), 0.95);
            transform: translateY(0) scale(1.06);
        }
        @keyframes dz-pulse { 0%, 100% { opacity: 0.82; } 50% { opacity: 1; } }

        /* macOS-style dock at the bottom-center of the stage (scoped to the
           relatively-positioned .deck-stage). */
        /* Bottom-LEFT corner, not center: the phone column owns the middle (its
           Gestures / Pop out controls sit there), and the chat overlay owns the
           right — the left gutter is the only bottom strip clear of both. */
        .deck-dockbar {
            position: absolute; left: 16px; bottom: 16px; z-index: 45;
            display: flex; align-items: flex-end; gap: 10px; padding: 8px 12px; border-radius: 20px;
            background: var(--sw-material-thin-bg);
            -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur);
            border: 0.5px solid var(--sw-separator);
            box-shadow: inset 0 1px 0 0 rgba(255,255,255,0.06), 0 22px 50px -20px rgba(0,0,0,0.6);
        }
        .deck-dock-tile {
            width: 54px; height: 54px; flex: 0 0 auto; padding: 6px 3px; border-radius: 14px;
            transform-origin: bottom center; cursor: grab;
        }
        .deck-dock-tile:active { cursor: grabbing; }

        /* ── App dock — launch bar under the phone bezel ──
           IN FLOW at the bottom of .deck-stage-center (never absolute, so it
           can't collide with the island dockbar / drop-zone / chat overlay).
           Same glass shell as .deck-dockbar; tiles bounce + fisheye on hover. */
        .deck-appdock {
            flex-shrink: 0; display: flex; align-items: flex-end; gap: 8px;
            margin-top: 8px; padding: 7px 10px; border-radius: 18px;
            background: var(--sw-material-thin-bg);
            -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur);
            border: 0.5px solid var(--sw-separator);
            box-shadow: inset 0 1px 0 0 rgba(255,255,255,0.06), 0 22px 50px -20px rgba(0,0,0,0.6);
        }
        .deck-app-tile {
            width: 46px; height: 46px; flex: 0 0 auto; padding: 5px 2px; border-radius: 12px;
            transform-origin: bottom center; cursor: pointer;
        }
        .deck-app-tile:active { cursor: pointer; }
        .deck-app-tile .ph { font-size: 19px; }
        .deck-app-tile small { font-size: 8.5px; }
        /* stays visible below 900px (the center column survives) — just tighter */
        @media (max-width: 900px) {
            .deck-appdock { gap: 5px; padding: 5px 8px; }
            .deck-app-tile { width: 40px; height: 40px; }
            .deck-app-tile small { display: none; }
        }

        @media (prefers-reduced-motion: reduce) {
            .is-floating, .deck-dock-tile { will-change: auto; }
            .deck-dropzone, .deck-dropzone-hint { transition: opacity 0.2s linear; transform: none; }
            .deck-dropzone.is-armed { animation: none; }
        }
        /* float/dock only exists on desktop — below the gutter breakpoint the
           islands live in (now display:none) gutters, so clear any transform. */
        @media (max-width: 900px) {
            .is-floating { transform: none !important; z-index: auto; }
            .deck-dockbar, .deck-dropzone { display: none; }
        }

        /* pedestal glow the phone stands on (static) */
        .phone-pedestal { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: min(520px, 62%); height: 82%; pointer-events: none; z-index: 0;
            background: radial-gradient(closest-side, rgba(var(--sw-blue-rgb), 0.15), rgba(var(--sw-blue-rgb), 0.05) 55%, transparent 76%); }
        .phone-pedestal::after { content: ""; position: absolute; left: 50%; bottom: 4%; transform: translateX(-50%); width: 70%; height: 26px; border-radius: 50%; background: rgba(0,0,0,0.5); filter: blur(18px); }

        /* ── Column 3 · agent-chat dock ── */
        .deck-chat-track { position: relative; height: 100dvh; }
        .deck-chat-overlay { position: fixed; top: 0; right: 0; bottom: 0; z-index: 20; width: clamp(320px, 24vw, 420px); display: flex; flex-direction: column;
            background: var(--sw-material-thin-bg); -webkit-backdrop-filter: var(--sw-material-thin-blur); backdrop-filter: var(--sw-material-thin-blur);
            border-left: 0.5px solid var(--sw-separator); box-shadow: -24px 0 60px -30px rgba(0,0,0,0.7);
            animation: deck-chat-in 0.16s ease-out; }
        @keyframes deck-chat-in { from { transform: translateX(16px); opacity: 0; } to { transform: none; opacity: 1; } }
        .deck-chat-pinned .deck-chat-panel { position: relative; height: 100%; display: flex; flex-direction: column; border-left: 0.5px solid var(--sw-separator); }
        .deck-agent-hairline { position: absolute; top: 0; left: 0; right: 0; height: 2px; z-index: 25; background: linear-gradient(90deg, transparent, var(--sw-blue), transparent); }

        /* ── background: Telemetry Dust (one static masked node) ── */
        .console-dust { position: absolute; inset: 0; pointer-events: none;
            background: radial-gradient(ellipse 120% 120% at var(--stage-cx, 50%) var(--stage-cy, 52%), rgba(var(--sw-blue-rgb), 0.55), rgba(var(--sw-blue-rgb), 0.14) 55%, transparent 80%);
            -webkit-mask-image: var(--dust-mask); mask-image: var(--dust-mask);
            -webkit-mask-repeat: repeat; mask-repeat: repeat; -webkit-mask-size: 240px 240px; mask-size: 240px 240px;
            opacity: 0.9; }
        /* fade the dust away from the phone center so it never speckles the screen */
        .console-dust::after { content: ""; position: absolute; inset: 0;
            background: radial-gradient(ellipse 42vh 58vh at var(--stage-cx, 50%) var(--stage-cy, 52%), var(--sw-window-bg) 0 34%, transparent 74%); }
        #console-ambient.is-dim .console-dust::after { opacity: 0.35; }

        /* ── responsive ── */
        @media (max-width: 1400px) { .deck-gutter { max-width: 280px; } }
        @media (max-width: 1180px) {
            .deck-grid[data-rail] { --deck-rail-w: 76px; }
            .deck-rail .deck-identity-label, .deck-rail .deck-identity-sub, .deck-rail .deck-back span,
            .deck-rail .deck-cluster > .deck-eyebrow, .deck-rail .deck-nav > .deck-eyebrow,
            .deck-rail .deck-nav-item > span, .deck-rail .deck-cluster-btn small,
            .deck-rail .deck-actions .sw-btn span, .deck-rail .deck-foot-btn span { display: none; }
            .deck-rail .deck-cluster-grid { grid-template-columns: 1fr; }
            .deck-rail .deck-nav-item { justify-content: center; gap: 0; padding: 9px 0; }
            .deck-rail .deck-actions { flex-direction: column; }
            .deck-rail .deck-collapse { display: none; }
            .deck-rail .deck-rail-telemetry { display: none; }
            /* 240, not 200: below ~236 the container query flips the cards to
               their stacked form, and the rail is already icon-only here so the
               freed width is better spent keeping the cards readable. A cap
               under the gutter's min-width would just be ignored anyway. */
            .deck-gutter { max-width: 240px; }
        }
        /* Stage-space (not viewport) escape hatch: the gutters' min-width must
           yield to the phone when the lane budget runs out — expanded rail AND
           open chat on a ~1200px window otherwise holds 2x212px and shrinks the
           screen to ~90px. Queries .deck-stage's content box, which EXCLUDES the
           chat's reserved padding-right, so an open chat correctly reads as less
           room. Must sit after the .deck-gutter declarations: @container adds no
           specificity, so an earlier block loses to the later `display: flex`. */
        @container (max-width: 800px) {
            .deck-gutter { display: none; }
        }
        @media (max-width: 900px) {
            .deck-grid[data-rail] { --deck-rail-w: 60px; }
            .deck-gutter { display: none; }
            .deck-stage-center { padding: 8px 0; }
            .deck-chat-overlay { width: min(100vw, 420px); }
        }
        /* base FAB (hidden by default; the <560 query below — later in source —
           flips it on). MUST precede the media query so it doesn't override it. */
        .deck-mobile-fab { display: none; position: fixed; top: 10px; left: 10px; z-index: 58; width: 40px; height: 40px; align-items: center; justify-content: center; border-radius: 12px; background: var(--sw-material-regular-bg); -webkit-backdrop-filter: var(--sw-material-regular-blur); backdrop-filter: var(--sw-material-regular-blur); border: 0.5px solid var(--sw-separator); color: var(--sw-label); }
        .deck-mobile-backdrop { position: fixed; inset: 0; z-index: 55; background: rgba(0,0,0,0.5); }
        /* very narrow: the rail stays a 0-width grid item (overflow-hidden) so the
           center column keeps its 1fr track and the phone still renders; the rail
           only becomes a fixed slide-over when opened. */
        @media (max-width: 560px) {
            .deck-grid { grid-template-columns: 0 minmax(0, 1fr) var(--deck-chat-w); }
            .deck-rail { overflow: hidden; }
            .deck-grid.mobile-nav-open .deck-rail { position: fixed; top: 0; bottom: 0; left: 0; width: min(280px, 84vw); z-index: 60; overflow-y: auto; box-shadow: 24px 0 60px -20px rgba(0,0,0,0.7); }
            .deck-mobile-fab { display: inline-flex; }
            /* opened as a 280px slide-over → show full labels (undo the ≤900 icon-only) */
            .deck-grid.mobile-nav-open .deck-rail .deck-nav-item > span,
            .deck-grid.mobile-nav-open .deck-rail .deck-back span,
            .deck-grid.mobile-nav-open .deck-rail .deck-cluster-btn small,
            .deck-grid.mobile-nav-open .deck-rail .deck-actions .sw-btn span,
            .deck-grid.mobile-nav-open .deck-rail .deck-foot-btn span { display: inline; }
            .deck-grid.mobile-nav-open .deck-rail .deck-identity-label,
            .deck-grid.mobile-nav-open .deck-rail .deck-identity-sub,
            .deck-grid.mobile-nav-open .deck-rail .deck-cluster > .deck-eyebrow,
            .deck-grid.mobile-nav-open .deck-rail .deck-nav > .deck-eyebrow { display: block; }
            .deck-grid.mobile-nav-open .deck-rail .deck-cluster-grid { grid-template-columns: repeat(3, 1fr); }
            .deck-grid.mobile-nav-open .deck-rail .deck-nav-item { justify-content: flex-start; gap: 11px; padding: 8px 11px; }
            .deck-grid.mobile-nav-open .deck-rail .deck-actions { flex-direction: row; }
            .deck-grid.mobile-nav-open .deck-rail .deck-rail-telemetry { display: block; }
        }

        @media (prefers-reduced-motion: reduce) {
            .deck-rail, .deck-chat-overlay, .deck-cluster-btn { transition: none; animation: none; }
            .deck-agent-hairline { animation: none; }
        }

        /* Toolbox floating host: the Vcam/CameraRoll/Cadence popovers were
           authored `absolute top-full left-0 right-0` for the old toolbar; when
           launched from the sidebar cluster we portal them into this centered
           host and neutralize that positioning so they read as floating cards. */
        .deck-toolbox-backdrop { position: fixed; inset: 0; z-index: 54; }
        .deck-toolbox-host { position: fixed; top: 64px; left: 50%; transform: translateX(-50%); width: min(440px, 92vw); z-index: 55; }
        .deck-toolbox-host .ws-controls-popover { position: static !important; inset: auto !important; margin: 0 !important; width: 100%; }
        /* The host is viewport-centered but the chat panel owns the right band
           (overlay: fixed panel; pinned: a real grid column of the same width).
           Shift the center left by half the chat width so the floaters stay
           over the stage instead of drifting under the chat. */
        @media (min-width: 901px) {
            body:has(.deck-grid[data-chat="overlay"]) .deck-toolbox-host,
            body:has(.deck-grid[data-chat="pinned"]) .deck-toolbox-host {
                left: calc(50% - clamp(320px, 24vw, 420px) / 2);
            }
        }
        @media (max-width: 900px) { .deck-toolbox-host { top: 52px; } }
        /* HAR recorder: non-modal corner card (no backdrop) so the phone stays
           clickable while recording. Pinned top-right; overrides the panel's own
           absolute positioning to static, same as deck-toolbox-host does. */

        /* Chat rail agent presence + unread badge (also referenced from globals). */
        .sw-chat-rail { position: relative; }
        .deck-presence-ring { position: relative; display: inline-flex; align-items: center; justify-content: center; }
        .deck-presence-ring[data-presence="acting"]::after,
        .deck-presence-ring[data-presence="needs-input"]::after {
            content: ""; position: absolute; inset: -5px; border-radius: 999px; border: 2px solid var(--sw-blue);
        }
        .deck-presence-ring[data-presence="needs-input"]::after { border-color: var(--sw-orange); }
        @media (prefers-reduced-motion: no-preference) {
            .deck-presence-ring[data-presence="acting"]::after { animation: deck-ring-pulse 1.8s ease-out infinite; }
            .deck-agent-hairline { animation: deck-hairline 2.2s ease-in-out infinite; }
        }
        @keyframes deck-ring-pulse { 0% { box-shadow: 0 0 0 0 rgba(var(--sw-blue-rgb), 0.5); } 70% { box-shadow: 0 0 0 7px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
        @keyframes deck-hairline { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
        .sw-chat-badge { position: absolute; top: 4px; right: 4px; min-width: 15px; height: 15px; padding: 0 4px; border-radius: 999px; background: var(--sw-blue); color: #fff; font-size: 9px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
        .deck-agent-toast { position: fixed; right: 54px; bottom: 16px; z-index: 45; max-width: 260px; padding: 10px 12px; border-radius: 13px; display: flex; flex-direction: column; gap: 6px; }
