/* A3 Studio (chat) — conversation-first studio composition. */
const { Button, Icon, Tag, HGroup, VGroup, Filler } = window.A3DesignSystem_a87aaf;
const { Message, Composer, EmptyState, EntryChooser } = window.A3Chat;
const { PlanRail, RightDock, ConfigTree } = window.A3Chat;
const { CHAT_PROJECT, CHAT_DATASETS, CHAT_SUGGESTIONS, CHAT_SCOPE, CHAT_SCOPE_DATA, CHAT_WORKFLOWS, CHAT_PLAN, ORCHESTRATOR_PLAN, ORCHESTRATOR_THREAD, CHAT_THREAD, CHAT_THREAD_RUN, CHAT_THREAD_CONFIG, CONFIG_TREE, CONFIG_FILES, STAGE_FILES } = window.A3ChatData;


const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "mode": "config",
    "accent": "#6366f1",
    "dark": false,
    "liveBrowser": true
}/*EDITMODE-END*/;

/* Accent palettes — override the primary scale the whole UI reads from. */
const ACCENTS = {
    '#6366f1': ['#eef2ff', '#e0e7ff', '#c7d2fe', '#a5b4fc', '#818cf8', '#6366f1', '#4f46e5', '#4338ca'], // indigo
    '#8b5cf6': ['#f5f3ff', '#ede9fe', '#ddd6fe', '#c4b5fd', '#a78bfa', '#8b5cf6', '#7c3aed', '#6d28d9'], // violet
    '#3b82f6': ['#eff6ff', '#dbeafe', '#bfdbfe', '#93c5fd', '#60a5fa', '#3b82f6', '#2563eb', '#1d4ed8'], // blue
    '#10b981': ['#ecfdf5', '#d1fae5', '#a7f3d0', '#6ee7b7', '#34d399', '#10b981', '#059669', '#047857'], // emerald
};
function accentVars(hex) {
    const p = ACCENTS[hex] || ACCENTS['#6366f1'];
    const keys = ['50', '100', '200', '300', '400', '500', '600', '700'];
    const vars = { '--iso-primary-color': p[5], '--iso-primary-contrast-color': '#ffffff' };
    keys.forEach((k, i) => { vars[`--iso-primary-${k}`] = p[i]; });
    return vars;
}

