/* ===================================================
   JAESWIFT.XYZ — JAE-AI CLI Easter Egg
   Local pseudo-shell handled in the JAE-AI chat panel.
   Intercepts messages starting with '/'.
   =================================================== */
(function () {
    'use strict';

    const MAX_HISTORY = 50;
    const HIST_KEY = 'jaeCliHist';
    let cmdHistory = [];
    let devMode = false;
    let matrixActive = false;
    let sessionCmdCount = 0;
    let sessionPerCmd = {};
    let mode = null; // null | 'ssh' | 'adventure' | 'ttyper'
    let modeState = {};
    const ACH_KEY = 'jae-cli-achievements-v1';
    const STATS_KEY = 'jae-cli-stats-v1';
    const TTYPER_KEY = 'jae-cli-ttyper-best-v1';
    let achievements = new Set();
    let allTimeStats = { total: 0, perCmd: {} };
    try {
        const ar = localStorage.getItem(ACH_KEY);
        if (ar) { const a = JSON.parse(ar); if (Array.isArray(a)) achievements = new Set(a); }
        const sr = localStorage.getItem(STATS_KEY);
        if (sr) allTimeStats = Object.assign({ total: 0, perCmd: {} }, JSON.parse(sr));
    } catch (e) {}
    function saveAch() { try { localStorage.setItem(ACH_KEY, JSON.stringify([...achievements])); } catch(e){} }
    function saveStats() { try { localStorage.setItem(STATS_KEY, JSON.stringify(allTimeStats)); } catch(e){} }
    function unlockAchievement(cmd) {
        if (achievements.has(cmd)) return false;
        achievements.add(cmd);
        saveAch();
        try {
            const t = document.createElement('div');
            t.className = 'jae-cli-toast';
            t.textContent = '🏆 NEW: Discovered /' + cmd + ' command';
            Object.assign(t.style, {
                position: 'fixed', bottom: '24px', right: '24px', zIndex: '9999',
                background: 'var(--bg-panel, #0d1810)', border: '1px solid var(--warning, #c9a227)',
                color: 'var(--warning, #c9a227)', padding: '10px 16px',
                fontFamily: 'var(--font-mono, monospace)', fontSize: '12px', letterSpacing: '1px',
                boxShadow: '0 0 18px rgba(201,162,39,0.45)', transition: 'opacity 0.4s',
                borderRadius: '2px', pointerEvents: 'none',
            });
            document.body.appendChild(t);
            setTimeout(() => { t.style.opacity = '0'; }, 3400);
            setTimeout(() => { t.remove(); }, 4000);
        } catch (e) {}
        return true;
    }

    // Load history
    try {
        const raw = localStorage.getItem(HIST_KEY);
        if (raw) cmdHistory = JSON.parse(raw) || [];
    } catch (e) {}

    function saveHistory() {
        try {
            localStorage.setItem(HIST_KEY, JSON.stringify(cmdHistory.slice(-MAX_HISTORY)));
        } catch (e) {}
    }

    // ─── Virtual FS ──────────────────────────────────
    const VFS = {
        'about.md': [
            '# JAESWIFT // OPERATOR FILE',
            '',
            'Codename: JAE',
            'Location: Manchester, United Kingdom',
            'Operator since: 2008',
            '',
            'Full-stack engineer, cyber-curious, builds things.',
            'Specialisms: Node / Python / Linux / nginx / Solana',
            '',
            'This site is my HUD. Built by hand, deployed by git, watched by me.',
        ].join('\n'),

        'projects.json': JSON.stringify({
            'jaeswift.xyz':      'This site — military CRT HUD, Flask+vanilla',
            'matty.lol':         'Crypto/Solana toolkit (Node/Express)',
            'contraband-depot':  'FMHY mirror, 24 categories, 16k+ links',
            'recon-awesomelist': '28 sectors / 135k items, weekly sync',
            'unredacted-vault':  '114+ declassified PDFs (MOD/CIA/etc)',
            'radar':             'RSS aggregator (HN/Reddit/Lobsters)',
            'sitrep':            'Daily AI briefing generator',
            'telemetry':         'Live ops command centre'
        }, null, 2),

        'skills.md': [
            '# FIELD LOADOUT',
            '',
            '## LANGS',
            '  - Python, JavaScript/Node, TypeScript, Rust, Bash, SQL',
            '',
            '## STACKS',
            '  - Flask, Express, React, Solana/Anchor, Docker, systemd',
            '',
            '## INFRA',
            '  - nginx, Debian, PostgreSQL, Redis, cron, fail2ban',
            '',
            '## OPS',
            '  - VPS ops, CI/CD via Gitea, observability, hardening'
        ].join('\n'),

        'secrets.enc': [
            '',
            '   ░█████╗░░█████╗░░█████╗░███████╗░██████╗░██████╗',
            '   ██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝',
            '   ███████║██║░░╚═╝██║░░╚═╝█████╗░░╚█████╗░╚█████╗░',
            '   ██╔══██║██║░░██╗██║░░██╗██╔══╝░░░╚═══██╗░╚═══██╗',
            '   ██║░░██║╚█████╔╝╚█████╔╝███████╗██████╔╝██████╔╝',
            '   ╚═╝░░╚═╝░╚════╝░░╚════╝░╚══════╝╚═════╝░╚═════╝░',
            '',
            '              ▓▒░  ACCESS DENIED  ░▒▓',
            '      File encrypted with AES-4096-QUANTUM-ROT13.',
            '            Authorisation required. >> /sudo',
        ].join('\n'),

        'mascot.txt': [
            '            ___           ___',
            '         ,-"   `.       ,"   `-,',
            '        /   _    \\_____/    _   \\',
            '       |  (o)   `       `   (o)  |',
            '        \\   \\_._      _._/   /',
            '         `._    `----\'    _.\'',
            '            `~---------~\'',
            '            // JAE MASCOT //',
            '            Black dragon, on duty.'
        ].join('\n'),

        'whoami.txt': '$USER — check /whoami for your details.'
    };

    // ─── Helpers ─────────────────────────────────────
    function fmt(lines) { return Array.isArray(lines) ? lines.join('\n') : lines; }

    function pad(n, len) {
        const s = String(n);
        return s.length >= len ? s : ' '.repeat(len - s.length) + s;
    }

    async function fakeProgress(label, steps, delay) {
        const out = [];
        for (let i = 1; i <= steps; i++) {
            const pct = Math.floor((i / steps) * 100);
            const filled = '█'.repeat(Math.floor(pct / 5));
            const empty = '░'.repeat(20 - filled.length);
            out.push(`${label} [${filled}${empty}] ${pct}%`);
            // intentionally not awaited — output comes as a block
        }
        return out;
    }

    // ─── Commands ────────────────────────────────────
    const commands = {};

    commands.help = function () {
        const base = [
            'JAE-AI TERMINAL — available commands:',
            '',
            '— SYSTEM —',
            '  /help               show this help',
            '  /ls                 list virtual files',
            '  /cat <file>         print file contents',
            '  /whoami             show your visitor fingerprint',
            '  /uptime             show site uptime',
            '  /uname              kernel/system info',
            '  /date               current date/time',
            '  /ping <host>        fake ping sweep',
            '  /sudo <cmd>         attempt privilege escalation',
            '  /rm -rf /           don\'t do it',
            '  /clear              clear the chat',
            '  /exit               try to leave',
            '  /history            show last commands',
            '  /neofetch           system summary ASCII',
            '',
            '— FUN —',
            '  /hack <target>      hollywood hacking simulator',
            '  /matrix             toggle matrix rain',
            '  /cmatrix            enhanced matrix (sound + ramp)',
            '  /fortune [-o]       random quip (-o for offensive)',
            '  /cowsay <msg>       a cow says something',
            '  /figlet <text>      big ASCII banner (<=20 chars)',
            '',
            '— MODES —',
            '  /ssh <user>@<host>  enter fake SSH session',
            '  /adventure          cyberpunk text adventure',
            '  /ttyper             typing speed test',
            '',
            '— STATS —',
            '  /achievements       your unlocked commands',
            '  /leaderboard        session & all-time stats',
        ];
        if (devMode) {
            base.push('');
            base.push('  [DEVELOPER MODE ACTIVE]');
            base.push('  /api                list real API endpoints');
            base.push('  /curl <url>         actually fetch a URL');
            base.push('  /geo                show GEO INTEL from telemetry');
        }
        base.push('');
        base.push('Tip: Press Escape (or Ctrl+Shift+B) as a boss key to hide everything.');
        base.push('Anything not prefixed with / goes to JAE-AI.');
        return fmt(base);
    };

    commands.ls = function () {
        return fmt([
            'total 6',
            '-rw-r--r-- 1 jae jae  412 apr 20 01:24 about.md',
            '-rw-r--r-- 1 jae jae  528 apr 20 01:24 projects.json',
            '-rw-r--r-- 1 jae jae  394 apr 20 01:24 skills.md',
            '-r-------- 1 jae jae  999 apr 20 01:24 secrets.enc',
            '-rw-r--r-- 1 jae jae  210 apr 20 01:24 mascot.txt',
            '-rw-r--r-- 1 jae jae   42 apr 20 01:24 whoami.txt'
        ]);
    };

    commands.cat = function (args) {
        const name = args[0];
        if (!name) return 'cat: missing operand — try /cat about.md';
        if (VFS[name] != null) return VFS[name];
        return `cat: ${name}: No such file or directory`;
    };

    commands.whoami = async function () {
        try {
            const r = await fetch('/api/visitor/scan');
            if (!r.ok) throw new Error('scan unavailable');
            const d = await r.json();
            return fmt([
                `operator:    ${d.ip_masked}`,
                `geolocation: ${d.city ? d.city + ', ' : ''}${d.country} ${d.country_flag || ''}`,
                `network:     ${d.isp}`,
                `browser:     ${d.browser} ${d.browser_version || ''}`,
                `os:          ${d.os} ${d.os_version || ''}`,
                `device:      ${d.device}`,
                `threat:      ${d.threat_level} — ${d.threat_reason}`,
                '',
                'you are a visitor. welcome.'
            ]);
        } catch (e) {
            return 'scan service unavailable. try again in a moment.';
        }
    };

    commands.uptime = async function () {
        try {
            const r = await fetch('/api/telemetry/overview');
            const d = await r.json();
            const up = (d.system && d.system.uptime) || d.uptime || 'unknown';
            const load = (d.system && d.system.load_1) || '—';
            const users = 'many';
            const now = new Date().toLocaleTimeString('en-GB');
            return fmt([
                ` ${now} up ${up},  ${users} users,  load average: ${load}`,
                ` HQ: Manchester // jaeswift-api.service ● ONLINE`
            ]);
        } catch (e) {
            return `up since boot — tired but operational`;
        }
    };

    commands.uname = function (args) {
        if (args[0] === '-a') {
            return 'Linux jaeswift 6.1.0-jaeswift #1 SMP PREEMPT_DYNAMIC 2026-04-20 x86_64 GNU/Linux';
        }
        return 'Linux';
    };

    commands.date = function () {
        return new Date().toUTCString();
    };

    commands.ping = function (args) {
        const host = args[0] || 'jaeswift.xyz';
        const out = [`PING ${host} (10.0.0.1) 56(84) bytes of data.`];
        for (let i = 0; i < 4; i++) {
            const t = (10 + Math.random() * 40).toFixed(2);
            out.push(`64 bytes from ${host}: icmp_seq=${i+1} ttl=54 time=${t} ms`);
        }
        out.push('');
        out.push(`--- ${host} ping statistics ---`);
        out.push('4 packets transmitted, 4 received, 0% packet loss, time 3006ms');
        return fmt(out);
    };

    commands.sudo = function (args) {
        if (!args.length) return 'usage: /sudo <command>';
        return fmt([
            `[sudo] password for operator: ****`,
            ``,
            `sudo: ACCESS DENIED — nice try.`,
            `This incident will be reported... actually, no it won\'t.`
        ]);
    };

    commands.rm = function (args) {
        if (args.join(' ') === '-rf /' || args.join(' ').startsWith('-rf /')) {
            return fmt([
                '   __   __',
                '  /  \\_/  \\    💥 FORMATTING /dev/sda ...',
                '  \\_( • )_/    💥 WIPING /home ...',
                '  //(     )\\\\   💥 DELETING /etc/passwd ...',
                '',
                '  ERROR: The file system is screaming.',
                '  ███████████░░░░░░░░░ 58%',
                '',
                '  ... just kidding.',
                '  SYSTEM RESTORED FROM BACKUP. Nice try, operator.'
            ]);
        }
        return `rm: refuse to do whatever that is`;
    };

    commands.hack = function (args) {
        const target = args.join(' ') || 'the-gibson';
        return fmt([
            `>>> TARGETING ${target}...`,
            `>>> BYPASSING FIREWALL... [████████████░░░░] 72%`,
            `>>> CRACKING PASSWORD... [████████████████] 100%`,
            `>>> INJECTING PAYLOAD...  [████████████████] 100%`,
            `>>> COVERING TRACKS...    [████████████████] 100%`,
            ``,
            `>>> CONNECTION ESTABLISHED.`,
            `>>> ACCESS GRANTED TO ${target.toUpperCase()}.`,
            ``,
            `... just kidding lol. nothing was hacked.`,
            `If you came here looking for real offsec — go read /cat skills.md and touch grass.`
        ]);
    };

    commands.matrix = function () {
        matrixActive = !matrixActive;
        toggleMatrix(matrixActive);
        return matrixActive
            ? 'Matrix rain enabled. Follow the white rabbit.'
            : 'Matrix rain disabled. Back to the desert of the real.';
    };

    commands.clear = function () {
        const cm = document.getElementById('chatMessages');
        if (cm) cm.innerHTML = '';
        return null; // nothing to print
    };

    commands.exit = function () {
        return fmt([
            'logout',
            '',
            'NICE TRY. You don\'t leave that easily.',
            'JAE-AI resumes normal operation.'
        ]);
    };

    const FORTUNES = [
        'SITREP: quiet on the wire. unusual.',
        'SITREP: nothing is fine. everything is broken. proceed.',
        'SITREP: coffee low. deploy anyway.',
        'SITREP: the logs know. the logs always know.',
        'SITREP: git status is a lifestyle.',
        'SITREP: ship it friday. regret it monday.',
        'SITREP: 99 bugs on the wall, patch one down, 117 on the wall.',
        'SITREP: if it works on the VPS, the VPS is now production.',
        'SITREP: the only safe deploy is no deploy.',
        'SITREP: trust nothing. grep everything.',
    ];

    commands.fortune = function () {
        return FORTUNES[Math.floor(Math.random() * FORTUNES.length)];
    };

    commands.history = function () {
        if (!cmdHistory.length) return '(no history yet)';
        const recent = cmdHistory.slice(-10);
        return recent.map((c, i) => `  ${pad(cmdHistory.length - recent.length + i + 1, 4)}  ${c}`).join('\n');
    };

    commands.neofetch = function () {
        const cc = (navigator.language || 'en').toUpperCase();
        const ua = navigator.userAgent || '';
        let browser = 'Unknown';
        if (/firefox/i.test(ua)) browser = 'Firefox';
        else if (/edg/i.test(ua)) browser = 'Edge';
        else if (/chrome/i.test(ua)) browser = 'Chrome';
        else if (/safari/i.test(ua)) browser = 'Safari';
        return fmt([
            '       ▄███████▄       operator@jaeswift.xyz',
            '      ██░░░░░░░██      ----------------------',
            '      ██░░░░░░░██      OS:      JAESWIFT/6.1.0 x86_64',
            '      ██░██░██░██      Host:    Manchester HQ',
            '      ██░░░░░░░██      Kernel:  CRT-tactical-v2',
            '       ██░░░░░██       Uptime:  24/7/365',
            '        ███████        Shell:   /bin/jaesh',
            '        █     █        Resolution: ' + screen.width + 'x' + screen.height,
            '       ██     ██       Terminal: JAE-AI',
            '      ██       ██      Browser: ' + browser,
            '                       Locale:  ' + cc,
            '                       CPU:     18-core @ spicy GHz',
            '                       Memory:  96GB tactical',
            '                       Theme:   mil-green/CRT/cyberpunk',
        ]);
    };

    // ─── Dev-mode commands ──────────────────────────
    commands.api = async function () {
        if (!devMode) return 'command not found. try /help';
        const endpoints = [
            '/api/visitor/scan',
            '/api/visitor/recent-arcs',
            '/api/leaderboards',
            '/api/telemetry/overview',
            '/api/telemetry/history',
            '/api/telemetry/geo',
            '/api/telemetry/visitors',
            '/api/telemetry/nginx-tail',
            '/api/telemetry/alerts',
            '/api/stats',
            '/api/navigation',
            '/api/changelog',
            '/api/chat (POST)',
            '/api/contraband',
            '/api/sitrep/latest',
            '/api/radar'
        ];
        return 'JAESWIFT API endpoints (dev-mode):\n\n  ' + endpoints.join('\n  ');
    };

    commands.curl = async function (args) {
        if (!devMode) return 'command not found. try /help';
        const url = args[0];
        if (!url) return 'usage: /curl <url>';
        try {
            const r = await fetch(url);
            const text = (await r.text()).slice(0, 2000);
            return `HTTP ${r.status} ${r.statusText}\ncontent-type: ${r.headers.get('content-type') || 'n/a'}\n\n${text}${text.length >= 2000 ? '\n...[truncated]' : ''}`;
        } catch (e) {
            return `curl: (6) ${e.message}`;
        }
    };

    commands.geo = async function () {
        if (!devMode) return 'command not found. try /help';
        try {
            const r = await fetch('/api/telemetry/geo');
            const d = await r.json();
            if (!Array.isArray(d) || !d.length) return 'GEO INTEL: no data.';
            return 'GEO INTEL (24h):\n\n' + d.slice(0, 15).map((row, i) =>
                `  ${pad(i+1, 2)}. ${(row.country_code || 'XX').padEnd(3)} ${(row.country_name || 'Unknown').padEnd(22)} ${pad(row.count || 0, 5)}`
            ).join('\n');
        } catch (e) {
            return 'telemetry unavailable.';
        }
    };

    // ─── /cowsay ─────────────────────────────────────
    commands.cowsay = function (args) {
        const msg = args.join(' ') || 'moo.';
        const text = msg.slice(0, 200);
        const top = ' ' + '_'.repeat(text.length + 2);
        const bot = ' ' + '-'.repeat(text.length + 2);
        return fmt([
            top,
            '< ' + text + ' >',
            bot,
            '        \\   ^__^',
            '         \\  (oo)\\_______',
            '            (__)\\       )\\/\\',
            '                ||----w |',
            '                ||     ||'
        ]);
    };

    // ─── /fortune (tame + offensive) ─────────────────
    const FORTUNES_TAME = [
        'SITREP: the compiler whispers. listen.',
        'SITREP: your branch is ahead by one regret.',
        'SITREP: staging is production if staging is where the users are.',
        'SITREP: tests pass locally is not a deployment strategy.',
        'SITREP: cache invalidation is a personality trait.',
        'SITREP: the database is fine. the database is never fine.',
        'SITREP: write it down. future-you is stupider than you think.',
        'SITREP: uptime is measured in coffee.',
        'SITREP: legacy code is just working code you hate.',
        'SITREP: every bug is someone else\'s feature.',
        'SITREP: the stack trace knows. trust the stack trace.',
        'SITREP: container orchestration is a synonym for chaos.',
        'SITREP: there are two kinds of backup: tested and imaginary.',
        'SITREP: TODO comments age like milk.',
        'SITREP: ship small, ship often, ship sober.',
        'SITREP: rm is a lifestyle. ls is religion.',
        'SITREP: root access is a personality disorder.',
        'SITREP: the terminal is the only honest relationship.',
        'SITREP: `it compiles` is not a vibe.',
        'SITREP: operator status: caffeinated. proceed.'
    ];
    const FORTUNES_OFFENSIVE = [
        'SITREP: your code smells like a fucking landfill. grep deeper.',
        'SITREP: whoever wrote this legacy shit owes the team a pint and a resignation letter.',
        'SITREP: you can\'t centralise a garbage fire. you can only photograph it for post-mortem.',
        'SITREP: touch grass, operator. the leaves are greener than your terminal.',
        'SITREP: the sysadmin is tired. the sysadmin is always fucking tired.',
        'SITREP: if it works, don\'t touch it. if it doesn\'t, blame the intern.',
        'SITREP: your git history is a crime scene. get a lawyer.',
        'SITREP: nginx config is 80% vibes and 20% stack overflow.',
        'SITREP: the only thing more fragile than your ego is this regex.',
        'SITREP: deploy on friday. embrace the chaos. live deliciously.',
        'SITREP: your tests are flaky because YOU are flaky.',
        'SITREP: merge conflicts are the universe\'s way of saying grow up.',
        'SITREP: chmod 777 is a cry for help.',
        'SITREP: whoever invented JavaScript owes humanity a bloody apology.',
        'SITREP: your Docker image is 4GB because you don\'t know what you\'re doing. fix it.',
        'SITREP: if you ssh into prod to "just check something", you are the incident.',
        'SITREP: that variable name is an act of violence against future maintainers.',
        'SITREP: you didn\'t break the build. the build broke you.',
        'SITREP: 3am deploys are for masochists and idiots. which one are you?',
        'SITREP: the documentation is a lie. it has always been a lie.',
        'SITREP: `works on my machine` is a confession, not a defence.',
        'SITREP: your monorepo is a cry for therapy.',
        'SITREP: stop copy-pasting from stack overflow you absolute muppet.',
        'SITREP: curl | bash is a religious experience and a mistake.',
        'SITREP: kubernetes is just containers with extra steps and extra crying.',
        'SITREP: the only good password is the one you forgot.',
        'SITREP: your side project is a liability, not a portfolio.',
        'SITREP: linters are for people who can\'t spell. spell better.',
        'SITREP: shit deploys fast. good shit deploys slower. great shit doesn\'t deploy on fridays.',
        'SITREP: if the server is smoking, that\'s your problem now.'
    ];
    commands.fortune = function (args) {
        const offensive = args && (args[0] === '-o' || args[0] === '--offensive');
        const pool = offensive ? FORTUNES_OFFENSIVE : FORTUNES_TAME;
        return pool[Math.floor(Math.random() * pool.length)];
    };

    // ─── /figlet — block-font ASCII banner ─���─────────
    const FIGLET_FONT = {
        'A': ['  ██  ','█  █ ','████ ','█  █ ','█  █ '],
        'B': ['███  ','█  █ ','███  ','█  █ ','███  '],
        'C': [' ███ ','█    ','█    ','█    ',' ███ '],
        'D': ['███  ','█  █ ','█  █ ','█  █ ','███  '],
        'E': ['████ ','█    ','███  ','█    ','████ '],
        'F': ['████ ','█    ','███  ','█    ','█    '],
        'G': [' ███ ','█    ','█ ██ ','█  █ ',' ███ '],
        'H': ['█  █ ','█  █ ','████ ','█  █ ','█  █ '],
        'I': ['███  ',' █   ',' █   ',' █   ','███  '],
        'J': ['  ██ ','   █ ','   █ ','█  █ ',' ██  '],
        'K': ['█  █ ','█ █  ','██   ','█ █  ','█  █ '],
        'L': ['█    ','█    ','█    ','█    ','████ '],
        'M': ['█   █','██ ██','█ █ █','█   █','█   █'],
        'N': ['█  █ ','██ █ ','█ ██ ','█  █ ','█  █ '],
        'O': [' ██  ','█  █ ','█  █ ','█  █ ',' ██  '],
        'P': ['███  ','█  █ ','███  ','█    ','█    '],
        'Q': [' ██  ','█  █ ','█  █ ','█ █  ',' █ █ '],
        'R': ['███  ','█  █ ','███  ','█ █  ','█  █ '],
        'S': [' ███ ','█    ',' ██  ','   █ ','███  '],
        'T': ['█████','  █  ','  █  ','  █  ','  █  '],
        'U': ['█  █ ','█  █ ','█  █ ','█  █ ',' ██  '],
        'V': ['█  █ ','█  █ ','█  █ ',' ██  ','  █  '],
        'W': ['█   █','█   █','█ █ █','██ ██','█   █'],
        'X': ['█  █ ',' ██  ','  █  ',' ██  ','█  █ '],
        'Y': ['█  █ ',' ██  ','  █  ','  █  ','  █  '],
        'Z': ['████ ','   █ ','  █  ',' █   ','████ '],
        '0': [' ██  ','█  █ ','█  █ ','█  █ ',' ██  '],
        '1': [' █   ','██   ',' █   ',' █   ','███  '],
        '2': [' ██  ','█  █ ','  █  ',' █   ','████ '],
        '3': ['███  ','   █ ',' ██  ','   █ ','███  '],
        '4': ['█  █ ','█  █ ','████ ','   █ ','   █ '],
        '5': ['████ ','█    ','███  ','   █ ','███  '],
        '6': [' ██  ','█    ','███  ','█  █ ',' ██  '],
        '7': ['████ ','   █ ','  █  ',' █   ','█    '],
        '8': [' ██  ','█  █ ',' ██  ','█  █ ',' ██  '],
        '9': [' ██  ','█  █ ',' ███ ','   █ ',' ██  '],
        ' ': ['     ','     ','     ','     ','     '],
        '!': ['  █  ','  █  ','  █  ','     ','  █  '],
        '?': [' ██  ','█  █ ','  █  ','     ','  █  '],
        '.': ['     ','     ','     ','     ','  █  '],
        '-': ['     ','     ','████ ','     ','     '],
        '_': ['     ','     ','     ','     ','████ '],
        '/': ['   █ ','  █  ',' █   ','█    ','     '],
        ':': ['     ','  █  ','     ','  █  ','     ']
    };
    commands.figlet = function (args) {
        const text = (args.join(' ') || 'JAE').toUpperCase().slice(0, 20);
        const rows = ['', '', '', '', ''];
        for (const ch of text) {
            const glyph = FIGLET_FONT[ch] || FIGLET_FONT['?'];
            for (let i = 0; i < 5; i++) rows[i] += glyph[i] + ' ';
        }
        return rows.join('\n');
    };

    // ─── /ssh fake session ───────────────────────────
    const SSH_FS = {
        '/': ['home', 'etc', 'var', 'opt', 'tmp'],
        '/home': ['operator', 'guest'],
        '/etc': ['hostname', 'motd', 'shadow'],
        '/var': ['log'],
        '/var/log': ['auth.log', 'syslog'],
    };
    const SSH_FILES = {
        '/etc/hostname': 'jaeswift-prod-01',
        '/etc/motd': '\n  WELCOME TO JAESWIFT PROD — authorised operators only.\n  All activity logged. Press Ctrl+C to get off my lawn.\n',
        '/etc/shadow': 'nice try. permission denied.',
        '/home/operator/notes.txt': 'remember: the password is NOT hunter2.',
        '/var/log/auth.log': '[' + new Date().toISOString() + '] sshd: Accepted publickey for operator from 127.0.0.1',
    };
    commands.ssh = function (args) {
        const target = args[0] || '';
        const m = target.match(/^([a-zA-Z0-9_-]+)@([a-zA-Z0-9.\-_]+)$/);
        if (!m) return 'usage: /ssh <user>@<host>    e.g. /ssh operator@jaeswift.xyz';
        const user = m[1], host = m[2];
        mode = 'ssh';
        modeState = { user, host, cwd: '/home/' + user };
        return fmt([
            `Connecting to ${host} on port 22...`,
            `The authenticity of host '${host}' can\'t be established.`,
            `ED25519 key fingerprint is SHA256:J4Esw1FtX${Math.random().toString(36).slice(2,14)}.`,
            `Warning: Permanently added '${host}' (ED25519) to the list of known hosts.`,
            `${user}@${host}'s password: ****`,
            `Authenticating...`,
            `Last login: ${new Date().toUTCString()} from 10.0.0.1`,
            ``,
            `Welcome to ${host} — type 'help' for commands, '/exit-ssh' to disconnect.`
        ]);
    };
    function sshHandle(line) {
        const raw = line.trim();
        if (!raw) return '';
        if (raw === '/exit-ssh' || raw === 'exit' || raw === 'logout') {
            const h = modeState.host;
            mode = null; modeState = {};
            return `Connection to ${h} closed.`;
        }
        const parts = raw.split(/\s+/);
        const cmd = parts[0].toLowerCase();
        const arg = parts.slice(1).join(' ');
        switch (cmd) {
            case 'help':
                return fmt(['available: ls, cat <f>, pwd, cd <d>, whoami, hostname, uname, date, exit']);
            case 'whoami': return modeState.user;
            case 'hostname': return modeState.host;
            case 'pwd': return modeState.cwd;
            case 'uname': return 'Linux';
            case 'date': return new Date().toString();
            case 'ls': {
                const dir = SSH_FS[modeState.cwd];
                if (dir) return dir.join('  ');
                if (modeState.cwd === '/home/' + modeState.user) return 'notes.txt  .bashrc  .ssh';
                return '';
            }
            case 'cd': {
                if (!arg || arg === '~') { modeState.cwd = '/home/' + modeState.user; return ''; }
                if (arg.startsWith('/')) modeState.cwd = arg;
                else modeState.cwd = (modeState.cwd + '/' + arg).replace(/\/+/g, '/');
                return '';
            }
            case 'cat': {
                if (!arg) return 'cat: missing operand';
                const full = arg.startsWith('/') ? arg : (modeState.cwd + '/' + arg).replace(/\/+/g, '/');
                if (SSH_FILES[full] != null) return SSH_FILES[full];
                return `cat: ${arg}: No such file or directory`;
            }
            default:
                return `${cmd}: command not found`;
        }
    }

    // ─── /adventure — cyberpunk text adventure ───────
    const ADV_ROOMS = {
        'alley':   { name: 'DARK ALLEY', desc: 'Rain slicks the neon puddles. A fire-escape ladder climbs north to a rooftop. East is a buzzing arcade door.', exits: { n: 'rooftop_low', e: 'arcade' }, items: ['rusty knife'] },
        'arcade':  { name: 'NEON ARCADE', desc: 'Machines scream colour. A bouncer eyes you. A back door leads south to a hacker den. West back to the alley.', exits: { w: 'alley', s: 'hacker_den' }, items: ['token'] },
        'hacker_den': { name: 'HACKER DEN', desc: 'Ten monitors, one cat. A hacker types without looking. They might talk. North back to arcade.', exits: { n: 'arcade' }, items: [], npc: 'hacker' },
        'rooftop_low': { name: 'LOW ROOFTOP', desc: 'Gravel, pigeons, a distant drone. A ladder climbs east to the high neon rooftop.', exits: { s: 'alley', e: 'rooftop_high' }, items: [] },
        'rooftop_high': { name: 'NEON ROOFTOP', desc: 'The city spreads below. A corporate drone blocks the datalink pylon. BOSS FIGHT.', exits: { w: 'rooftop_low' }, items: [], boss: 'drone' }
    };
    commands.adventure = function () {
        mode = 'adventure';
        modeState = { room: 'alley', inv: [], hp: 100, bossHp: 60, talked: false, victory: false };
        return fmt([
            '== NEON/NULL — a very short cyberpunk — ==',
            '',
            'You are an operator looking to upload the encryption key to the city.',
            'Commands: look, n/s/e/w (or north/south/east/west), take <item>,',
            '          use <item>, inventory (i), attack, talk, /exit-adventure',
            '',
            advDescribe()
        ]);
    };
    function advDescribe() {
        const r = ADV_ROOMS[modeState.room];
        const out = [`[${r.name}]`, r.desc];
        if (r.items && r.items.length) out.push('You see: ' + r.items.join(', '));
        if (r.boss && modeState.bossHp > 0) out.push('A CORPORATE DRONE is here. HP: ' + modeState.bossHp);
        const exits = Object.keys(r.exits).map(k => ({n:'north',s:'south',e:'east',w:'west'})[k]).join(', ');
        out.push('Exits: ' + exits);
        return out.join('\n');
    }
    function adventureHandle(line) {
        const raw = line.trim().toLowerCase();
        if (!raw) return '';
        if (raw === '/exit-adventure' || raw === 'exit-adventure' || raw === 'quit') {
            mode = null; modeState = {};
            return 'You jack out. Back to JAE-AI.';
        }
        const dirMap = { n: 'n', north: 'n', s: 's', south: 's', e: 'e', east: 'e', w: 'w', west: 'w' };
        if (dirMap[raw]) {
            const r = ADV_ROOMS[modeState.room];
            const d = dirMap[raw];
            if (r.boss && modeState.bossHp > 0) return 'The drone blocks your path. ATTACK it first.';
            if (!r.exits[d]) return 'You can\'t go that way.';
            modeState.room = r.exits[d];
            return advDescribe();
        }
        if (raw === 'look' || raw === 'l') return advDescribe();
        if (raw === 'inventory' || raw === 'i' || raw === 'inv') {
            return modeState.inv.length ? 'You carry: ' + modeState.inv.join(', ') : 'Your pockets are empty.';
        }
        if (raw.startsWith('take ')) {
            const item = raw.slice(5).trim();
            const r = ADV_ROOMS[modeState.room];
            const idx = r.items.indexOf(item);
            if (idx === -1) return `There is no ${item} here.`;
            r.items.splice(idx, 1);
            modeState.inv.push(item);
            return `You take the ${item}.`;
        }
        if (raw.startsWith('use ')) {
            const item = raw.slice(4).trim();
            if (!modeState.inv.includes(item)) return `You don't have a ${item}.`;
            if (item === 'token' && modeState.room === 'arcade') {
                modeState.inv.push('encryption key');
                return 'The arcade machine clunks. A secret panel opens — you palm an ENCRYPTION KEY.';
            }
            if (item === 'encryption key' && modeState.room === 'rooftop_high' && modeState.bossHp <= 0) {
                modeState.victory = true;
                mode = null; modeState = {};
                return fmt([
                    'You jack the ENCRYPTION KEY into the pylon.',
                    'The city\'s surveillance grid goes dark for 11 seconds.',
                    'That\'s all you needed.',
                    '',
                    '>> YOU WIN. Mission complete. Exiting adventure. <<'
                ]);
            }
            return 'Nothing happens.';
        }
        if (raw === 'talk') {
            const r = ADV_ROOMS[modeState.room];
            if (r.npc === 'hacker' && !modeState.talked) {
                modeState.talked = true;
                if (!modeState.inv.includes('token')) return 'The hacker: "Arcade machine. Back one. Use the token. Trust me."';
                if (!modeState.inv.includes('encryption key')) return 'The hacker: "Use what you have. The arcade remembers."';
                return 'The hacker: "Rooftop. Pylon. Key. Go."';
            }
            return 'No one to talk to.';
        }
        if (raw === 'attack') {
            const r = ADV_ROOMS[modeState.room];
            if (!r.boss || modeState.bossHp <= 0) return 'Nothing to attack.';
            const weapon = modeState.inv.includes('rusty knife') ? 'knife' : 'fist';
            const dmg = weapon === 'knife' ? (8 + Math.floor(Math.random()*8)) : (3 + Math.floor(Math.random()*4));
            modeState.bossHp -= dmg;
            modeState.hp -= (3 + Math.floor(Math.random()*6));
            let out = `You attack with your ${weapon} for ${dmg} damage. Drone HP: ${Math.max(0,modeState.bossHp)}.`;
            if (modeState.hp <= 0) {
                mode = null; modeState = {};
                return out + '\nThe drone flatlines you. GAME OVER.';
            }
            if (modeState.bossHp <= 0) out += '\nThe drone crashes to the gravel. The pylon is exposed.';
            out += ` Your HP: ${modeState.hp}.`;
            return out;
        }
        return `Unknown command: '${raw}'. Try look, directions, take, use, attack, talk.`;
    }

    // ─── /ttyper — typing speed test ─────────────────
    const TTYPER_QUOTES = [
        'the quick brown fox jumps over the lazy dog while the sysadmin sips cold coffee and watches nginx logs scroll endlessly',
        'code is read many more times than it is written so leave comments that future you will not curse at',
        'in the terminal no one can hear you scream but the bash history remembers every single thing you have ever typed',
        'deploy on friday and spend the weekend rolling back changes like a very unfortunate software archaeologist',
        'the best debugger is still a print statement followed by staring at the output for twenty minutes in silence'
    ];
    commands.ttyper = function () {
        const q = TTYPER_QUOTES[Math.floor(Math.random() * TTYPER_QUOTES.length)];
        mode = 'ttyper';
        modeState = { quote: q, start: 0 };
        return fmt([
            '=== TTYPER — typing speed test ===',
            'Type the quote below exactly as shown, then press Enter.',
            'Type "/exit-ttyper" to cancel.',
            '',
            '> ' + q,
            '',
            '(timer starts on your first submission)'
        ]);
    };
    function ttyperHandle(line) {
        const raw = line.trim();
        if (raw === '/exit-ttyper' || raw === 'exit-ttyper' || raw === 'quit') {
            mode = null; modeState = {};
            return 'TTYPER cancelled.';
        }
        if (!modeState.start) modeState.start = Date.now();
        const elapsed = (Date.now() - modeState.start) / 1000;
        const target = modeState.quote;
        const a = raw, b = target;
        const maxLen = Math.max(a.length, b.length);
        let correct = 0;
        for (let i = 0; i < Math.min(a.length, b.length); i++) if (a[i] === b[i]) correct++;
        const accuracy = maxLen === 0 ? 100 : Math.round((correct / maxLen) * 100);
        const words = target.split(/\s+/).length;
        const wpm = elapsed > 0 ? Math.round((words / elapsed) * 60) : 0;
        let best = null;
        try {
            const raw2 = localStorage.getItem(TTYPER_KEY);
            if (raw2) best = JSON.parse(raw2);
        } catch (e) {}
        let bestNote = '';
        if (!best || (wpm > (best.wpm || 0) && accuracy >= 90)) {
            const rec = { wpm, accuracy, ts: Date.now() };
            try { localStorage.setItem(TTYPER_KEY, JSON.stringify(rec)); } catch(e){}
            bestNote = '\n*** NEW PERSONAL BEST ***';
            best = rec;
        }
        mode = null; modeState = {};
        return fmt([
            '=== TTYPER RESULT ===',
            `time:     ${elapsed.toFixed(2)}s`,
            `WPM:      ${wpm}`,
            `accuracy: ${accuracy}%`,
            best ? `all-time best: ${best.wpm} WPM @ ${best.accuracy}%` : '',
            bestNote
        ].filter(Boolean));
    }

    // ─── /cmatrix — enhanced matrix rain ─────────────
    let cmatrixCanvas = null, cmatrixRAF = null, cmatrixAudio = null, cmatrixStart = 0;
    function toggleCMatrix(on) {
        if (on) {
            cmatrixCanvas = document.createElement('canvas');
            cmatrixCanvas.id = 'cmatrixRain';
            Object.assign(cmatrixCanvas.style, {
                position: 'fixed', inset: '0', width: '100%', height: '100%',
                pointerEvents: 'none', zIndex: '8', opacity: '0.32',
            });
            document.body.appendChild(cmatrixCanvas);
            const ctx = cmatrixCanvas.getContext('2d');
            function resize() { cmatrixCanvas.width = innerWidth; cmatrixCanvas.height = innerHeight; }
            resize();
            window.addEventListener('resize', resize);
            const chars = 'アイウエオカキクケコサシスセソタチツテトナニヌネノ01JAESWIFT░▒▓█'.split('');
            const size = 14;
            const cols = Math.floor(cmatrixCanvas.width / size);
            const drops = new Array(cols).fill(0).map(() => Math.random() * 40);
            cmatrixStart = Date.now();
            try {
                const AC = window.AudioContext || window.webkitAudioContext;
                if (AC) cmatrixAudio = new AC();
            } catch (e) {}
            let tick = 0;
            function draw() {
                const t = (Date.now() - cmatrixStart) / 1000;
                const phase = (t % 30) / 30;
                const speed = 1 + phase * 2.5;
                ctx.fillStyle = 'rgba(0, 0, 0, 0.08)';
                ctx.fillRect(0, 0, cmatrixCanvas.width, cmatrixCanvas.height);
                ctx.font = size + 'px JetBrains Mono, monospace';
                for (let i = 0; i < drops.length; i++) {
                    const ch = chars[Math.floor(Math.random() * chars.length)];
                    ctx.fillStyle = (i % 2 === 0) ? '#00ff66' : '#33ddff';
                    ctx.fillText(ch, i * size, drops[i] * size);
                    if (drops[i] * size > cmatrixCanvas.height && Math.random() > 0.97) drops[i] = 0;
                    drops[i] += speed;
                }
                tick++;
                if (cmatrixAudio && tick % 4 === 0) {
                    try {
                        const osc = cmatrixAudio.createOscillator();
                        const g = cmatrixAudio.createGain();
                        osc.type = 'square';
                        osc.frequency.value = 1600 + Math.random() * 800;
                        g.gain.value = 0.0035;
                        osc.connect(g); g.connect(cmatrixAudio.destination);
                        osc.start();
                        osc.stop(cmatrixAudio.currentTime + 0.02);
                    } catch (e) {}
                }
                cmatrixRAF = requestAnimationFrame(draw);
            }
            draw();
        } else {
            if (cmatrixRAF) cancelAnimationFrame(cmatrixRAF);
            if (cmatrixCanvas) cmatrixCanvas.remove();
            if (cmatrixAudio) { try { cmatrixAudio.close(); } catch(e){} }
            cmatrixCanvas = null; cmatrixAudio = null;
        }
    }
    let cmatrixActive = false;
    commands.cmatrix = function () {
        cmatrixActive = !cmatrixActive;
        toggleCMatrix(cmatrixActive);
        return cmatrixActive
            ? 'CMATRIX engaged. Dual-colour rain with audio. Toggle off with /cmatrix.'
            : 'CMATRIX disengaged.';
    };

    // ─── /achievements ───────────────────────────────
    const TOTAL_COMMANDS = 24;
    commands.achievements = function () {
        const unlocked = [...achievements].sort();
        const n = unlocked.length;
        const pct = Math.round((n / TOTAL_COMMANDS) * 100);
        const filled = Math.min(10, Math.round((n / TOTAL_COMMANDS) * 10));
        const bar = '[' + '█'.repeat(filled) + '░'.repeat(10 - filled) + ']';
        const out = [
            '=== ACHIEVEMENTS ===',
            '',
            `Progress: ${bar} ${n}/${TOTAL_COMMANDS} (${pct}%)`,
            ''
        ];
        if (n === 0) out.push('No commands discovered yet. Try /help to start.');
        else {
            out.push('Unlocked:');
            unlocked.forEach(c => out.push('  ✓ /' + c));
        }
        out.push('');
        out.push('Keep exploring, operator.');
        return fmt(out);
    };

    // ─── /leaderboard ────────────────────────────────
    function rankFor(total) {
        if (total >= 2000) return 'general';
        if (total >= 1000) return 'colonel';
        if (total >= 500)  return 'major';
        if (total >= 200)  return 'captain';
        if (total >= 100)  return 'lieutenant';
        if (total >= 50)   return 'sergeant';
        if (total >= 20)   return 'corporal';
        return 'recruit';
    }
    commands.leaderboard = function () {
        const entries = Object.entries(allTimeStats.perCmd || {}).sort((a,b) => b[1] - a[1]);
        const mostUsed = entries.length ? entries[0][0] : '—';
        const sessionEntries = Object.entries(sessionPerCmd).sort((a,b) => b[1] - a[1]);
        return fmt([
            '=== LEADERBOARD (this browser) ===',
            '',
            `Session commands:      ${sessionCmdCount}`,
            `All-time commands:     ${allTimeStats.total || 0}`,
            `Commands unlocked:     ${achievements.size}/${TOTAL_COMMANDS}`,
            `Most-used (all-time):  /${mostUsed}`,
            `Rank:                  ${rankFor(allTimeStats.total || 0).toUpperCase()}`,
            '',
            'Session top 5:',
            ...sessionEntries.slice(0, 5).map(([c, n]) => `  /${c}  ×${n}`),
            '',
            'All-time top 5:',
            ...entries.slice(0, 5).map(([c, n]) => `  /${c}  ×${n}`)
        ]);
    };

    // ─── Matrix rain overlay ─────────────────────────
    let matrixCanvas = null;
    let matrixRAF = null;
    function toggleMatrix(on) {
        if (on) {
            matrixCanvas = document.createElement('canvas');
            matrixCanvas.id = 'matrixRain';
            Object.assign(matrixCanvas.style, {
                position: 'fixed', inset: '0', width: '100%', height: '100%',
                pointerEvents: 'none', zIndex: '7', opacity: '0.18',
            });
            document.body.appendChild(matrixCanvas);
            const ctx = matrixCanvas.getContext('2d');
            function resize() {
                matrixCanvas.width = window.innerWidth;
                matrixCanvas.height = window.innerHeight;
            }
            resize();
            window.addEventListener('resize', resize);
            const chars = 'アイウエオカキクケコサシスセソタチツテトナニヌネノ01JAESWIFT'.split('');
            const size = 16;
            const cols = Math.floor(matrixCanvas.width / size);
            const drops = new Array(cols).fill(1);
            function draw() {
                ctx.fillStyle = 'rgba(0, 0, 0, 0.06)';
                ctx.fillRect(0, 0, matrixCanvas.width, matrixCanvas.height);
                ctx.fillStyle = '#00cc33';
                ctx.font = size + 'px JetBrains Mono, monospace';
                for (let i = 0; i < drops.length; i++) {
                    const ch = chars[Math.floor(Math.random() * chars.length)];
                    ctx.fillText(ch, i * size, drops[i] * size);
                    if (drops[i] * size > matrixCanvas.height && Math.random() > 0.975) drops[i] = 0;
                    drops[i]++;
                }
                matrixRAF = requestAnimationFrame(draw);
            }
            draw();
        } else {
            if (matrixRAF) cancelAnimationFrame(matrixRAF);
            if (matrixCanvas) matrixCanvas.remove();
            matrixCanvas = null;
        }
    }

    // ─── Konami Code → Developer Mode ────────────────
    const KONAMI = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
    let kBuf = [];
    document.addEventListener('keydown', function (e) {
        kBuf.push(e.key);
        if (kBuf.length > KONAMI.length) kBuf.shift();
        const match = kBuf.length === KONAMI.length && kBuf.every((k, i) => k.toLowerCase() === KONAMI[i].toLowerCase());
        if (match && !devMode) {
            devMode = true;
            // Add a floating dev badge
            const badge = document.createElement('div');
            badge.id = 'devModeBadge';
            Object.assign(badge.style, {
                position: 'fixed', top: '68px', right: '16px', zIndex: '900',
                background: 'var(--bg-panel)', border: '1px solid var(--warning)',
                color: 'var(--warning)', padding: '6px 12px', fontFamily: 'var(--font-mono)',
                fontSize: '11px', letterSpacing: '1.5px', cursor: 'pointer',
                boxShadow: '0 0 14px rgba(201,162,39,0.4)',
            });
            badge.innerHTML = '⚡ DEVELOPER MODE // UNLOCKED';
            badge.title = 'Click to disable';
            badge.addEventListener('click', () => {
                devMode = false; badge.remove();
            });
            document.body.appendChild(badge);
            try { window.dispatchEvent(new CustomEvent('jae-dev-mode-on')); } catch (err) {}
        }
    });

    // ─── Entry point — handle a /-command ───────────
    async function handle(raw) {
        const trimmed = (raw || '').trim();
        if (!trimmed) return { handled: false };

        // If we are inside a nested mode, route input there regardless of '/' prefix,
        // except for an explicit /exit-<mode> escape (handled inside each).
        if (mode === 'ssh') {
            try { const out = sshHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
            catch (e) { return { handled: true, output: 'ssh error: ' + e.message }; }
        }
        if (mode === 'adventure') {
            try { const out = adventureHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
            catch (e) { return { handled: true, output: 'adventure error: ' + e.message }; }
        }
        if (mode === 'ttyper') {
            try { const out = ttyperHandle(trimmed); return { handled: true, output: out == null ? '' : String(out) }; }
            catch (e) { return { handled: true, output: 'ttyper error: ' + e.message }; }
        }

        if (!trimmed.startsWith('/')) return { handled: false };

        const parts = trimmed.slice(1).split(/\s+/);
        const cmd = (parts[0] || '').toLowerCase();
        const args = parts.slice(1);

        cmdHistory.push(trimmed);
        if (cmdHistory.length > MAX_HISTORY) cmdHistory = cmdHistory.slice(-MAX_HISTORY);
        saveHistory();

        if (!cmd) return { handled: true, output: '' };

        const fn = commands[cmd];
        if (!fn) {
            return { handled: true, output: `command not found: /${cmd}. type /help for available commands.` };
        }

        // Track stats + unlock achievements for known commands
        try {
            sessionCmdCount++;
            sessionPerCmd[cmd] = (sessionPerCmd[cmd] || 0) + 1;
            allTimeStats.total = (allTimeStats.total || 0) + 1;
            allTimeStats.perCmd = allTimeStats.perCmd || {};
            allTimeStats.perCmd[cmd] = (allTimeStats.perCmd[cmd] || 0) + 1;
            saveStats();
            unlockAchievement(cmd);
        } catch (e) {}

        try {
            const result = await fn(args);
            return { handled: true, output: (result == null ? '' : String(result)) };
        } catch (e) {
            return { handled: true, output: `error: ${e.message}` };
        }
    }

    // ─── Boss key — Escape / Ctrl+Shift+B ────────────
    function bossKeyActivate() {
        try {
            if (matrixActive) { matrixActive = false; toggleMatrix(false); }
            if (cmatrixActive) { cmatrixActive = false; toggleCMatrix(false); }
            const badge = document.getElementById('devModeBadge');
            if (badge) badge.style.display = 'none';
            const toasts = document.querySelectorAll('.jae-cli-toast');
            toasts.forEach(t => t.remove());
            const ci = document.getElementById('chatInput');
            if (ci) { ci.value = ''; try { ci.blur(); } catch(e){} }
            const tip = document.createElement('div');
            tip.textContent = 'NORMAL MODE';
            Object.assign(tip.style, {
                position: 'fixed', top: '12px', left: '50%', transform: 'translateX(-50%)',
                background: '#111', color: '#9aa', border: '1px solid #333',
                padding: '4px 10px', fontFamily: 'monospace', fontSize: '10px',
                letterSpacing: '2px', zIndex: '99999', opacity: '0.85',
                borderRadius: '2px', pointerEvents: 'none',
            });
            document.body.appendChild(tip);
            setTimeout(() => { tip.style.transition = 'opacity 0.5s'; tip.style.opacity = '0'; }, 1500);
            setTimeout(() => tip.remove(), 2200);
        } catch (e) {}
    }
    document.addEventListener('keydown', function (e) {
        const isEsc = (e.key === 'Escape');
        const isCtrlShiftB = (e.ctrlKey && e.shiftKey && (e.key === 'B' || e.key === 'b'));
        if (isEsc || isCtrlShiftB) {
            // Don't nuke on Escape while user is typing in chat unless something is actually "hot"
            const hot = matrixActive || cmatrixActive || document.getElementById('devModeBadge') || document.querySelector('.jae-cli-toast');
            if (isCtrlShiftB || (isEsc && hot)) {
                bossKeyActivate();
                if (isCtrlShiftB) e.preventDefault();
            }
        }
    });

    window.__jaeCLI = {
        handle: handle,
        commands: commands,
        isDev: function () { return devMode; },
        isInMode: function () { return mode !== null; },
        currentMode: function () { return mode; },
        bossKey: bossKeyActivate,
    };
})();
