<?php
// ── Backend API ───────────────────────────────────────────────────────────────
$JOBS_DIR  = __DIR__ . '/jobs';
$QUEUE_URL = 'https://aptum.fun/chat-queue.php';
if (!is_dir($JOBS_DIR)) mkdir($JOBS_DIR, 0755, true);

$action = $_POST['action'] ?? $_GET['action'] ?? '';

if ($action === 'send') {
    header('Content-Type: application/json');
    $msg     = trim($_POST['message'] ?? '');
    $session = $_POST['session_id'] ?? null;
    $model   = $_POST['model'] ?? 'llama-3.3-70b';
    if (!$msg) { echo json_encode(['error' => 'empty']); exit; }
    $r = @file_get_contents($QUEUE_URL, false, stream_context_create(['http' => [
        'method'  => 'POST',
        'header'  => "Content-Type: application/json\r\n",
        'content' => json_encode(['action'=>'enqueue','message'=>$msg,'session_id'=>$session,'model'=>$model]),
        'timeout' => 10,
    ]]));
    echo $r ?: json_encode(['error' => 'queue unreachable']);
    exit;
}

if ($action === 'events') {
    header('Content-Type: application/json');
    header('Cache-Control: no-cache');
    $job_id = preg_replace('/[^a-f0-9]/', '', $_GET['job_id'] ?? '');
    $since  = max(0, (int)($_GET['since'] ?? 0));
    if (!$job_id) { echo json_encode(['events'=>[],'next'=>0,'done'=>false]); exit; }
    $evfile  = "$JOBS_DIR/$job_id.events.jsonl";
    $jobfile = "$JOBS_DIR/$job_id.json";
    if (!file_exists($evfile)) { echo json_encode(['events'=>[],'next'=>0,'done'=>false]); exit; }
    clearstatcache();
    $lines  = file($evfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $new    = array_slice($lines, $since);
    $events = array_values(array_filter(array_map(fn($l) => json_decode($l, true), $new)));
    $done   = false;
    if (file_exists($jobfile)) {
        $job  = json_decode(file_get_contents($jobfile), true);
        $done = ($job['status'] ?? '') === 'done';
    }
    echo json_encode(['events'=>$events,'next'=>$since+count($new),'done'=>$done]);
    exit;
}

if ($action === 'models') {
    header('Content-Type: application/json');
    echo json_encode(['models' => [
        ['id'=>'llama-3.3-70b',  'label'=>'Llama 3.3 70B',   'tag'=>'Best',    'color'=>'#34d399'],
        ['id'=>'llama-3.1-8b',   'label'=>'Llama 3.1 8B',    'tag'=>'Fast',    'color'=>'#60a5fa'],
        ['id'=>'kimi-k2',        'label'=>'Kimi K2',          'tag'=>'New',     'color'=>'#a78bfa'],
        ['id'=>'kimi-k2-think',  'label'=>'Kimi K2 Thinking', 'tag'=>'Reason',  'color'=>'#c084fc'],
        ['id'=>'kimi-k2.5',      'label'=>'Kimi K2.5',        'tag'=>'New',     'color'=>'#e879f9'],
        ['id'=>'deepseek-r1',    'label'=>'DeepSeek R1',      'tag'=>'Reason',  'color'=>'#f59e0b'],
        ['id'=>'qwen2.5-72b',    'label'=>'Qwen 2.5 72B',     'tag'=>'',        'color'=>'#38bdf8'],
        ['id'=>'mixtral-8x7b',   'label'=>'Mixtral 8x7B',     'tag'=>'',        'color'=>'#6ee7b7'],
        ['id'=>'mistral-7b',     'label'=>'Mistral 7B',       'tag'=>'',        'color'=>'#86efac'],
        ['id'=>'gemma-2-9b',     'label'=>'Gemma 2 9B',       'tag'=>'',        'color'=>'#fda4af'],
        ['id'=>'nemotron-51b',   'label'=>'Nemotron 51B',     'tag'=>'',        'color'=>'#67e8f9'],
    ], 'default' => 'llama-3.3-70b']);
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>APTUM — AI Agent</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;1,9..40,300&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg:#0a0a0f; --surface:#111118; --surface2:#16161f; --surface3:#1c1c28;
  --border:rgba(255,255,255,0.07); --border-soft:rgba(255,255,255,0.04);
  --accent:#7c6af7; --accent2:#a78bfa; --accent-glow:rgba(124,106,247,0.25);
  --green:#34d399; --red:#f87171; --yellow:#fbbf24;
  --text:#e8e8f0; --text2:#9090a8; --text3:#55556a;
  --user-bg:#1c1c2e; --ai-bg:#111118;
  --think-bg:#160f2a; --think-border:#6d28d9;
  --radius:16px; --sidebar-w:272px;
  --font-head:'Syne',sans-serif; --font-body:'DM Sans',sans-serif;
  --font-mono:'SF Mono','Cascadia Code','Fira Code',monospace;
}
html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--font-body);font-size:15px;line-height:1.6;overflow:hidden}