function MenuPill({ icon, value, displayLabel, options, onChange, minWidth = 180 }) {
    const [open, setOpen] = React.useState(false);
    const ref = React.useRef(null);
    const opts = options.map(o => (o && o.divider ? o : (typeof o === 'object' ? o : { label: o, value: o })));
    const sel = opts.find(o => !o.divider && o.value === value) || opts.find(o => !o.divider);
    React.useEffect(() => {
        if (!open) return;
        const onDoc = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
        document.addEventListener('mousedown', onDoc);
        return () => document.removeEventListener('mousedown', onDoc);
    }, [open]);
    return (
        <div ref={ref} style={{ position: 'relative' }}>
            <HGroup gap="1" onClick={() => setOpen(o => !o)}
                style={{ height: 'var(--control-height-sm)', padding: '0 var(--sp1)', borderRadius: 'var(--radius-md)', background: open ? 'var(--iso-surface-200)' : 'var(--iso-surface-100)', fontSize: 12, cursor: 'pointer' }}>
                {icon ? <Icon name={icon} size={11} color="var(--p-text-muted-color)" /> : sel.icon ? <Icon name={sel.icon} size={11} color="var(--p-text-muted-color)" /> : null}
                <span style={{ maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{displayLabel ?? sel.label}</span>
                <Icon name="far fa-chevron-down" size={9} color="var(--p-text-muted-color)" style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .12s' }} />
            </HGroup>
            {open && (
                <div style={{ position: 'absolute', top: 'calc(100% + 5px)', left: 0, minWidth, zIndex: 60, background: 'var(--iso-surface-0)', border: '1px solid var(--iso-surface-200)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-md)', padding: 'var(--sp0-5)' }}>
                    {opts.map((o, i) => {
                        if (o.divider) return <div key={`d${i}`} style={{ height: 1, background: 'var(--iso-surface-200)', margin: '4px 6px' }} />;
                        const active = sel && o.value === sel.value;
                        return (
                            <HGroup key={String(o.value)} gap="1" onClick={() => { onChange && onChange(o.value); setOpen(false); }}
                                style={{ padding: '7px 10px', borderRadius: 'var(--radius-sm)', cursor: 'pointer', fontSize: 13, background: active ? 'var(--iso-primary-50)' : 'transparent', color: active ? 'var(--iso-primary-color)' : 'var(--p-text-color)', fontWeight: active ? 600 : 400 }}
                                onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--iso-surface-100)'; }}
                                onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}>
                                {o.icon && <Icon name={o.icon} size={12} color={active ? 'var(--iso-primary-color)' : 'var(--p-text-muted-color)'} style={{ width: 16 }} />}
                                <VGroup gap="0" style={{ flex: 1, minWidth: 0 }}>
                                    <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{o.label}</span>
                                    {o.hint && <span style={{ fontSize: 11, fontWeight: 400, color: 'var(--p-text-muted-color)' }}>{o.hint}</span>}
                                </VGroup>
                                {active && <Icon name="fas fa-check" size={11} color="var(--iso-primary-color)" />}
                            </HGroup>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

function TopBar({ projectName, siteName, mode, workflows, onProject, onSite, onChat, onConfig, onOrchestrate, tabs, activeTab, onSelectTab, onCloseTab }) {
    const isRun = mode === 'run';
    const isConfig = mode === 'config';
    const isOrch = mode === 'orchestrate';
    const workflowOptions = [
        { label: 'Project config', value: '__config', icon: 'far fa-folder-gear', hint: 'Edit workflows, schemas & libraries' },
        { label: 'Learning', value: '__orchestrate', icon: 'far fa-graduation-cap', hint: 'Learn all workflows by chaining them' },
        { label: 'Chat to MCP', value: '__chat', icon: 'far fa-plug', hint: 'Every workflow exposed as an MCP tool' },
    ];
    const modeIcon = isConfig ? 'far fa-folder-gear' : isRun ? 'far fa-plug' : 'far fa-graduation-cap';
    const modeLabel = isConfig ? 'Config' : isRun ? 'Chat to MCP' : 'Learning';
    const modeTarget = isConfig ? 'job-application' : 'All workflows';
    return (
        <HGroup gap="1" style={{ height: 'var(--sp5)', flex: '0 0 auto', padding: '0 0 0 var(--sp1-5)', borderBottom: '1px solid var(--iso-surface-200)', background: 'var(--iso-surface-0)' }}>
            <HGroup gap="1">
                <svg width="16" height="16" viewBox="0 0 128 128" fill="none">
                    <path d="M 63.999985,63.999996 14.844273,35.653537 63.999989,7.3070782 113.15569,35.653537 V 92.346454 L 63.999989,120.69292 14.844273,92.346454 V 54.551177 l 49.155716,28.346457 16.38522,-9.448819 V 54.551177 L 47.61475,35.653537 63.999989,26.204718 96.770448,45.102357 V 82.897634 L 63.999989,101.79528 31.229511,82.897634" stroke="var(--iso-primary-color)" strokeWidth="7.2" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
                <span style={{ fontWeight: 700 }}>A3</span>
            </HGroup>
            <div style={{ width: 1, height: 18, background: 'var(--iso-surface-200)', margin: '0 4px' }} />
            <MenuPill icon={modeIcon}
                value={isRun ? '__chat' : isConfig ? '__config' : isOrch ? '__orchestrate' : projectName}
                displayLabel={`${modeLabel} · ${modeTarget}`}
                minWidth={250} options={workflowOptions}
                onChange={v => { if (v === '__chat') onChat(); else if (v === '__config') onConfig(); else if (v === '__orchestrate') onOrchestrate(); else onProject(v); }} />
            {isOrch && <MenuPill icon="far fa-globe" value={siteName} displayLabel={siteName || 'No site'} onChange={onSite}
                options={['spacex.com', 'boeing.com', 'blueorigin.com']} />}
            <Filler />
            {/* Right-dock tabs live in the top bar, aligned over the dock column */}
            <HGroup gap="0" align="stretch" style={{ height: '100%' }}>
                {tabs.map(tb => {
                    const active = tb.id === activeTab;
                    return (
                        <HGroup key={tb.id} gap="1" onClick={() => onSelectTab(tb.id)}
                            style={{ height: '100%', padding: '0 14px', cursor: 'pointer', maxWidth: 220,
                                borderLeft: '1px solid var(--iso-surface-200)',
                                borderBottom: active ? '2px solid var(--iso-primary-color)' : '2px solid transparent',
                                background: active ? 'var(--iso-surface-0)' : 'var(--iso-surface-50)',
                                color: active ? 'var(--p-text-color)' : 'var(--p-text-muted-color)' }}>
                            <Icon name={tb.icon} size={12} color={active ? 'var(--iso-primary-color)' : 'var(--p-text-muted-color)'} />
                            <span style={{ fontSize: 12, fontWeight: active ? 600 : 400, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{tb.label}</span>
                            {tb.live && <Tag value="Live" severity="warning" rounded />}
                            {tb.closable && <Icon name="far fa-xmark" size={11} color="var(--p-text-muted-color)"
                                onClick={e => { e.stopPropagation(); onCloseTab(tb.id); }} style={{ marginLeft: 2 }} />}
                        </HGroup>
                    );
                })}
            </HGroup>
        </HGroup>
    );
}

function ResizeHandle({ onStart, onDrag }) {
    const [active, setActive] = React.useState(false);
    const start = e => {
        e.preventDefault();
        const startX = e.clientX;
        if (onStart) onStart();
        setActive(true);
        document.body.style.cursor = 'col-resize';
        document.body.style.userSelect = 'none';
        const move = ev => onDrag(ev.clientX - startX);
        const up = () => {
            setActive(false);
            document.body.style.cursor = '';
            document.body.style.userSelect = '';
            window.removeEventListener('mousemove', move);
            window.removeEventListener('mouseup', up);
        };
        window.addEventListener('mousemove', move);
        window.addEventListener('mouseup', up);
    };
    return (
        <div onMouseDown={start} style={{
            width: 6, flex: '0 0 auto', cursor: 'col-resize', position: 'relative', zIndex: 5,
            marginLeft: -3, marginRight: -3, alignSelf: 'stretch',
        }}>
            <div style={{ position: 'absolute', inset: '0 2px', background: active ? 'var(--iso-primary-color)' : 'transparent', transition: 'background .12s' }}
                onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--iso-surface-300)'; }}
                onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }} />
        </div>
    );
}

function ChatStudio() {
    const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
    const mode = t.mode;
    const [entry, setEntry] = React.useState(true);
    // Flip the whole document (html/body) to the dark scheme so no light
    // surface shows behind the app — not just the app's own root.
    React.useEffect(() => {
        const el = document.documentElement;
        el.classList.toggle('theme-dark', !!t.dark);
        document.body.style.background = 'var(--iso-surface-0)';
        return () => el.classList.remove('theme-dark');
    }, [t.dark]);
    const [thread, setThread] = React.useState([]);
    const [input, setInput] = React.useState('');
    const [model, setModel] = React.useState('claude-opus-4.5');
    const [dataset, setDataset] = React.useState(CHAT_PROJECT.dataset);
    const [learnPlan, setLearnPlan] = React.useState(CHAT_PLAN.map(s => ({ ...s })));
    const cloneOrch = () => ORCHESTRATOR_PLAN.map(g => ({ ...g, stages: g.stages.map(s => ({ ...s })) }));
    const [orchPlan, setOrchPlan] = React.useState(cloneOrch);
    // Project-config file tree, built up live as the agent writes files.
    const [configPaths, setConfigPaths] = React.useState([]);
    // Which booking.com page the browser preview shows — tracks the running stage.
    const [browserStep, setBrowserStep] = React.useState('blank');
    const [busy, setBusy] = React.useState(false);
    const [streaming, setStreaming] = React.useState(false);
    const [browserOpen, setBrowserOpen] = React.useState(true);
    const [projectName, setProjectName] = React.useState(CHAT_PROJECT.title);
    const [siteName, setSiteName] = React.useState('');
    const [openFile, setOpenFile] = React.useState(null);   // { name, lines }
    const [openData, setOpenData] = React.useState(null);   // 'inputs' | 'outputs' | 'context'
    const [rightTab, setRightTab] = React.useState('browser');
    const [railW, setRailW] = React.useState(230);
    const [browserW, setBrowserW] = React.useState(400);
    const railStart = React.useRef(230);
    const browserStart = React.useRef(400);
    const scrollRef = React.useRef(null);
    const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
    const isConfig = mode === 'config';

    // Open a stage's source file in the right dock.
    const openStageFile = stage => {
        const lines = STAGE_FILES[stage] || [
            `// ${stage}.stage.ts`,
            "",
            "import { Stage } from '@athree/runner';",
            "",
            `export default class ${stage.replace(/(^|-)(\w)/g, (_, __, c) => c.toUpperCase())} extends Stage {`,
            "    async run(ctx) {",
            "        // Not learned yet — A3 will author this stage",
            "        // when the orchestrator chain reaches it.",
            "        return { matched: false };",
            "    }",
            "}",
        ];
        setOpenFile({ name: `${stage}.stage.ts`, stage, lines });
        setRightTab('file');
    };
    // Open a project-config source file (config mode).
    const openConfigFile = path => {
        const lines = CONFIG_FILES[path];
        if (!lines) return;
        setOpenFile({ name: path, stage: path, lines });
        setRightTab('file');
    };
    const openDataGroup = group => { setOpenData(group); setRightTab('data'); };

    // Tabs available in the dock, in order. In config mode: file only.
    const tabs = [];
    if (browserOpen && !isConfig) tabs.push({ id: 'browser', label: 'Browser', icon: 'far fa-display', live: true });
    if (openFile) tabs.push({ id: 'file', label: openFile.name, icon: 'far fa-file-code', closable: !isConfig });
    if (openData && !isConfig) tabs.push({ id: 'data', label: { inputs: 'Inputs', outputs: 'Outputs', context: 'State' }[openData], icon: 'far fa-table-list', closable: true });
    const dockOpen = tabs.length > 0;
    const activeTab = tabs.some(tb => tb.id === rightTab) ? rightTab : (tabs[0] && tabs[0].id);

    const closeTab = id => {
        if (id === 'browser') setBrowserOpen(false);
        if (id === 'file') setOpenFile(null);
        if (id === 'data') setOpenData(null);
        if (rightTab === id) {
            const rest = tabs.filter(tb => tb.id !== id);
            setRightTab(rest[0] ? rest[0].id : 'browser');
        }
    };

    // The canned "script" played out (streamed) for each mode.
    const SCRIPTS = { learn: CHAT_THREAD, orchestrate: ORCHESTRATOR_THREAD, run: CHAT_THREAD_RUN, config: CHAT_THREAD_CONFIG };
    const playRef = React.useRef(0);

    // Apply a stage-status delta to the learn-mode rail (keeps the left
    // rail in sync with what the chat is reporting).
    const applyStages = delta => {
        if (!delta) return;
        setLearnPlan(prev => prev.map(s => delta[s.name] ? { ...s, status: delta[s.name] } : s));
    };

    // Apply an orchestrator stage delta, keyed "workflow/stage".
    const applyOrchStages = delta => {
        if (!delta) return;
        setOrchPlan(prev => prev.map(g => ({
            ...g,
            stages: g.stages.map(s => delta[`${g.workflow}/${s.name}`] ? { ...s, status: delta[`${g.workflow}/${s.name}`] } : s),
        })));
    };

    // Add a written file (or several, via cfiles) to the config tree and
    // open the latest in the editor.
    const applyConfigFiles = msg => {
        const files = msg.cfiles || (msg.cfile ? [msg.cfile] : []);
        if (!files.length) return;
        setConfigPaths(prev => {
            const add = files.filter(f => !prev.includes(f));
            return add.length ? [...prev, ...add] : prev;
        });
        const last = files[files.length - 1];
        const name = last.split('/').pop();
        if (CONFIG_FILES[name]) { setOpenFile({ name, stage: name, lines: CONFIG_FILES[name] }); setRightTab('file'); }
    };

    // Build the nested folder/file tree from the flat list of written paths.
    const configTree = React.useMemo(() => {
        const folders = [], byFolder = {}, rootFiles = [];
        configPaths.forEach(p => {
            const i = p.indexOf('/');
            if (i === -1) { rootFiles.push({ type: 'file', name: p, path: p }); return; }
            const folder = p.slice(0, i), name = p.slice(i + 1);
            if (!byFolder[folder]) { byFolder[folder] = []; folders.push(folder); }
            byFolder[folder].push({ type: 'file', name, path: name });
        });
        return [...folders.map(f => ({ type: 'folder', name: f, children: byFolder[f] })), ...rootFiles];
    }, [configPaths]);

    // Stream a script out after a picked prompt: agent text types in
    // word-by-word; tool calls / diffs pop in instantly.
    const playScript = (script, promptText) => {
        const myId = ++playRef.current;
        const alive = () => playRef.current === myId;
        setLearnPlan(CHAT_PLAN.map(s => ({ ...s })));
        setOrchPlan(cloneOrch());
        setConfigPaths([]);
        setBrowserStep('blank');
        setSiteName(CHAT_PROJECT.site);
        setThread([{ role: 'user', text: promptText }]);
        setBusy(true);
        const rest = script.filter(m => m.role !== 'user');
        let i = 0;
        // Random 1–3s pause between steps so playback feels deliberate.
        const gap = () => 500 + Math.random() * 1000;
        const next = () => {
            if (!alive()) return;
            if (i >= rest.length) { setBusy(false); return; }
            const m = rest[i++];
            applyStages(m.stages);
            applyOrchStages(m.ostages);
            applyConfigFiles(m);
            if (m.screen) setBrowserStep(m.screen);
            if (m.role === 'agent') {
                const words = m.text.split(' ');
                setThread(prev => [...prev, { ...m, text: '' }]);
                setStreaming(true);
                let w = 0;
                const streamWord = () => {
                    if (!alive()) return;
                    w++;
                    setThread(prev => {
                        const copy = prev.slice();
                        const li = copy.length - 1;
                        copy[li] = { ...copy[li], text: words.slice(0, w).join(' ') };
                        return copy;
                    });
                    if (w < words.length) setTimeout(streamWord, 22 + Math.random() * 33);
                    else { setStreaming(false); setTimeout(next, gap()); }
                };
                setTimeout(streamWord, 130);
            } else {
                setThread(prev => [...prev, m]);
                setTimeout(next, gap());
            }
        };
        setTimeout(next, gap());
    };

    // Swap to a blank conversation when the mode changes; config also
    // pre-opens its first mission file in the editor.
    React.useEffect(() => {
        playRef.current++;
        setSiteName('');
        setBrowserStep('blank');
        setThread([]);
        setBusy(false);
        setStreaming(false);
        setLearnPlan(CHAT_PLAN.map(s => ({ ...s })));
        setOrchPlan(cloneOrch());
        setConfigPaths([]);
        if (mode === 'config') {
            setOpenData(null);
            setOpenFile(null);
        } else if (mode === 'run' || mode === 'orchestrate') {
            // Surface the live browser on the right when entering these modes.
            setBrowserOpen(true);
            setRightTab('browser');
        }
    }, [mode]);

    // Follow the liveBrowser tweak.
    React.useEffect(() => { setBrowserOpen(t.liveBrowser); }, [t.liveBrowser]);

    React.useEffect(() => {
        const el = scrollRef.current;
        if (el) el.scrollTop = el.scrollHeight;
    }, [thread, busy]);

    const send = () => {
        const text = input.trim();
        if (!text || busy) return;
        setInput('');
        // If starting fresh, play the mode's full script; otherwise append a short follow-up.
        if (thread.length === 0) { playScript(SCRIPTS[mode] || CHAT_THREAD, text); return; }
        setThread(prev => [...prev, { role: 'user', text }]);
        setBusy(true);
        const reply = mode === 'run'
            ? [
                { role: 'agent', text: 'Calling the relevant MCP tool to handle that.' },
                { role: 'tool', kind: 'workflow', icon: 'far fa-diagram-project', name: 'upload-resume', args: '{ resume: "daniel-okafor-resume.pdf" }', result: 'ok · résumé attached to SX-3192', status: 'ok' },
                { role: 'agent', text: 'Done — the résumé is attached to application **SX-3192**.' },
            ]
            : mode === 'config'
            ? [
                { role: 'agent', text: "I'll update the project sources to match." },
                { role: 'tool', kind: 'config', icon: 'far fa-file-pen', name: 'write', args: 'libraries/careers.lib.ts', result: '+6 lines', status: 'ok' },
                { role: 'agent', text: "Updated. Open the file on the left to review the change." },
            ]
            : [
                { role: 'agent', text: "On it — continuing the run. I'll match and execute the remaining stages." },
                { role: 'tool', icon: 'far fa-location-dot', name: 'match stage', args: 'fill-form', result: 'matched', status: 'ok' },
                { role: 'tool', icon: 'far fa-play', name: 'run stage', args: 'fill-form', result: 'passed · applicant filled', status: 'ok' },
            ];
        setTimeout(() => { setThread(prev => [...prev, ...reply]); setBusy(false); }, 1100);
    };

    if (entry) {
        return (
            <div className={t.dark ? 'theme-dark' : ''} style={{ height: '100vh', background: 'var(--iso-surface-50)', color: 'var(--p-text-color)', ...accentVars(t.accent) }}>
                <EntryChooser project={CHAT_PROJECT}
                    onUseExisting={() => { setEntry(false); setTweak('mode', 'orchestrate'); }}
                    onStartNew={() => { setEntry(false); setTweak('mode', 'config'); }} />
            </div>
        );
    }

    return (
        <div className={t.dark ? 'theme-dark' : ''} style={{ display: 'flex', flexFlow: 'column', height: '100vh', background: 'var(--iso-surface-0)', color: 'var(--p-text-color)', ...accentVars(t.accent) }}>
            <TopBar projectName={projectName} siteName={siteName} mode={mode} workflows={CHAT_WORKFLOWS}
                onProject={v => { setProjectName(v); setSiteName((CHAT_WORKFLOWS.find(w => w.name === v) || {}).site || siteName); setTweak('mode', 'orchestrate'); }}
                onSite={setSiteName} onChat={() => setTweak('mode', 'run')} onConfig={() => setTweak('mode', 'config')}
                onOrchestrate={() => setTweak('mode', 'orchestrate')}
                tabs={tabs} activeTab={activeTab} onSelectTab={setRightTab} onCloseTab={closeTab} />
            <div style={{ flex: 1, minHeight: 0, display: 'flex' }}>
                <div style={{ width: railW, flex: '0 0 auto' }}>
                    {isConfig
                        ? <ConfigTree project={CHAT_PROJECT} tree={configTree} activeFile={openFile ? openFile.stage : null} onOpen={openConfigFile} />
                        : <PlanRail project={CHAT_PROJECT} plan={learnPlan} workflows={CHAT_WORKFLOWS} orchestratorPlan={orchPlan} datasets={CHAT_DATASETS} dataset={dataset} onDataset={setDataset} scope={CHAT_SCOPE} mode={mode}
                            activeFile={openFile && rightTab === 'file' ? openFile.stage : null}
                            onOpenStage={openStageFile} onOpenData={openDataGroup}
                            onPickWorkflow={v => { setProjectName(v); setSiteName((CHAT_WORKFLOWS.find(w => w.name === v) || {}).site || siteName); setTweak('mode', 'orchestrate'); }} />}
                </div>
                <ResizeHandle onStart={() => { railStart.current = railW; }} onDrag={dx => setRailW(clamp(railStart.current + dx, 180, 360))} />
                <div style={{ flex: 1, minWidth: 320, display: 'flex', flexFlow: 'column', background: 'var(--iso-surface-0)' }}>
                    <div ref={scrollRef} style={{ flex: 1, minHeight: 0, overflow: 'auto', display: 'flex', flexFlow: 'column' }}>
                        {thread.length === 0
                            ? <EmptyState suggestion={CHAT_SUGGESTIONS[mode]} onPick={p => playScript(SCRIPTS[mode] || CHAT_THREAD, p)} />
                            : <div style={{ maxWidth: 760, width: '100%', margin: '0 auto', padding: '24px 24px 8px', display: 'flex', flexFlow: 'column', gap: 18 }}>
                                {thread.map((m, i) => <Message key={i} m={m} onOpenFile={isConfig ? openConfigFile : openStageFile} onGoMode={v => setTweak('mode', v)} />)}
                                <HGroup gap="1" style={{ marginLeft: 36, color: 'var(--p-text-muted-color)', fontSize: 13, visibility: (busy && !streaming) ? 'visible' : 'hidden' }}>
                                    <Icon name="fas fa-spinner-third" size={12} style={{ animation: 'a3-spin .7s linear infinite' }} />
                                    Working…
                                </HGroup>
                            </div>}
                    </div>
                    <Composer value={input} onChange={setInput} onSend={send} busy={busy} mode={mode} model={model} onModel={setModel} />
                </div>
                {dockOpen && (
                    <>
                        <ResizeHandle onStart={() => { browserStart.current = browserW; }} onDrag={dx => setBrowserW(clamp(browserStart.current - dx, 300, 620))} />
                        <div style={{ width: browserW, flex: '0 0 auto' }}>
                            <RightDock activeTab={activeTab} screen={browserStep} openFile={openFile} openData={openData} scopeData={CHAT_SCOPE_DATA} />
                        </div>
                    </>
                )}
            </div>

            <TweaksPanel>
                <TweakSection label="Mode" />
                <TweakSelect label="Mode" value={t.mode}
                    options={[{ value: 'config', label: 'Config' }, { value: 'orchestrate', label: 'Learning' }, { value: 'run', label: 'Chat to MCP' }]}
                    onChange={v => setTweak('mode', v)} />
                <div style={{ fontSize: 11, color: 'var(--p-text-muted-color)', padding: '2px 2px 6px', lineHeight: 1.5 }}>
                    {mode === 'run'
                        ? 'Chat to MCP — every workflow is exposed to the agent as an MCP tool; talk to them all at once.'
                        : mode === 'config'
                        ? 'Project config — edit missions, workflows, schemas, libraries & skills with the agent.'
                        : 'Learning — learn all workflows at once, chaining each one’s output into the next.'}
                </div>
                <TweakSection label="Appearance" />
                <TweakToggle label="Dark mode" value={t.dark}
                    onChange={v => setTweak('dark', v)} />
                <TweakColor label="Accent" value={t.accent}
                    options={['#6366f1', '#8b5cf6', '#3b82f6', '#10b981']}
                    onChange={v => setTweak('accent', v)} />
                <TweakToggle label="Live browser panel" value={t.liveBrowser}
                    onChange={v => setTweak('liveBrowser', v)} />
            </TweaksPanel>
        </div>
    );
}

window.A3Chat = Object.assign(window.A3Chat || {}, { ChatStudio });
