Files
jarvis/public_html/index.html
T
myron b014fd96ab Fix HA toggle (Rocket Loader) and network map cleanup
HA toggle broken after modularization: separate <script src> files are
deferred by Cloudflare Rocket Loader, so inline onchange handlers fire
before toggleHA() is defined. Fix: add data-cfasync="false" to all
four JARVIS script tags to prevent Rocket Loader interference.

Network map cleanup:
- Deduplicate agent devices by hostname — two homeassistant agents
  (10.48.200.97 and 172.30.32.1) now show as one node; prefer online
  over offline, then most-recently-seen
- Offline agents excluded from inner rings — jellyfin (offline June 3)
  and mini_it12 (offline June 12) no longer appear on the map
- DB-pinned devices (Yealink phones) unaffected; still show in devices ring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 03:20:37 +00:00

455 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>JARVIS — Integrated Defense and Logistics System</title>
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700;900&family=Rajdhani:wght@300;400;500;600&family=Share+Tech+Mono&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="assets/css/jarvis.css"/>
</head>
<body>
<canvas id="particleCanvas"></canvas>
<canvas id="dataStreamCanvas"></canvas>
<canvas id="hudCornersCanvas"></canvas>
<div id="alertOverlay"></div>
<div id="vignetteOverlay"></div>
<div id="faceScanOverlay">
<div class="fso-ring"></div>
<div class="fso-tr"></div>
<div class="fso-bl"></div>
<div class="fso-dot"></div>
<div class="fso-label">TRACKING</div>
</div>
<div class="scanlines"></div>
<div class="scanline-sweep"></div>
<!-- ── LOGIN ────────────────────────────────────────────────────────── -->
<div id="loginScreen">
<div class="login-reactor">
<div class="ring r1"></div><div class="ring r2"></div>
<div class="ring r3"></div><div class="ring r4"></div>
<div class="core"></div>
<div class="hud-ticks"></div>
</div>
<h1>JARVIS</h1>
<p>Just A Rather Very Intelligent System</p>
<form class="login-form" id="loginForm">
<input type="text" id="loginUser" placeholder="IDENTIFICATION" autocomplete="username" value="myron"/>
<input type="password" id="loginPass" placeholder="ACCESS CODE" autocomplete="current-password"/>
<button type="submit">INITIALIZE SYSTEM</button>
<div id="loginError"></div>
</form>
</div>
<!-- ── MAIN APP ──────────────────────────────────────────────────────── -->
<div id="app">
<!-- Top Bar -->
<div id="topBar">
<div class="tb-logo">
<div class="tb-reactor"><div class="tbr-ring tbr-r1"></div><div class="tbr-ring tbr-r2"></div><div class="tbr-core"></div><canvas id="audioRingCanvas" width="60" height="60"></canvas></div>
<span class="tb-logo-text" data-text="JARVIS SYSTEM">JARVIS SYSTEM</span>
</div>
<div class="tb-center">
<div class="tb-stat">LOCAL&nbsp;<span id="tb-cpu">--</span>% CPU</div>
<div class="tb-stat">MEM&nbsp;<span id="tb-mem">--</span>%</div>
<div class="tb-stat">DO SERVER&nbsp;<span id="tb-do" class="text-dim">--</span></div>
<div class="tb-stat"><span id="tb-alerts" class="text-green">NO ALERTS</span></div>
<div class="tb-stat" id="tb-planner" style="display:none"><span id="tb-planner-text" class="text-yellow"></span></div>
</div>
<div class="tb-right">
<div>
<div id="clock">--:--:--</div>
<div id="date-display">LOADING...</div>
</div>
<div class="status-dot"></div>
<button id="cameraBtn" class="btn-camera" onclick="toggleCamera()" title="Auto-mic when face detected (hands-free)">◉ CAMERA</button>
<button id="panelToggleBtn" class="btn-panels" onclick="togglePanels()" title="Toggle side panels (or say 'focus mode')">◧ PANELS</button>
<button id="agentBtn" class="btn-agent" onclick="openAgentModal()" title="Install JARVIS Agent on this machine"><div class="agent-dot"></div>AGENT</button>
<button id="btn-swap-panels" onclick="swapPanels()" title="Swap side panels">⇄ SWAP</button>
<button class="btn-logout" onclick="logout()">LOGOUT</button>
</div>
</div>
<!-- Main Layout -->
<div id="mainLayout">
<!-- LEFT: System Stats -->
<div id="leftPanel">
<div class="panel">
<div class="panel-title">JARVIS SERVER <span style="font-size:0.5rem;color:var(--text-dim)">165.22.1.228</span><div class="indicator"></div></div>
<!-- Metric bars + sparklines -->
<div class="metric-row">
<div class="metric-label">CPU <span id="cpu-val">--%</span></div>
<div class="metric-bar"><div class="metric-bar-fill" id="cpu-bar" style="width:0%"></div></div>
<div class="sparkline-wrap"><canvas id="spark-cpu"></canvas></div>
</div>
<div class="metric-row">
<div class="metric-label">MEMORY <span id="mem-val">--%</span></div>
<div class="metric-bar"><div class="metric-bar-fill" id="mem-bar" style="width:0%"></div></div>
<div class="sparkline-wrap"><canvas id="spark-mem"></canvas></div>
</div>
<div class="metric-row">
<div class="metric-label">DISK <span id="disk-val">--%</span></div>
<div class="metric-bar"><div class="metric-bar-fill" id="disk-bar" style="width:0%"></div></div>
<div class="sparkline-wrap"><canvas id="spark-disk"></canvas></div>
</div>
<div class="val-row"><div class="lbl">UPTIME</div><div class="val" id="uptime-val">--</div></div>
<div class="val-row"><div class="lbl">LOAD</div><div class="val" id="load-val">--</div></div>
<div class="val-row"><div class="lbl">HOST</div><div class="val" id="host-val">--</div></div>
<!-- Services -->
<div style="font-family:var(--font-display);font-size:0.5rem;letter-spacing:2px;color:var(--text-dim);margin:10px 0 5px">SERVICES</div>
<div id="services-list">
<div class="loading-shimmer" style="margin-bottom:4px"></div>
</div>
<!-- Site health -->
<div style="font-family:var(--font-display);font-size:0.5rem;letter-spacing:2px;color:var(--text-dim);margin:10px 0 5px">WEBSITES</div>
<div id="sites-list">
<div class="loading-shimmer" style="margin-bottom:4px"></div>
</div>
<!-- Top processes -->
<div style="font-family:var(--font-display);font-size:0.5rem;letter-spacing:2px;color:var(--text-dim);margin:10px 0 5px">PROCESSES</div>
<div id="procs-list">
<div class="loading-shimmer" style="margin-bottom:4px"></div>
</div>
</div>
</div>
<!-- CENTER: Arc Reactor + Chat -->
<div id="centerPanel">
<div id="arcReactor">
<div class="arc-ring r1"></div><div class="arc-ring r2"></div>
<div class="arc-ring r3"></div><div class="arc-ring r4"></div>
<div class="arc-ring r5"></div><div class="arc-ring r6"></div>
<div class="arc-ring r7"></div>
<div class="arc-core"></div>
<div class="hud-ticks"></div>
</div>
<div id="chatArea">
<div id="chatLog">
<div class="msg system">◈ JARVIS ONLINE — AWAITING INSTRUCTIONS ◈</div>
</div>
<div id="waveform">
<div class="wave-bar" style="--d:0.4s"></div>
<div class="wave-bar" style="--d:0.5s"></div>
<div class="wave-bar" style="--d:0.35s"></div>
<div class="wave-bar" style="--d:0.6s"></div>
<div class="wave-bar" style="--d:0.45s"></div>
<div class="wave-bar" style="--d:0.55s"></div>
<div class="wave-bar" style="--d:0.4s"></div>
<div class="wave-bar" style="--d:0.5s"></div>
<div class="wave-bar" style="--d:0.38s"></div>
<div class="wave-bar" style="--d:0.52s"></div>
</div>
<div id="contextChip">
<span id="contextType">CONTEXT</span>
<span id="contextLabel"></span>
<button id="contextClear" onclick="clearContext()" title="Clear context">×</button>
</div>
<div id="inputArea">
<button id="micBtn" onclick="toggleVoice()" title="Voice Command">
<span id="micIcon">🎤</span>
</button>
<input type="text" id="textInput" placeholder="Enter command or speak to JARVIS..."
autocomplete="off" onkeydown="if(event.key==='Enter')sendMessage()"/>
<button id="sendBtn" onclick="sendMessage()">TRANSMIT</button>
<button id="searchBtn" onclick="openSearchModal()" title="Search chat history" style="background:transparent;border:1px solid var(--panel-border);color:var(--text-dim);font-size:1rem;padding:0 10px;cursor:pointer;transition:all 0.2s;min-height:36px" onmouseover="this.style.color='var(--cyan)';this.style.borderColor='var(--cyan)'" onmouseout="this.style.color='var(--text-dim)';this.style.borderColor='var(--panel-border)'">🔍</button>
</div>
</div>
</div>
<!-- RIGHT: Network + VMs + HA -->
<div id="rightPanel">
<!-- Weather Widget -->
<div class="panel" style="flex:0 0 auto">
<div class="panel-title">WEATHER <span id="weather-loc" style="font-size:0.55rem;color:var(--text-dim)">FORT WORTH, TX</span></div>
<div style="display:flex;align-items:flex-start;gap:12px;margin-bottom:8px">
<div style="flex:1">
<div style="display:flex;align-items:baseline;gap:8px">
<span style="font-size:1.8rem;font-family:var(--font-display);color:var(--cyan);line-height:1" id="weather-temp">--</span>
<span style="font-size:0.75rem;color:var(--text-dim)">°F</span>
</div>
<div style="font-size:0.7rem;color:var(--text-primary);margin-top:2px;font-family:var(--font-display);letter-spacing:1px" id="weather-desc">LOADING...</div>
<div style="font-size:0.58rem;color:var(--text-dim);margin-top:3px" id="weather-details"></div>
</div>
<div style="text-align:right;flex-shrink:0">
<div style="font-size:0.52rem;color:var(--text-dim);letter-spacing:1px">FEELS LIKE</div>
<div style="font-size:1rem;font-family:var(--font-display);color:var(--cyan)" id="weather-feels">--°F</div>
<div style="font-size:0.52rem;color:var(--text-dim);margin-top:4px;letter-spacing:1px">HUMIDITY</div>
<div style="font-size:0.75rem;font-family:var(--font-display);color:var(--text-primary)" id="weather-humidity">--%</div>
</div>
</div>
<div id="weather-forecast" style="display:grid;grid-template-columns:repeat(4,1fr);gap:4px"></div>
</div>
<!-- Network Status -->
<div class="panel" style="flex:0 1 auto;max-height:35%;display:flex;flex-direction:column;min-height:100px">
<div class="panel-title">NETWORK STATUS <div class="indicator"></div><span id="net-agent-count" style="font-size:0.6rem;color:var(--cyan);margin-left:auto"></span><button onclick="addNetworkDevice()" title="Add device" style="background:none;border:none;color:var(--cyan);cursor:pointer;font-size:1rem;padding:0 4px;margin-left:4px;line-height:1">+</button></div>
<canvas id="topoCanvas" height="100"></canvas>
<div id="network-list" style="overflow-y:auto;flex:1;padding-right:2px">
<div class="loading-shimmer" style="margin-bottom:6px"></div>
<div class="loading-shimmer" style="margin-bottom:6px"></div>
<div class="loading-shimmer"></div>
</div>
<button onclick="scanNetwork()" style="margin-top:8px;flex-shrink:0;width:100%;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:4px;padding:4px;color:var(--cyan);font-family:var(--font-display);font-size:0.55rem;letter-spacing:2px;cursor:pointer" id="scanBtn">RUN NETWORK SCAN</button>
</div>
<!-- Tab Panel -->
<div class="panel" style="flex:1;overflow:hidden;display:flex;flex-direction:column">
<!-- Clearance Alert Banner -->
<div id="clearance-banner">
<span>◈ CLEARANCE REQUIRED —</span>
<span class="clr-count" id="clr-banner-count">0</span>
<span>PENDING AUTHORIZATION</span>
<span class="clr-view" onclick="switchTab('clearance')">VIEW →</span>
</div>
<div class="tab-bar">
<div class="tab active" onclick="switchTab('ha')">HOME</div>
<div class="tab" onclick="switchTab('alerts')">ALERTS</div>
<div class="tab" onclick="switchTab('news')">NEWS</div>
<div class="tab" onclick="switchTab('agents')">AGENTS</div>
<div class="tab" onclick="switchTab('sites')">SITES</div>
<div class="tab" id="tab-btn-intel" onclick="switchTab('intel')">INTEL</div>
<div class="tab" id="tab-btn-comms" onclick="switchTab('comms')">COMMS</div>
<div class="tab" id="tab-btn-guardian" onclick="switchTab('guardian')">GUARDIAN</div>
<div class="tab" id="tab-btn-missions" onclick="switchTab('missions')">MISSIONS</div>
<div class="tab" id="tab-btn-directives" onclick="switchTab('directives')">DIRECTIVES</div>
<div class="tab" id="tab-btn-clearance" onclick="switchTab('clearance')">CLEARANCE <span id="clr-tab-badge" style="display:none;background:#ff2244;color:#fff;border-radius:3px;padding:0 4px;font-size:0.5rem;margin-left:2px"></span></div>
</div>
<div id="tab-vms" class="tab-pane" style="overflow-y:auto;flex:1">
<div id="vm-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-ha" class="tab-pane active" style="overflow-y:auto;flex:1">
<div id="ha-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-alerts" class="tab-pane" style="overflow-y:auto;flex:1">
<div id="alerts-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-news" class="tab-pane" style="overflow-y:auto;flex:1">
<div style="display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px">
<button onclick="toggleNewsFilter()" title="Filter news sources" style="background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:0.85rem;padding:2px 4px;transition:color 0.2s" onmouseover="this.style.color='var(--cyan)'" onmouseout="this.style.color='var(--text-dim)'"></button>
</div>
<div id="news-filter-panel" style="display:none;margin-bottom:8px;padding:8px;background:rgba(0,212,255,0.04);border:1px solid var(--panel-border);border-radius:4px">
<div style="font-family:var(--font-display);font-size:0.52rem;letter-spacing:2px;color:var(--cyan);margin-bottom:6px">SHOW CATEGORIES</div>
<div id="news-filter-checkboxes" style="display:flex;flex-direction:column;gap:4px"></div>
</div>
<div id="news-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-agents" class="tab-pane" style="overflow-y:auto;flex:1">
<div id="agents-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-intel" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="intel-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-comms" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="comms-list"><div class="loading-shimmer"></div></div>
<div class="comms-section-label" style="margin:12px 4px 6px">◈ OUTBOX — SENT &amp; QUEUED</div>
<div id="comms-outbox"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-guardian" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="guardian-list"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-missions" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="missions-hud"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-directives" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="directives-hud"><div class="loading-shimmer"></div></div>
</div>
<div id="tab-clearance" class="tab-pane" style="overflow-y:auto;flex:1;padding:4px 0">
<div id="clearance-hud"><div class="loading-shimmer"></div></div>
</div>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div id="bottomBar">
<div class="bb-item">
<div class="bb-dot online"></div>
<span>JARVIS CORE</span> ONLINE
</div>
<div class="bb-item">
<div class="bb-dot" id="bb-do-dot"></div>
<span>DO SERVER</span> <span id="bb-do-status">CHECKING</span>
</div>
<div class="bb-item">
<div class="bb-dot" id="bb-pve-dot"></div>
<span>PROXMOX</span> <span id="bb-pve-status">CHECKING</span>
</div>
<div class="bb-item">
<div class="bb-dot" id="bb-ha-dot"></div>
<span>HOME ASSISTANT</span> <span id="bb-ha-status">CHECKING</span>
</div>
<div class="bb-item">
<div class="bb-dot" id="bb-agent-dot"></div>
<span>AGENTS</span> <span id="bb-agent-status">--</span>
</div>
<div class="bb-item">
<div class="bb-dot" id="bb-arc-dot"></div>
<span>ARC REACTOR</span> <span id="bb-arc-status">OFFLINE</span>
</div>
<div class="bb-item" id="bb-guardian-item" style="cursor:pointer" onclick="switchGuardianTab()">
<div class="bb-dot" id="bb-guardian-dot" style="background:var(--text-dim)"></div>
<span>GUARDIAN</span> <span id="bb-guardian-status" style="color:var(--text-dim)">INIT</span>
<span id="bb-guardian-badge" style="display:none;background:var(--red);color:#fff;font-size:0.45rem;padding:1px 4px;border-radius:2px;font-family:var(--font-mono);letter-spacing:0">0</span>
</div>
<div class="bb-item" style="cursor:pointer" onclick="switchTab('clearance')" id="bb-memory-item">
<div class="bb-dot" id="bb-memory-dot" style="background:rgba(0,212,255,0.3)"></div>
<span>MEMORY</span> <span id="bb-memory-count" style="color:var(--text-dim)">--</span>
</div>
<div id="ekgWrap" style="margin-left:auto"><canvas id="ekgCanvas"></canvas></div>
<div style="font-size:0.65rem;flex-shrink:0">
JARVIS v2.0 · SECURITY LEVEL ALPHA · UPDATED <span id="last-refresh">--:--:--</span>
</div>
</div>
</div>
<!-- NETWORK MAP OVERLAY -->
<div id="netMapOverlay">
<div id="nmHeader">
<div id="nmTitle"><div class="nm-pulse"></div>◈ NETWORK TOPOLOGY — LIVE</div>
<div id="nmStats">NODES <span id="nm-node-count"></span> &nbsp;·&nbsp; ONLINE <span id="nm-online-count"></span> &nbsp;·&nbsp; AGENTS <span id="nm-agent-count"></span></div>
<div style="display:flex;align-items:center;gap:12px">
<span style="font-family:var(--font-mono);font-size:0.52rem;color:var(--text-dim);letter-spacing:1px">SAY "CLOSE MAP"</span>
<button id="nmClose" onclick="closeNetMap()">✕ CLOSE</button>
</div>
</div>
<canvas id="nmCanvas"></canvas>
<div id="nmLegend">
<span><span class="nm-leg-dot" style="background:#00ff88;box-shadow:0 0 4px #00ff88"></span>PROXMOX</span>
<span><span class="nm-leg-dot" style="background:#ffd700;box-shadow:0 0 4px #ffd700"></span>SERVICES</span>
<span><span class="nm-leg-dot" style="background:#00beff;box-shadow:0 0 4px #00beff"></span>AGENTS</span>
<span><span class="nm-leg-dot" style="background:rgba(0,160,200,0.9)"></span>DEVICES</span>
<span><span class="nm-leg-dot" style="background:rgba(0,110,170,0.9)"></span>NETWORK</span>
<span><span class="nm-leg-dot" style="background:#ff2244;box-shadow:0 0 4px #ff2244"></span>OFFLINE</span>
<span style="margin-left:auto;opacity:0.4;font-size:0.5rem">CYAN = DATA IN · ORANGE = CMD OUT</span>
</div>
<div id="nmNodeInfo"><div class="ni-title" id="ni-name"></div><div class="ni-row" id="ni-ip"></div><div class="ni-row" id="ni-status"></div><div class="ni-row" id="ni-type"></div></div>
</div>
<!-- COMMAND PALETTE -->
<div id="cmdPalette">
<div id="cmdPaletteBox">
<input id="cmdPaletteInput" type="text" placeholder="TYPE A COMMAND..." autocomplete="off" spellcheck="false"/>
<div id="cmdPaletteList"></div>
<div id="cmdPaletteFooter">
<span>↑↓ navigate</span><span>↵ execute</span><span>ESC close</span><span>CTRL+K toggle</span>
</div>
</div>
</div>
<!-- SLEEP OVERLAY -->
<div id="sleepOverlay">
<div class="sleep-reactor">
<div class="sleep-ring sr1"></div>
<div class="sleep-ring sr2"></div>
<div class="sleep-ring sr3"></div>
<div class="sleep-core"></div>
</div>
<div class="sleep-label">JARVIS — STANDBY</div>
<div class="sleep-sub">SAY "WAKE UP JARVIS" TO RESUME</div>
</div>
<div id="sitesModal" style="position:fixed;inset:0;background:rgba(0,0,0,0.92);z-index:9999;display:none;align-items:flex-start;justify-content:center;padding:24px;overflow-y:auto">
<div style="background:var(--panel-bg);border:1px solid var(--panel-border);width:100%;max-width:960px;font-family:var(--font-mono)">
<div style="display:flex;align-items:center;justify-content:space-between;padding:16px 24px;border-bottom:1px solid var(--panel-border)">
<div style="color:var(--cyan);font-size:0.75rem;letter-spacing:4px">◈ SITES MANAGER — EMAIL SETTINGS</div>
<button onclick="closeSitesModal()" style="background:transparent;border:1px solid var(--panel-border);color:var(--text-dim);cursor:pointer;font-family:var(--font-mono);font-size:0.6rem;padding:4px 12px;letter-spacing:2px">✕ CLOSE</button>
</div>
<div style="padding:20px 24px">
<!-- Global API Key -->
<div style="background:rgba(0,212,255,0.04);border:1px solid rgba(0,212,255,0.2);padding:16px;margin-bottom:20px">
<div style="color:var(--cyan);font-size:0.62rem;letter-spacing:3px;margin-bottom:10px">▸ CYBERMAIL API KEY — PUSH TO ALL SITES</div>
<div style="display:flex;gap:10px;align-items:center">
<input id="global-api-key" type="password"
style="flex:1;background:#0a0f1a;border:1px solid rgba(0,212,255,0.25);color:var(--text);font-family:var(--font-mono);font-size:0.7rem;padding:8px 12px;outline:none"
placeholder="sk_live_...">
<button onclick="pushApiKey()"
style="background:rgba(0,212,255,0.12);border:1px solid var(--cyan);color:var(--cyan);font-family:var(--font-mono);font-size:0.6rem;letter-spacing:2px;padding:8px 18px;cursor:pointer;white-space:nowrap">
PUSH TO ALL
</button>
</div>
<div id="push-status" style="font-size:0.6rem;color:var(--text-dim);margin-top:6px;min-height:16px"></div>
</div>
<!-- Site Cards Grid -->
<div id="sites-grid" style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div style="grid-column:1/-1;color:var(--text-dim);font-size:0.65rem">LOADING...</div>
</div>
</div>
</div>
</div>
<div id="agentModal">
<div class="agent-modal-box">
<button class="agent-modal-close" onclick="document.getElementById('agentModal').classList.remove('open')">&#x2715; CLOSE</button>
<h3 id="agentModalTitle">&#9679; JARVIS AGENT</h3>
<div id="agentModalContent"></div>
</div>
</div>
<!-- Hidden camera feed for face detection -->
<video id="faceVideo" autoplay muted playsinline
style="position:fixed;top:-9999px;left:-9999px;width:320px;height:240px"></video>
<script src="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js" crossorigin="anonymous"></script>
<script data-cfasync="false" src="assets/js/jarvis-effects.js"></script>
<script data-cfasync="false" src="assets/js/jarvis-overlays.js"></script>
<script data-cfasync="false" src="assets/js/jarvis-app.js"></script>
<script data-cfasync="false" src="assets/js/jarvis-protocols.js"></script>
<!-- VISION LIGHTBOX -->
<div id="vision-lightbox">
<div id="vision-lb-header">
<span id="vision-lb-title">◈ VISION PROTOCOL</span>
<button id="vision-lb-close" onclick="closeVisionLightbox()">✕ CLOSE</button>
</div>
<div id="vision-lb-spinner">● SCANNING...</div>
<img id="vision-lb-img" alt="Agent Screenshot" style="display:none">
<pre id="vision-lb-analysis"></pre>
</div>
<!-- CHAT HISTORY SEARCH MODAL -->
<div id="searchModal" style="display:none;position:fixed;inset:0;z-index:200;background:rgba(0,0,0,0.7);backdrop-filter:blur(4px);align-items:center;justify-content:center">
<div style="background:#000d1f;border:1px solid var(--panel-border);border-radius:6px;padding:20px;width:min(620px,94vw);max-height:80vh;display:flex;flex-direction:column;gap:12px">
<div style="display:flex;align-items:center;justify-content:space-between">
<div style="font-family:var(--font-display);font-size:0.75rem;letter-spacing:3px;color:var(--cyan)">◈ CHAT HISTORY SEARCH</div>
<button onclick="closeSearchModal()" style="background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:1rem"></button>
</div>
<div style="display:flex;gap:8px">
<input id="searchInput" type="text" placeholder="Search conversations…"
style="flex:1;background:rgba(0,212,255,0.05);border:1px solid var(--panel-border);color:var(--text-primary);font-family:var(--font-mono);font-size:0.75rem;padding:8px 12px;outline:none"
onkeydown="if(event.key==='Enter')runSearch()" autocomplete="off"/>
<button onclick="runSearch()" style="background:rgba(0,212,255,0.1);border:1px solid rgba(0,212,255,0.3);color:var(--cyan);font-family:var(--font-display);font-size:0.6rem;letter-spacing:2px;padding:8px 16px;cursor:pointer">SEARCH</button>
</div>
<div id="searchResults" style="overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:6px;min-height:60px">
<div style="color:var(--text-dim);font-size:0.65rem;text-align:center;padding:20px">Type to search your JARVIS conversations</div>
</div>
</div>
</div>
<nav id="mobileNav">
<button class="mob-nav-btn active" id="mob-btn-left" onclick="mobSwitch('left')">
<span class="mob-icon">📊</span>STATS
</button>
<button class="mob-nav-btn" id="mob-btn-center" onclick="mobSwitch('center')">
<span class="mob-icon">💬</span>CHAT
</button>
<button class="mob-nav-btn" id="mob-btn-right" onclick="mobSwitch('right')">
<span class="mob-icon">🛰</span>INFO
</button>
</nav>
</body>
</html>