/* noise overlay */
body::before{content:'';position:fixed;inset:0;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");opacity:.018;pointer-events:none;z-index:999}

/* ── Shell ── */
.shell{display:grid;grid-template-columns:var(--sidebar-w) 1fr;height:100vh}

/* ── Sidebar ── */
.sidebar{background:var(--surface);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden;position:relative}
.sidebar::after{content:'';position:absolute;bottom:0;left:0;right:0;height:100px;background:linear-gradient(to top,var(--surface),transparent);pointer-events:none;z-index:2}

.sb-brand{padding:18px 18px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0}
.brand-orb{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--accent),#4f46e5);display:flex;align-items:center;justify-content:center;box-shadow:0 0 20px var(--accent-glow);flex-shrink:0;position:relative;overflow:hidden}
.brand-orb::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.2),transparent)}
.brand-orb svg{width:17px;height:17px;fill:white;position:relative}
.brand-name{font-family:var(--font-head);font-size:17px;font-weight:800;letter-spacing:-.02em}
.brand-badge{font-size:9px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--accent2);background:rgba(124,106,247,.12);border:1px solid rgba(124,106,247,.2);padding:2px 6px;border-radius:4px;margin-top:1px}

.sb-new{padding:10px 14px;flex-shrink:0}
.btn-new{width:100%;display:flex;align-items:center;gap:8px;padding:9px 13px;background:rgba(124,106,247,.1);border:1px solid rgba(124,106,247,.2);border-radius:10px;color:var(--accent2);font-family:var(--font-body);font-size:13px;font-weight:500;cursor:pointer;transition:all .2s}
.btn-new:hover{background:rgba(124,106,247,.18);border-color:rgba(124,106,247,.35);box-shadow:0 0 12px rgba(124,106,247,.12)}
.btn-new svg{width:14px;height:14px;stroke:currentColor;fill:none;flex-shrink:0}

.sb-label{padding:6px 18px 3px;font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--text3);flex-shrink:0}
.sb-chats{flex:1;overflow-y:auto;padding:4px 8px 80px;scrollbar-width:thin;scrollbar-color:var(--border) transparent}
.chat-item{display:flex;align-items:center;padding:8px 11px;border-radius:9px;cursor:pointer;gap:9px;transition:background .15s;color:var(--text2);font-size:13px;margin-bottom:2px}
.chat-item:hover{background:var(--border-soft);color:var(--text)}
.chat-item.active{background:rgba(124,106,247,.1);color:var(--text)}
.chat-item svg{width:13px;height:13px;stroke:currentColor;fill:none;flex-shrink:0;opacity:.5}
.chat-item-text{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chat-item-time{font-size:10px;color:var(--text3);flex-shrink:0}

.sb-footer{position:absolute;bottom:0;left:0;right:0;padding:10px 8px 12px;border-top:1px solid var(--border);background:var(--surface);z-index:3}
.sb-footer-btn{display:flex;align-items:center;gap:9px;padding:7px 11px;border-radius:9px;cursor:pointer;color:var(--text2);font-size:13px;font-family:var(--font-body);transition:all .15s;border:none;background:transparent;text-align:left;width:100%}
.sb-footer-btn:hover{background:var(--border-soft);color:var(--text)}
.sb-footer-btn svg{width:14px;height:14px;stroke:currentColor;fill:none}

/* ── Main ── */
.main{display:flex;flex-direction:column;overflow:hidden;background:var(--bg);position:relative}
.main::before{content:'';position:absolute;top:-80px;left:50%;transform:translateX(-50%);width:600px;height:280px;background:radial-gradient(ellipse,rgba(124,106,247,.07) 0%,transparent 70%);pointer-events:none}

/* ── Header ── */
.header{height:60px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 20px;flex-shrink:0;backdrop-filter:blur(8px);background:rgba(10,10,15,.85);position:relative;z-index:10}
.header-model{display:flex;align-items:center;gap:8px;cursor:pointer;padding:6px 10px;border-radius:8px;transition:background .15s;border:1px solid transparent}
.header-model:hover{background:var(--surface2);border-color:var(--border)}
.model-dot{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 6px var(--green);animation:statusPulse 2s ease-in-out infinite;flex-shrink:0}
.model-dot.busy{background:var(--yellow);box-shadow:0 0 6px var(--yellow);animation:pulse .8s ease-in-out infinite}
@keyframes statusPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(.85)}}
@keyframes pulse{50%{opacity:.4}}
.model-name{font-family:var(--font-head);font-size:14px;font-weight:600;color:var(--text)}
.header-model svg{width:14px;height:14px;stroke:var(--text3);fill:none}
.header-actions{display:flex;align-items:center;gap:5px}
.hbtn{width:34px;height:34px;border-radius:8px;display:flex;align-items:center;justify-content:center;border:1px solid var(--border);background:transparent;color:var(--text2);cursor:pointer;transition:all .15s;font-family:var(--font-body);font-size:13px}
.hbtn:hover{background:var(--surface2);color:var(--text)}
.hbtn svg{width:15px;height:15px;stroke:currentColor;fill:none}
.hbtn-share{padding:0 12px;gap:6px;display:flex;align-items:center}

/* model dropdown */
.model-drop{position:absolute;top:66px;left:14px;background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:6px;min-width:220px;box-shadow:0 8px 32px rgba(0,0,0,.6);z-index:100;display:none;animation:dropIn .15s ease}
@keyframes dropIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}
.model-drop.open{display:block}
.model-section{padding:4px 8px 2px;font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--text3)}
.model-opt{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:8px;cursor:pointer;transition:background .12s}
.model-opt:hover{background:var(--surface2)}
.model-opt.sel{background:rgba(124,106,247,.1)}
.model-opt-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0}
.model-opt-name{font-size:13px;color:var(--text);font-weight:500;flex:1}
.model-opt-tag{font-size:10px;color:var(--text3);background:var(--surface3);border:1px solid var(--border);padding:1px 5px;border-radius:4px}
.model-check{width:13px;height:13px;stroke:var(--accent2);fill:none;flex-shrink:0}

/* ── Chat scroll ── */
.chat-scroll{flex:1;overflow-y:auto;scroll-behavior:smooth;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.06) transparent}
.chat-inner{max-width:740px;margin:0 auto;padding:36px 22px 20px}

/* ── Welcome ── */
.welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 200px);text-align:center;padding:40px 20px;animation:fadeUp .5s ease both}
@keyframes fadeUp{from{opacity:0;transform:translateY(18px)}to{opacity:1;transform:translateY(0)}}
.welcome-orb{width:68px;height:68px;border-radius:20px;background:linear-gradient(135deg,var(--accent) 0%,#4f46e5 50%,#7c3aed 100%);display:flex;align-items:center;justify-content:center;margin-bottom:22px;box-shadow:0 0 40px rgba(124,106,247,.3),0 0 80px rgba(124,106,247,.1);position:relative;animation:orbFloat 4s ease-in-out infinite}
@keyframes orbFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
.welcome-orb::before{content:'';position:absolute;inset:1px;border-radius:19px;background:linear-gradient(135deg,rgba(255,255,255,.15),transparent)}
.welcome-orb svg{width:30px;height:30px;fill:white;position:relative}
.welcome-title{font-family:var(--font-head);font-size:30px;font-weight:800;letter-spacing:-.03em;margin-bottom:10px;line-height:1.15}
.welcome-sub{font-size:14.5px;color:var(--text2);max-width:360px;line-height:1.6;margin-bottom:32px;font-weight:300}
.suggestions{display:grid;grid-template-columns:1fr 1fr;gap:10px;width:100%;max-width:540px}
.sugg-card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:14px 16px;cursor:pointer;text-align:left;transition:all .2s ease;position:relative;overflow:hidden;font-family:var(--font-body)}
.sugg-card::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,var(--accent-glow),transparent);opacity:0;transition:opacity .2s}
.sugg-card:hover{border-color:rgba(124,106,247,.3);transform:translateY(-1px);box-shadow:0 4px 20px rgba(0,0,0,.3)}
.sugg-card:hover::before{opacity:1}
.sugg-icon{font-size:18px;margin-bottom:8px;display:block}
.sugg-text{font-size:13px;font-weight:500;color:var(--text);line-height:1.4;position:relative}
.sugg-sub{font-size:11.5px;color:var(--text3);margin-top:2px;position:relative}

/* ── Messages ── */
.msg-group{margin-bottom:24px;animation:fadeIn .25s ease both}
@keyframes fadeIn{from{opacity:0;transform:translateY(7px)}to{opacity:1;transform:translateY(0)}}
.msg-row{display:flex;gap:12px;align-items:flex-start}
.msg-row.user{flex-direction:row-reverse}
.avatar{width:30px;height:30px;border-radius:8px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;margin-top:2px}
.avatar.ai{background:linear-gradient(135deg,var(--accent),#4f46e5);box-shadow:0 0 12px rgba(124,106,247,.25);color:white}
.avatar.user{background:linear-gradient(135deg,#1e293b,#334155);color:var(--text2);border:1px solid var(--border)}

.bubble{max-width:75%;padding:12px 16px;border-radius:14px;font-size:14.5px;line-height:1.65}
.bubble.ai{background:var(--ai-bg);border:1px solid var(--border);color:var(--text);border-top-left-radius:4px}
.bubble.user{background:var(--user-bg);border:1px solid rgba(124,106,247,.15);color:var(--text);border-top-right-radius:4px;white-space:pre-wrap;word-break:break-word}

/* thinking block */
.think-block{background:var(--think-bg);border:1px solid var(--think-border);border-radius:10px;margin-bottom:10px;overflow:hidden}
.think-header{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;font-size:.75rem;color:#a78bfa;font-weight:500;user-select:none;transition:background .15s}
.think-header:hover{background:rgba(109,40,217,.15)}
.think-spin{width:11px;height:11px;border:2px solid #6d28d9;border-top-color:transparent;border-radius:50%;animation:spin .7s linear infinite;flex-shrink:0}
@keyframes spin{to{transform:rotate(360deg)}}
.think-label{flex:1}
.think-chevron{transition:transform .2s;font-size:.65rem;color:var(--text3)}
.think-block.collapsed .think-chevron{transform:rotate(-90deg)}
.think-content{padding:0 12px 10px;font-size:.75rem;font-family:var(--font-mono);color:#c4b5fd;line-height:1.6;white-space:pre-wrap;word-break:break-word;max-height:200px;overflow-y:auto}
.think-block.collapsed .think-content{display:none}

/* tool cards */
.tool-card{background:#0d0d15;border:1px solid var(--border);border-left:3px solid #374151;border-radius:8px;padding:8px 12px;margin:6px 0;font-family:var(--font-mono);font-size:.73rem}
.tool-card.running{border-left-color:var(--yellow)}
.tool-card.success{border-left-color:var(--green)}
.tool-card.error{border-left-color:var(--red)}
.tool-header{display:flex;align-items:center;gap:6px}
.tool-name{font-weight:600;color:#94a3b8}
.tool-spin-s{width:8px;height:8px;border:1.5px solid var(--yellow);border-top-color:transparent;border-radius:50%;animation:spin .6s linear infinite}
.tool-args{color:var(--text3);margin-top:3px;white-space:pre-wrap;word-break:break-all}
.tool-out{margin-top:6px;white-space:pre-wrap;word-break:break-all;max-height:130px;overflow-y:auto;color:#86efac}
.tool-out.err{color:var(--red)}

/* markdown prose */
.prose{font-size:14.5px;line-height:1.7;color:var(--text)}
.prose p{margin-bottom:.7em}.prose p:last-child{margin-bottom:0}
.prose h1,.prose h2,.prose h3{font-family:var(--font-head);font-weight:700;margin:1em 0 .4em;line-height:1.25}
.prose h1{font-size:1.2em}.prose h2{font-size:1.1em}.prose h3{font-size:1em}
.prose ul,.prose ol{margin:.4em 0 .4em 1.3em}.prose li{margin-bottom:.15em}
.prose blockquote{border-left:3px solid var(--border);padding-left:12px;color:var(--text2);margin:.5em 0}
.prose a{color:#60a5fa;text-decoration:underline;text-underline-offset:2px}
.prose strong{font-weight:600}.prose em{font-style:italic}
.prose pre{background:#111;border:1px solid var(--border);border-radius:10px;margin:.7em 0;overflow:hidden;position:relative}
.code-head{display:flex;align-items:center;justify-content:space-between;padding:5px 12px;background:#1a1a1a;border-bottom:1px solid var(--border);font-size:.7rem;color:var(--text3);font-family:var(--font-mono)}
.copy-btn{background:transparent;border:none;color:var(--text3);cursor:pointer;font-size:.7rem;padding:2px 7px;border-radius:4px;transition:all .15s;font-family:var(--font-body)}
.copy-btn:hover{background:var(--surface2);color:var(--text)}
.prose pre code{display:block;padding:12px 14px;overflow-x:auto;font-size:.8rem;line-height:1.6;background:transparent!important;font-family:var(--font-mono)}
.prose code:not(pre code){background:rgba(255,255,255,.07);border:1px solid var(--border);padding:1px 5px;border-radius:4px;font-size:.84em;font-family:var(--font-mono);color:var(--accent2)}
.prose table{width:100%;border-collapse:collapse;margin:.7em 0;font-size:.88em}
.prose th,.prose td{border:1px solid var(--border);padding:6px 10px;text-align:left}
.prose th{background:var(--surface2);font-weight:600}
.cursor::after{content:'▋';animation:blink 1s step-end infinite;color:var(--accent)}
@keyframes blink{50%{opacity:0}}

/* message meta */
.msg-meta{display:flex;align-items:center;gap:8px;margin-top:5px;padding:0 4px;min-height:24px}
.msg-row.user+.msg-meta,.msg-meta.user{justify-content:flex-end}
.meta-time{font-size:11px;color:var(--text3)}
.meta-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s}
.msg-group:hover .meta-actions{opacity:1}
.meta-btn{width:26px;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text3);border:none;background:transparent;transition:all .15s}
.meta-btn:hover{background:var(--surface2);color:var(--text2)}
.meta-btn svg{width:13px;height:13px;stroke:currentColor;fill:none}

/* typing indicator */
.typing-bubble{background:var(--ai-bg);border:1px solid var(--border);border-radius:14px;border-top-left-radius:4px;padding:13px 16px;display:flex;gap:5px;align-items:center}
.tdot{width:6px;height:6px;border-radius:50%;background:var(--accent2);opacity:.5;animation:bounce 1.2s ease-in-out infinite}
.tdot:nth-child(2){animation-delay:.2s}.tdot:nth-child(3){animation-delay:.4s}
@keyframes bounce{0%,80%,100%{transform:translateY(0);opacity:.5}40%{transform:translateY(-5px);opacity:1}}

/* ── Input Zone ── */
.input-zone{flex-shrink:0;padding:12px 22px 18px;position:relative;z-index:10}
.input-zone::before{content:'';position:absolute;top:-36px;left:0;right:0;height:36px;background:linear-gradient(to top,var(--bg),transparent);pointer-events:none}
.input-wrap{max-width:740px;margin:0 auto}
.input-box{background:var(--surface);border:1px solid var(--border);border-radius:16px;transition:border-color .2s,box-shadow .2s;overflow:hidden}
.input-box:focus-within{border-color:rgba(124,106,247,.4);box-shadow:0 0 0 3px rgba(124,106,247,.07),0 4px 24px rgba(0,0,0,.4)}
textarea#inp{width:100%;background:transparent;border:none;resize:none;color:var(--text);font-family:var(--font-body);font-size:14.5px;line-height:1.6;padding:14px 16px 6px;outline:none;max-height:200px;min-height:52px;overflow-y:auto;scrollbar-width:thin;caret-color:var(--accent2)}
textarea#inp::placeholder{color:var(--text3)}
.input-bottom{display:flex;align-items:center;justify-content:space-between;padding:6px 8px 8px 12px}
.input-tools{display:flex;align-items:center;gap:1px}
.tool-btn{display:flex;align-items:center;gap:5px;padding:5px 8px;border-radius:7px;border:none;background:transparent;color:var(--text3);font-family:var(--font-body);font-size:12px;cursor:pointer;transition:all .15s}
.tool-btn:hover{background:var(--surface2);color:var(--text2)}
.tool-btn svg{width:13px;height:13px;stroke:currentColor;fill:none}
.tool-div{width:1px;height:14px;background:var(--border);margin:0 2px}
.send-btn{width:36px;height:36px;border-radius:10px;background:var(--accent);border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s ease;flex-shrink:0;position:relative;overflow:hidden}
.send-btn::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.15),transparent)}
.send-btn:hover{background:var(--accent2);box-shadow:0 0 16px rgba(124,106,247,.4);transform:scale(1.04)}
.send-btn:active{transform:scale(.97)}
.send-btn:disabled{opacity:.35;cursor:not-allowed;transform:none;box-shadow:none}
.send-btn svg{width:15px;height:15px;stroke:white;fill:none;position:relative}
.input-footer{text-align:center;margin-top:8px;font-size:11.5px;color:var(--text3)}

/* toast */
#toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%) translateY(12px);background:var(--surface);border:1px solid var(--border);color:var(--text);font-family:var(--font-body);font-size:13px;padding:9px 16px;border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.5);opacity:0;transition:all .25s ease;pointer-events:none;z-index:9999;white-space:nowrap}

/* scrollbar */
::-webkit-scrollbar{width:5px;height:5px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(255,255,255,.07);border-radius:99px}
::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.12)}

@media(max-width:768px){
  .shell{grid-template-columns:1fr}
  .sidebar{display:none}
  .chat-inner{padding:20px 14px 14px}
  .suggestions{grid-template-columns:1fr}
  .bubble{max-width:90%}
}
</style>
</head>
<body>
<div class="shell">

<!-- ══ Sidebar ══ -->
<aside class="sidebar">
  <div class="sb-brand">
    <div class="brand-orb">
      <svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
    </div>
    <div>
      <div class="brand-name">APTUM</div>
      <div class="brand-badge">AI Agent</div>
    </div>
  </div>
  <div class="sb-new">
    <button class="btn-new" onclick="newChat()">
      <svg viewBox="0 0 24 24" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
      New conversation
    </button>
  </div>
  <div class="sb-label">Recent</div>
  <div class="sb-chats" id="sbChats"></div>
  <div class="sb-footer">
    <button class="sb-footer-btn">
      <svg viewBox="0 0 24 24" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 1 0-14.14 14.14M12 2v2m0 18v-2m10-8h-2M4 12H2"/></svg>
      Settings
    </button>
  </div>
</aside>

<!-- ══ Main ══ -->
<main class="main">

  <header class="header">
    <div class="header-model" onclick="toggleModelDrop()">
      <div class="model-dot" id="modelDot"></div>
      <span class="model-name" id="modelName">Loading…</span>
      <svg viewBox="0 0 24 24" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
    </div>

    <div class="model-drop" id="modelDrop"></div>

    <div class="header-actions">
      <button class="hbtn hbtn-share" onclick="showToast('Link copied!')">
        <svg viewBox="0 0 24 24" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
        Share
      </button>
      <button class="hbtn" title="More">
        <svg viewBox="0 0 24 24" stroke-width="2"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg>
      </button>
    </div>
  </header>

  <div class="chat-scroll" id="chatScroll">
    <div class="chat-inner" id="chatInner">

      <div class="welcome" id="welcome">
        <div class="welcome-orb">
          <svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
        </div>
        <h1 class="welcome-title">Hello, I'm APTUM</h1>
        <p class="welcome-sub">Autonomous agent powered by NVIDIA NIM. I can write, run code, search files, and browse the web.</p>
        <div class="suggestions">
          <div class="sugg-card" onclick="suggest(this)">
            <span class="sugg-icon">⚡</span>
            <div class="sugg-text">What Python version is installed?</div>
            <div class="sugg-sub">Run a bash command</div>
          </div>
          <div class="sugg-card" onclick="suggest(this)">
            <span class="sugg-icon">✍️</span>
            <div class="sugg-text">Write and run a hello world script</div>
            <div class="sugg-sub">Create & execute a file</div>
          </div>
          <div class="sugg-card" onclick="suggest(this)">
            <span class="sugg-icon">🔍</span>
            <div class="sugg-text">List all files in the workspace</div>
            <div class="sugg-sub">Explore the filesystem</div>
          </div>
          <div class="sugg-card" onclick="suggest(this)">
            <span class="sugg-icon">🌐</span>
            <div class="sugg-text">Fetch the NVIDIA NIM docs page</div>
            <div class="sugg-sub">Browse the web</div>
          </div>
        </div>
      </div>

      <div id="messages"></div>
    </div>
  </div>

  <div class="input-zone">
    <div class="input-wrap">
      <div class="input-box">
        <textarea id="inp" placeholder="Message APTUM…" rows="1"></textarea>
        <div class="input-bottom">
          <div class="input-tools">
            <button class="tool-btn" onclick="showToast('Attach coming soon')">
              <svg viewBox="0 0 24 24" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
              Attach
            </button>
            <div class="tool-div"></div>
            <button class="tool-btn" onclick="showToast('Web search active')">
              <svg viewBox="0 0 24 24" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
              Search
            </button>
            <button class="tool-btn" onclick="showToast('Code execution active')">
              <svg viewBox="0 0 24 24" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
              Code
            </button>
          </div>
          <button class="send-btn" id="sendBtn" onclick="send()" disabled>
            <svg viewBox="0 0 24 24" stroke-width="2.2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
          </button>
        </div>
      </div>
      <div class="input-footer">APTUM can make mistakes. Verify important information.</div>
    </div>
  </div>
</main>
</div>

<div id="toast"></div>

<script>
// ── marked setup ───────────────────────────────────────────────────────────
const renderer = new marked.Renderer();
renderer.code = (code, lang) => {
  let html;
  try { html = lang && hljs.getLanguage(lang) ? hljs.highlight(code, {language:lang}).value : hljs.highlightAuto(code).value; }
  catch { html = esc(code); }
  const id = 'c' + Math.random().toString(36).slice(2);
  return `<pre><div class="code-head"><span>${esc(lang||'code')}</span><button class="copy-btn" onclick="copyCode('${id}')">Copy</button></div><code id="${id}">${html}</code></pre>`;
};
marked.use({renderer, breaks:true, gfm:true});

// ── State ──────────────────────────────────────────────────────────────────
const BASE = location.pathname;
let sessions = [], activeSession = null, busy = false;
let currentModelId = 'llama-3.3-70b', allModels = [];

// ── Init ───────────────────────────────────────────────────────────────────
(async () => {
  loadSessions();
  renderSidebar();
  try {
    const r = await fetch(BASE + '?action=models');
    const d = await r.json();
    allModels = d.models;
    currentModelId = d.default;
    renderModelDrop();
    updateModelHeader();
    setDot('ok');
  } catch { setDot(''); document.getElementById('modelName').textContent = 'offline'; }
  inp.focus();
})();

// ── Model UI ───────────────────────────────────────────────────────────────
function renderModelDrop() {
  const drop = document.getElementById('modelDrop');
  drop.innerHTML = allModels.map(m => `
    <div class="model-opt${m.id===currentModelId?' sel':''}" onclick="selectModel('${m.id}')">
      <div class="model-opt-dot" style="background:${m.color};box-shadow:0 0 5px ${m.color}"></div>
      <span class="model-opt-name">${esc(m.label)}</span>
      ${m.tag ? `<span class="model-opt-tag">${esc(m.tag)}</span>` : ''}
      ${m.id===currentModelId ? `<svg class="model-check" viewBox="0 0 24 24" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>` : ''}
    </div>`).join('');
}

function updateModelHeader() {
  const m = allModels.find(x => x.id === currentModelId);
  document.getElementById('modelName').textContent = m ? m.label : currentModelId;
}

function selectModel(id) {
  currentModelId = id;
  if (activeSession) activeSession.model = id;
  renderModelDrop();
  updateModelHeader();
  closeDrop();
  saveSessions();
  showToast('Switched to ' + (allModels.find(x=>x.id===id)?.label || id));
}

function toggleModelDrop() { document.getElementById('modelDrop').classList.toggle('open'); }
function closeDrop() { document.getElementById('modelDrop').classList.remove('open'); }
document.addEventListener('click', e => {
  if (!e.target.closest('.header-model') && !e.target.closest('.model-drop')) closeDrop();
});

// ── Session management ─────────────────────────────────────────────────────
function loadSessions() {
  try { sessions = JSON.parse(localStorage.getItem('aptum_v2') || '[]'); }
  catch { sessions = []; }
}
function saveSessions() {
  localStorage.setItem('aptum_v2', JSON.stringify(sessions.slice(0, 60)));
}
function findSess(id) { return sessions.find(s => s.id === id); }

function renderSidebar() {
  const el = document.getElementById('sbChats');
  if (!sessions.length) {
    el.innerHTML = '<div style="padding:10px 12px;font-size:.75rem;color:var(--text3)">No conversations yet</div>';
    return;
  }
  const t = s => { const d=new Date(s.ts||Date.now()),n=new Date(); return d.toDateString()===n.toDateString()?d.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}):d.toLocaleDateString([],{month:'short',day:'numeric'}); };
  el.innerHTML = sessions.map(s => `
    <div class="chat-item${activeSession?.id===s.id?' active':''}" onclick="loadSession('${s.id}')">
      <svg viewBox="0 0 24 24" stroke-width="1.8"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
      <span class="chat-item-text">${esc(s.name)}</span>
      <span class="chat-item-time">${t(s)}</span>
    </div>`).join('');
}

function newChat() {
  activeSession = null;
  document.getElementById('messages').innerHTML = '';
  document.getElementById('welcome').style.display = '';
  inp.value = ''; inp.style.height = 'auto';
  document.getElementById('sendBtn').disabled = true;
  renderSidebar();
}

function loadSession(id) {
  const s = findSess(id);
  if (!s) return;
  activeSession = s;
  document.getElementById('welcome').style.display = 'none';
  document.getElementById('messages').innerHTML = '';
  if (s.model) { currentModelId = s.model; renderModelDrop(); updateModelHeader(); }
  for (const m of s.messages || []) {
    if (m.role === 'user') renderUser(m.content, false);
    else if (m.role === 'assistant') {
      const g = renderAssistantGroup();
      if (m.thinking) renderThink(g, m.thinking, true);
      renderMd(g.querySelector('.prose'), m.content);
      renderMeta(g, m.content, false);
    }
  }
  renderSidebar();
  scrollBottom();
}

// ── Send ───────────────────────────────────────────────────────────────────
async function send() {
  const msg = inp.value.trim();
  if (!msg || busy) return;
  document.getElementById('welcome').style.display = 'none';

  if (!activeSession) {
    activeSession = {id:null, name:msg.slice(0,45)+(msg.length>45?'…':''), model:currentModelId, messages:[], ts:Date.now()};
  }

  renderUser(msg, true);
  inp.value = ''; resize(); setBusy(true);

  const fd = new FormData();
  fd.append('action','send'); fd.append('message',msg);
  fd.append('model', currentModelId);
  if (activeSession.id) fd.append('session_id', activeSession.id);

  let jobId;
  try {
    const r = await fetch(BASE, {method:'POST', body:fd});
    const d = await r.json();
    if (d.error) throw new Error(d.error);
    jobId = d.job_id;
  } catch(e) {
    renderErr('Failed to queue: ' + e.message);
    setBusy(false); return;
  }

  // Typing indicator
  const typingId = 'ty_' + Date.now();
  showTyping(typingId);

  const g = renderAssistantGroup();
  g.style.display = 'none'; // hide until we have content

  let thinkBuf='', thinkEl=null, tokenBuf='', offset=0, iter=0, gotContent=false;
  const proseEl = g.querySelector('.prose');
  proseEl.classList.add('cursor');

  const poll = setInterval(async () => {
    if (++iter > 360) { clearInterval(poll); renderErr('[timeout]'); setBusy(false); return; }
    try {
      const r = await fetch(`${BASE}?action=events&job_id=${jobId}&since=${offset}`);
      const d = await r.json();
      for (const ev of (d.events||[])) {
        if (!gotContent && (ev.event==='thinking'||ev.event==='token'||ev.event==='tool_start')) {
          gotContent = true;
          removeTyping(typingId);
          g.style.display = '';
        }
        handleEv(ev);
      }
      offset = d.next ?? offset;
      if (d.done) { clearInterval(poll); finalize(); }
    } catch {}
  }, 500);

  function handleEv(ev) {
    const data = ev.data || {};
    switch(ev.event) {
      case 'session':
        activeSession.id    = data.session_id;
        activeSession.model = data.model || currentModelId;
        currentModelId      = activeSession.model;
        updateModelHeader(); renderModelDrop();
        if (!findSess(activeSession.id)) { sessions.unshift(activeSession); }
        saveSessions(); renderSidebar();
        setDot('busy');
        break;
      case 'thinking':
        thinkBuf += data.text || '';
        if (!thinkEl) { thinkEl = addThink(g, false); }
        thinkEl.querySelector('.think-content').textContent = thinkBuf;
        scrollBottom();
        break;
      case 'token':
        tokenBuf += data.text || '';
        renderMd(proseEl, tokenBuf);
        scrollBottom();
        break;
      case 'tool_start': {
        const args = Object.entries(data.args||{}).map(([k,v])=>`${k}: ${v}`).join('\n');
        const c = document.createElement('div');
        c.className = 'tool-card running';
        c.id = 'tc_'+data.name+'_'+Date.now();
        c.innerHTML = `<div class="tool-header"><div class="tool-spin-s"></div><span class="tool-name">${esc(data.name)}</span></div>`
          +(args?`<div class="tool-args">${esc(args)}</div>`:'');
        g.querySelector('.tools').appendChild(c);
        scrollBottom(); break;
      }
      case 'tool_end': {
        const cards = g.querySelectorAll('.tool-card');
        let card = null;
        for (let i=cards.length-1;i>=0;i--) if(cards[i].querySelector('.tool-name')?.textContent===data.name){card=cards[i];break;}
        if (card) {
          card.className='tool-card '+(data.is_error?'error':'success');
          card.querySelector('.tool-spin-s')?.remove();
          const o=document.createElement('div');
          o.className='tool-out'+(data.is_error?' err':'');
          o.textContent=data.output||'';
          card.appendChild(o);
        }
        scrollBottom(); break;
      }
      case 'done':
        if (data.text && !tokenBuf) tokenBuf = data.text;
        break;
      case 'error':
        proseEl.textContent += '\n[Error: '+(data.message||'unknown')+']';
        break;
    }
  }

  function finalize() {
    removeTyping(typingId);
    g.style.display = '';
    proseEl.classList.remove('cursor');
    if (thinkEl) collapseThink(thinkEl);
    renderMd(proseEl, tokenBuf);
    renderMeta(g, tokenBuf, true);
    if (activeSession) {
      activeSession.messages.push({role:'assistant', content:tokenBuf, thinking:thinkBuf||null});
      saveSessions();
    }
    setBusy(false); scrollBottom();
  }
}

// ── Thinking ───────────────────────────────────────────────────────────────
function addThink(g, collapsed) {
  const b = document.createElement('div');
  b.className='think-block'+(collapsed?' collapsed':'');
  b.innerHTML=`<div class="think-header" onclick="this.parentElement.classList.toggle('collapsed')">
    <div class="think-spin"></div><span class="think-label">Thinking…</span><span class="think-chevron">▾</span>
  </div><div class="think-content"></div>`;
  g.querySelector('.think-area').appendChild(b);
  return b;
}
function renderThink(g, text, collapsed) {
  const b=addThink(g,collapsed);
  b.querySelector('.think-content').textContent=text;
  if(collapsed){b.querySelector('.think-label').textContent='Thought process';b.querySelector('.think-spin').style.display='none';}
}
function collapseThink(b) {
  b.classList.add('collapsed');
  b.querySelector('.think-label').textContent='Thought process';
  const s=b.querySelector('.think-spin'); if(s) s.style.display='none';
}

// ── Typing indicator ───────────────────────────────────────────────────────
function showTyping(id) {
  const el=document.createElement('div');
  el.className='msg-group'; el.id=id;
  el.innerHTML=`<div class="msg-row"><div class="avatar ai">A</div><div class="typing-bubble"><div class="tdot"></div><div class="tdot"></div><div class="tdot"></div></div></div>`;
  document.getElementById('messages').appendChild(el);
  scrollBottom();
}
function removeTyping(id) { document.getElementById(id)?.remove(); }

// ── DOM helpers ────────────────────────────────────────────────────────────
function renderUser(text, save) {
  if (save && activeSession) activeSession.messages.push({role:'user', content:text});
  const g = document.createElement('div');
  g.className='msg-group';
  g.innerHTML=`<div class="msg-row user"><div class="avatar user">U</div><div class="bubble user">${esc(text)}</div></div>
  <div class="msg-meta user"><span class="meta-time">${time()}</span></div>`;
  document.getElementById('messages').appendChild(g);
  scrollBottom();
}

function renderAssistantGroup() {
  const g = document.createElement('div');
  g.className='msg-group';
  g.innerHTML=`<div class="msg-row"><div class="avatar ai">A</div>
    <div style="flex:1;min-width:0">
      <div class="think-area"></div>
      <div class="tools"></div>
      <div class="bubble ai"><div class="prose"></div></div>
    </div></div>
  <div class="msg-meta" id="meta_${Date.now()}"></div>`;
  document.getElementById('messages').appendChild(g);
  return g;
}

function renderMeta(g, text, live) {
  const m = g.querySelector('.msg-meta');
  if (!m) return;
  m.innerHTML=`<span class="meta-time">${time()}</span>
  <div class="meta-actions">
    <button class="meta-btn" title="Copy" onclick="copyText(this,'${escAttr(text)}')"><svg viewBox="0 0 24 24" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
    <button class="meta-btn" title="Good"><svg viewBox="0 0 24 24" stroke-width="2"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3H14z"/><path d="M7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg></button>
    <button class="meta-btn" title="Bad"><svg viewBox="0 0 24 24" stroke-width="2"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3H10z"/><path d="M17 2h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"/></svg></button>
  </div>`;
}

function renderErr(msg) {
  const g = document.createElement('div');
  g.className='msg-group';
  g.innerHTML=`<div class="msg-row"><div class="avatar ai" style="background:#7f1d1d">!</div><div class="bubble ai" style="color:var(--red)">${esc(msg)}</div></div>`;
  document.getElementById('messages').appendChild(g);
  scrollBottom();
}

function renderMd(el, text) {
  if (!text) return;
  try { el.innerHTML = marked.parse(text); }
  catch { el.textContent = text; }
}

// ── Utilities ──────────────────────────────────────────────────────────────
function suggest(card) { inp.value=card.querySelector('.sugg-text').textContent.trim(); resize(); updateBtn(); send(); }
function scrollBottom() { const s=document.getElementById('chatScroll'); s.scrollTop=s.scrollHeight; }
function esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function escAttr(s) { return s.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/\n/g,'\\n'); }
function time() { return new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); }

function copyCode(id) {
  const el=document.getElementById(id); if(!el) return;
  navigator.clipboard.writeText(el.textContent).then(()=>{
    const b=el.closest('pre')?.querySelector('.copy-btn');
    if(b){b.textContent='Copied!';setTimeout(()=>b.textContent='Copy',2000);}
  });
}
function copyText(btn, text) {
  navigator.clipboard.writeText(text).then(()=>{
    btn.innerHTML=`<svg viewBox="0 0 24 24" stroke-width="2" style="stroke:var(--green)"><polyline points="20 6 9 17 4 12"/></svg>`;
    setTimeout(()=>btn.innerHTML=`<svg viewBox="0 0 24 24" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`,2000);
    showToast('Copied!');
  });
}

function setBusy(v) {
  busy=v;
  document.getElementById('modelDot').className='model-dot'+(v?' busy':'');
  document.getElementById('sendBtn').disabled=v||!inp.value.trim();
  if(!v) setDot('ok');
}
function setDot(s) { document.getElementById('modelDot').className='model-dot'+(s?' '+s:''); }

function showToast(msg) {
  const t=document.getElementById('toast');
  t.textContent=msg; t.style.opacity='1'; t.style.transform='translateX(-50%) translateY(0)';
  clearTimeout(t._t); t._t=setTimeout(()=>{t.style.opacity='0';t.style.transform='translateX(-50%) translateY(8px)';},2200);
}

// ── Input ──────────────────────────────────────────────────────────────────
const inp = document.getElementById('inp');
function resize() { inp.style.height='auto'; inp.style.height=Math.min(inp.scrollHeight,200)+'px'; }
function updateBtn() { document.getElementById('sendBtn').disabled=busy||!inp.value.trim(); }
inp.addEventListener('input', () => { resize(); updateBtn(); });
inp.addEventListener('keydown', e => { if(e.key==='Enter'&&(e.ctrlKey||e.metaKey)){e.preventDefault();send();} });
</script>
</body>
</html>
