'use client';

import { useState, useEffect, useCallback, useRef } from 'react';
import {
  Cpu,
  HardDrive,
  MemoryStick,
  WifiOff,
  Mic,
  Play,
  Square,
  RefreshCw,
  Zap,
  Brain,
  Server,
  Monitor,
  Clock,
  ChevronDown,
  ChevronUp,
  Activity,
  Download,
  Trash2,
  Copy,
  Check,
  X,
  Plus,
  Send,
  Terminal,
  Search,
  History,
  Keyboard,
  FileText,
  Sun,
  Moon,
  Tag,
  Star,
  MessageSquare,
  Settings,
  Volume2,
} from 'lucide-react';
import type {
  OllamaData,
  WhisperData,
  SystemData,
  Toast,
  PullProgress,
  StreamMetrics,
} from '../../lib/types';
import {
  formatBytes,
  formatUptime,
  estimateRam,
  checkMemoryFit,
  getModelBadges,
} from '../../lib/format';
import { StatusDot } from '../../components/StatusDot';
import { ProgressBar } from '../../components/ProgressBar';
import { Sparkline } from '../../components/Sparkline';
import { RamBudgetBar } from './components/RamBudgetBar';
import { MarkdownResponse } from './components/MarkdownResponse';
import { MemoryDrilldown } from './components/MemoryDrilldown';

export default function Dashboard() {
  const [ollama, setOllama] = useState<OllamaData | null>(null);
  const [whisper, setWhisper] = useState<WhisperData | null>(null);
  const [system, setSystem] = useState<SystemData | null>(null);
  const [loading, setLoading] = useState(true);
  const [actionLoading, setActionLoading] = useState<string | null>(null);
  const [expandedModel, setExpandedModel] = useState<string | null>(null);
  const [lastRefresh, setLastRefresh] = useState<Date>(new Date());
  const [promptModel, setPromptModel] = useState<string | null>(null);
  const [promptText, setPromptText] = useState('');
  const [promptResponse, setPromptResponse] = useState('');
  const [promptLoading, setPromptLoading] = useState(false);
  const [toasts, setToasts] = useState<Toast[]>([]);
  const [pullInput, setPullInput] = useState('');
  const [pullLoading, setPullLoading] = useState(false);
  const [pullProgress, setPullProgress] = useState<PullProgress | null>(null);
  const [copied, setCopied] = useState(false);
  const [streamMetrics, setStreamMetrics] = useState<StreamMetrics | null>(null);
  const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);
  const [modelSearch, setModelSearch] = useState('');
  const [showHistory, setShowHistory] = useState(false);
  const [showShortcuts, setShowShortcuts] = useState(false);
  const [modelfileData, setModelfileData] = useState<Record<string, string>>({});
  const [theme, setTheme] = useState<'dark' | 'light'>('dark');
  const [modelTags, setModelTags] = useState<Record<string, string[]>>({});
  const [autoLoadModel, setAutoLoadModel] = useState<string | null>(null);
  const [chatMode, setChatMode] = useState(false);
  const [chatMessages, setChatMessages] = useState<
    Array<{ role: 'user' | 'assistant'; content: string }>
  >([]);
  const [systemPrompt, setSystemPrompt] = useState('');
  const [memoryHistory, setMemoryHistory] = useState<number[]>([]);
  const [extractionHealth, setExtractionHealth] = useState<{
    status: string;
    service?: string;
  } | null>(null);
  const [ollamaLogs, setOllamaLogs] = useState<string[]>([]);
  const [showLogs, setShowLogs] = useState(false);
  const [whisperTestResult, setWhisperTestResult] = useState<{
    text: string;
    latencyMs: number;
  } | null>(null);
  const [whisperTestLoading, setWhisperTestLoading] = useState(false);
  const [compareModel, setCompareModel] = useState<string | null>(null);
  const [compareResponse, setCompareResponse] = useState('');
  const [modelMetadata, setModelMetadata] = useState<Record<string, { contextLength?: number }>>(
    {}
  );
  const [modelSort, setModelSort] = useState<'name' | 'size' | 'params' | 'running' | 'modified'>(
    'name'
  );
  const [modelBenchmarks, setModelBenchmarks] = useState<
    Record<string, { tokPerSec: number; totalTokens: number; timestamp: number }>
  >({});
  const [sessionStats, setSessionStats] = useState<
    Record<string, { prompts: number; tokens: number }>
  >({});
  const [countdownTick, setCountdownTick] = useState(0);
  const [visionImages, setVisionImages] = useState<string[]>([]);
  const [showSettings, setShowSettings] = useState(false);
  const [inferenceLog, setInferenceLog] = useState<
    Array<{
      model: string;
      prompt: string;
      response: string;
      tokPerSec?: number;
      timestamp: number;
    }>
  >([]);
  const [showInferenceLog, setShowInferenceLog] = useState(false);
  const [inferenceSearch, setInferenceSearch] = useState('');
  const [showMemoryDrilldown, setShowMemoryDrilldown] = useState(false);
  const [ttsData, setTtsData] = useState<{
    engines: Array<{
      name: string;
      type: 'ollama' | 'python';
      status: 'ready' | 'partial' | 'missing';
      model: string;
      size?: string;
      voices?: string[];
      details: string;
    }>;
    venv: { exists: boolean; packages?: string[] };
  } | null>(null);
  const responseRef = useRef<HTMLDivElement>(null);
  const abortRef = useRef<AbortController | null>(null);
  const compareAbortRef = useRef<AbortController | null>(null);
  const fetchingRef = useRef(false);

  const addToast = useCallback((message: string, type: Toast['type'] = 'info') => {
    const id = Date.now() + Math.random();
    setToasts(prev => [...prev, { id, message, type }]);
    setTimeout(() => setToasts(prev => prev.filter(t => t.id !== id)), 4000);
  }, []);

  const fetchAll = useCallback(async () => {
    if (fetchingRef.current) return;
    fetchingRef.current = true;
    setLoading(true);
    const [oRes, wRes, sRes] = await Promise.allSettled([
      fetch('/api/ollama').then(r => r.json()),
      fetch('/api/whisper').then(r => r.json()),
      fetch('/api/system').then(r => r.json()),
    ]);
    if (oRes.status === 'fulfilled') setOllama(oRes.value);
    if (wRes.status === 'fulfilled') setWhisper(wRes.value);
    if (sRes.status === 'fulfilled') {
      setSystem(sRes.value);
      // F7: Record memory snapshot for sparkline (max 30 points = ~7.5 min at 15s)
      if (sRes.value?.memory?.appMemory) {
        setMemoryHistory(prev => [...prev.slice(-29), sRes.value.memory.appMemory]);
      }
    }
    // TTS engine status
    try {
      const tRes = await fetch('/api/tts');
      if (tRes.ok) setTtsData(await tRes.json());
    } catch {
      /* ignore */
    }
    // F15: Check extraction service health via server-side proxy (avoids browser CORS/console errors)
    try {
      const eRes = await fetch('/api/extraction/health');
      if (eRes.ok) setExtractionHealth(await eRes.json());
      else setExtractionHealth({ status: 'error' });
    } catch {
      setExtractionHealth({ status: 'offline' });
    }
    setLastRefresh(new Date());
    setLoading(false);
    fetchingRef.current = false;
  }, []);

  // Restore persisted state from localStorage (F10, F14, F16)
  useEffect(() => {
    const savedTheme = localStorage.getItem('llm-theme') as 'dark' | 'light' | null;
    if (savedTheme) {
      setTheme(savedTheme);
      document.documentElement.classList.toggle('light', savedTheme === 'light');
    }
    try {
      setModelTags(JSON.parse(localStorage.getItem('llm-model-tags') || '{}'));
    } catch {
      /* ignore */
    }
    setAutoLoadModel(localStorage.getItem('llm-auto-load-model'));
    const savedSort = localStorage.getItem('llm-model-sort');
    if (savedSort) setModelSort(savedSort as typeof modelSort);
    try {
      const savedBench = localStorage.getItem('llm-model-benchmarks');
      if (savedBench) setModelBenchmarks(JSON.parse(savedBench));
    } catch {
      /* ignore */
    }
    try {
      const savedLog = localStorage.getItem('llm-inference-log');
      if (savedLog) setInferenceLog(JSON.parse(savedLog));
    } catch {
      /* ignore */
    }
  }, []);

  useEffect(() => {
    fetchAll();
  }, [fetchAll]);

  // BN3: Save chat messages to localStorage when they change
  useEffect(() => {
    if (promptModel && chatMode && chatMessages.length > 0) {
      localStorage.setItem(`llm-chat-${promptModel}`, JSON.stringify(chatMessages.slice(-50)));
    }
  }, [chatMessages, promptModel, chatMode]);

  // BN3: Restore chat messages when prompt modal opens
  useEffect(() => {
    if (promptModel && chatMode) {
      try {
        const saved = localStorage.getItem(`llm-chat-${promptModel}`);
        if (saved) setChatMessages(JSON.parse(saved));
      } catch {
        /* ignore */
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [promptModel, chatMode]);

  // N12: Countdown tick — update every second for live unload countdown
  useEffect(() => {
    if (!ollama || ollama.running.length === 0) return;
    const interval = setInterval(() => setCountdownTick(t => t + 1), 1000);
    return () => clearInterval(interval);
  }, [ollama?.running.length]);

  // F16: Auto-load preferred model when Ollama is online but nothing loaded
  useEffect(() => {
    if (!autoLoadModel || !ollama || ollama.status !== 'online') return;
    if (ollama.runningCount > 0) return;
    if (!ollama.models.some(m => m.name === autoLoadModel)) return;
    handleModelAction('load', autoLoadModel);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [ollama?.status, ollama?.runningCount, autoLoadModel]);

  useEffect(() => {
    if (promptLoading || pullLoading) return;
    const interval = setInterval(fetchAll, 15000);
    return () => clearInterval(interval);
  }, [fetchAll, promptLoading, pullLoading]);

  // Prompt history helpers (F3)
  const getPromptHistory = (): Array<{ prompt: string; model: string; ts: number }> => {
    try {
      return JSON.parse(localStorage.getItem('llm-prompt-history') || '[]');
    } catch {
      return [];
    }
  };
  const saveToHistory = (prompt: string, model: string) => {
    const history = getPromptHistory().filter(h => h.prompt !== prompt || h.model !== model);
    history.unshift({ prompt, model, ts: Date.now() });
    localStorage.setItem('llm-prompt-history', JSON.stringify(history.slice(0, 20)));
  };

  // Theme toggle (F10)
  const toggleTheme = () => {
    const next = theme === 'dark' ? 'light' : 'dark';
    setTheme(next);
    document.documentElement.classList.toggle('light', next === 'light');
    localStorage.setItem('llm-theme', next);
  };

  // Model tags helpers (F14)
  const toggleTag = (modelName: string, tag: string) => {
    setModelTags(prev => {
      const current = prev[modelName] || [];
      const next = current.includes(tag) ? current.filter(t => t !== tag) : [...current, tag];
      const updated = { ...prev, [modelName]: next };
      localStorage.setItem('llm-model-tags', JSON.stringify(updated));
      return updated;
    });
  };

  // Fetch Ollama logs (F8)
  const fetchLogs = async () => {
    try {
      const res = await fetch('/api/ollama/logs');
      const data = await res.json();
      if (data.lines) setOllamaLogs(data.lines);
    } catch {
      /* ignore */
    }
  };

  // Whisper transcription test (F12)
  const handleWhisperTest = async (file: File) => {
    setWhisperTestLoading(true);
    setWhisperTestResult(null);
    try {
      const formData = new FormData();
      formData.append('audio', file);
      if (whisper?.models?.[0]?.name) formData.append('model', whisper.models[0].name);
      const res = await fetch('/api/whisper/transcribe', { method: 'POST', body: formData });
      const data = await res.json();
      if (data.text) setWhisperTestResult({ text: data.text, latencyMs: data.latencyMs });
      else addToast(data.error || 'Transcription failed', 'error');
    } catch (err) {
      addToast(`Whisper test failed: ${err}`, 'error');
    }
    setWhisperTestLoading(false);
  };

  // Model comparison (F5) — send same prompt to second model
  // BN2: Added AbortController so compare stream cancels on modal close
  const handleCompare = async (prompt: string, model2: string) => {
    setCompareModel(model2);
    setCompareResponse('');
    const controller = new AbortController();
    compareAbortRef.current = controller;
    try {
      const res = await fetch('/api/ollama/stream', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ model: model2, prompt }),
        signal: controller.signal,
      });
      if (!res.ok || !res.body) {
        setCompareResponse('Error');
        return;
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      let full = '';
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const c = JSON.parse(line);
            if (c.response) {
              full += c.response;
              setCompareResponse(full);
            }
          } catch {
            /* skip */
          }
        }
      }
      if (!full) setCompareResponse('(empty response)');
    } catch (err) {
      if (!controller.signal.aborted) {
        setCompareResponse(`Error: ${err}`);
      }
    }
    compareAbortRef.current = null;
  };

  // Auto-load model helpers (F16)
  const setPreferredModel = (modelName: string | null) => {
    setAutoLoadModel(modelName);
    if (modelName) localStorage.setItem('llm-auto-load-model', modelName);
    else localStorage.removeItem('llm-auto-load-model');
  };

  // Fetch modelfile + metadata for expanded model (F9 + N5)
  const fetchModelfile = async (modelName: string) => {
    if (modelfileData[modelName]) return;
    try {
      const res = await fetch('/api/ollama', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'show', model: modelName }),
      });
      const data = await res.json();
      if (data.modelfile) {
        setModelfileData(prev => ({ ...prev, [modelName]: data.modelfile }));
      }
      // N5: Extract context_length from model_info
      if (data.model_info) {
        const ctxKey = Object.keys(data.model_info).find(k => k.endsWith('.context_length'));
        const contextLength = ctxKey ? data.model_info[ctxKey] : undefined;
        if (contextLength) {
          setModelMetadata(prev => ({ ...prev, [modelName]: { contextLength } }));
        }
      }
    } catch {
      /* ignore */
    }
  };

  // Keyboard shortcuts (F11) + Escape handler
  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      const target = e.target as HTMLElement;
      const inInput = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA';

      if (e.key === 'Escape') {
        if (showShortcuts) {
          setShowShortcuts(false);
          return;
        }
        if (promptLoading) {
          abortRef.current?.abort();
          setPromptLoading(false);
        } else {
          setPromptModel(null);
          setPromptResponse('');
          setPromptText('');
        }
        setDeleteConfirm(null);
        return;
      }
      if (inInput) return;
      if (e.key === '?') {
        setShowShortcuts(s => !s);
        return;
      }
      if (e.key === 'r' || e.key === 'R') {
        fetchAll();
        return;
      }
      if (e.key === '/') {
        e.preventDefault();
        setModelSearch('');
        document.querySelector<HTMLInputElement>('[data-model-search]')?.focus();
        return;
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [promptLoading, showShortcuts, fetchAll]);

  const handleModelAction = async (action: string, model: string) => {
    setActionLoading(`${action}-${model}`);
    try {
      const res = await fetch('/api/ollama', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action, model }),
      });
      const data = await res.json();
      if (data.success) {
        addToast(data.message || `${action} ${model} succeeded`, 'success');
      } else {
        addToast(data.error || `${action} failed`, 'error');
      }
      await fetchAll();
    } catch (err) {
      addToast(`${action} failed: ${err}`, 'error');
    }
    setActionLoading(null);
    setDeleteConfirm(null);
  };

  const handlePull = async () => {
    if (!pullInput.trim()) return;
    const modelName = pullInput.trim();
    setPullLoading(true);
    setPullProgress({ status: 'starting', completed: 0, total: 0 });
    try {
      const res = await fetch('/api/ollama/pull', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ model: modelName }),
      });
      if (!res.ok || !res.body) {
        addToast(`Pull failed: ${res.status}`, 'error');
        setPullLoading(false);
        setPullProgress(null);
        return;
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const chunk = JSON.parse(line);
            if (chunk.total && chunk.completed) {
              setPullProgress({
                status: chunk.status || 'downloading',
                completed: chunk.completed,
                total: chunk.total,
              });
            } else if (chunk.status) {
              setPullProgress(prev => ({
                status: chunk.status,
                completed: prev?.completed || 0,
                total: prev?.total || 0,
              }));
            }
          } catch {
            /* skip */
          }
        }
      }
      addToast(`Successfully pulled ${modelName}`, 'success');
      setPullInput('');
      await fetchAll();
    } catch (err) {
      addToast(`Pull failed: ${err}`, 'error');
    }
    setPullLoading(false);
    setPullProgress(null);
  };

  // Streaming prompt
  const handlePrompt = async () => {
    if (!promptModel || !promptText.trim()) return;
    saveToHistory(promptText.trim(), promptModel);
    setPromptLoading(true);
    setPromptResponse('');
    setStreamMetrics(null);
    const controller = new AbortController();
    abortRef.current = controller;
    try {
      const res = await fetch('/api/ollama/stream', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          model: promptModel,
          prompt: promptText,
          ...(visionImages.length > 0 ? { images: visionImages } : {}),
        }),
        signal: controller.signal,
      });
      if (!res.ok || !res.body) {
        setPromptResponse('Error: Failed to connect to Ollama');
        setPromptLoading(false);
        return;
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      let fullResponse = '';
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const chunk = JSON.parse(line);
            if (chunk.response) {
              fullResponse += chunk.response;
              setPromptResponse(fullResponse);
              responseRef.current?.scrollTo(0, responseRef.current.scrollHeight);
            }
            if (chunk.done && chunk.eval_count && chunk.eval_duration) {
              const durationMs = chunk.eval_duration / 1e6;
              const tokensPerSec = durationMs > 0 ? (chunk.eval_count / durationMs) * 1000 : 0;
              setStreamMetrics({ tokensPerSec, totalTokens: chunk.eval_count, durationMs });
              // N11: Persist benchmark
              if (promptModel) {
                const updated = {
                  ...modelBenchmarks,
                  [promptModel]: {
                    tokPerSec: tokensPerSec,
                    totalTokens: chunk.eval_count,
                    timestamp: Date.now(),
                  },
                };
                setModelBenchmarks(updated);
                localStorage.setItem('llm-model-benchmarks', JSON.stringify(updated));
              }
              // N13: Update session stats
              if (promptModel) {
                setSessionStats(prev => {
                  const cur = prev[promptModel] || { prompts: 0, tokens: 0 };
                  return {
                    ...prev,
                    [promptModel]: {
                      prompts: cur.prompts + 1,
                      tokens: cur.tokens + chunk.eval_count,
                    },
                  };
                });
              }
            }
          } catch {
            /* skip malformed lines */
          }
        }
      }
      if (!fullResponse) setPromptResponse('(empty response)');
      // F30: Save to inference log
      if (promptModel && fullResponse) {
        setInferenceLog(prev => {
          const entry = {
            model: promptModel,
            prompt: promptText.trim(),
            response: fullResponse,
            tokPerSec: streamMetrics?.tokensPerSec,
            timestamp: Date.now(),
          };
          const updated = [entry, ...prev].slice(0, 100);
          localStorage.setItem('llm-inference-log', JSON.stringify(updated));
          return updated;
        });
      }
    } catch (err) {
      if (controller.signal.aborted) {
        // User cancelled — keep partial response
      } else {
        setPromptResponse(`Error: ${err}`);
      }
    }
    abortRef.current = null;
    setPromptLoading(false);
  };

  const copyResponse = () => {
    navigator.clipboard.writeText(promptResponse);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  // F29: Export all llm-* localStorage keys as JSON
  const exportSettings = () => {
    const data: Record<string, string> = {};
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key?.startsWith('llm-')) {
        data[key] = localStorage.getItem(key) || '';
      }
    }
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `llm-dashboard-settings-${new Date().toISOString().slice(0, 10)}.json`;
    a.click();
    URL.revokeObjectURL(url);
    addToast('Settings exported', 'success');
  };

  // F29: Import settings from JSON file
  const importSettings = (file: File) => {
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const data = JSON.parse(reader.result as string);
        if (typeof data !== 'object' || data === null) throw new Error('Invalid format');
        let count = 0;
        for (const [key, value] of Object.entries(data)) {
          if (key.startsWith('llm-') && typeof value === 'string') {
            localStorage.setItem(key, value);
            count++;
          }
        }
        addToast(`Imported ${count} settings — refresh to apply`, 'success');
      } catch {
        addToast('Invalid settings file', 'error');
      }
    };
    reader.readAsText(file);
  };

  // F31: Clear all llm-* localStorage keys
  const factoryReset = () => {
    const keysToRemove: string[] = [];
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key?.startsWith('llm-')) keysToRemove.push(key);
    }
    keysToRemove.forEach(k => localStorage.removeItem(k));
    setModelTags({});
    setAutoLoadModel(null);
    setModelBenchmarks({});
    setInferenceLog([]);
    setModelSort('name');
    setChatMessages([]);
    setTheme('dark');
    document.documentElement.classList.remove('light');
    addToast(`Cleared ${keysToRemove.length} settings`, 'success');
    setShowSettings(false);
  };

  // Chat mode streaming (F4)
  const handleChat = async () => {
    if (!promptModel || !promptText.trim()) return;
    saveToHistory(promptText.trim(), promptModel);
    const userMsg = { role: 'user' as const, content: promptText.trim() };
    setChatMessages(prev => [...prev, userMsg]);
    setPromptText('');
    setPromptLoading(true);
    setStreamMetrics(null);
    const controller = new AbortController();
    abortRef.current = controller;
    const allMessages = [
      ...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),
      ...chatMessages,
      userMsg,
    ];
    let assistantContent = '';
    try {
      const res = await fetch('/api/ollama/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ model: promptModel, messages: allMessages }),
        signal: controller.signal,
      });
      if (!res.ok || !res.body) {
        setChatMessages(prev => [
          ...prev,
          { role: 'assistant', content: 'Error: Failed to connect' },
        ]);
        setPromptLoading(false);
        return;
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';
        for (const line of lines) {
          if (!line.trim()) continue;
          try {
            const chunk = JSON.parse(line);
            if (chunk.message?.content) {
              assistantContent += chunk.message.content;
              setChatMessages(prev => {
                const updated = [...prev];
                const last = updated[updated.length - 1];
                if (last?.role === 'assistant') {
                  updated[updated.length - 1] = { ...last, content: assistantContent };
                } else {
                  updated.push({ role: 'assistant', content: assistantContent });
                }
                return updated;
              });
              responseRef.current?.scrollTo(0, responseRef.current.scrollHeight);
            }
            if (chunk.done && chunk.eval_count && chunk.eval_duration) {
              const durationMs = chunk.eval_duration / 1e6;
              const tokensPerSec = durationMs > 0 ? (chunk.eval_count / durationMs) * 1000 : 0;
              setStreamMetrics({ tokensPerSec, totalTokens: chunk.eval_count, durationMs });
              // N11: Persist benchmark
              if (promptModel) {
                const updated = {
                  ...modelBenchmarks,
                  [promptModel]: {
                    tokPerSec: tokensPerSec,
                    totalTokens: chunk.eval_count,
                    timestamp: Date.now(),
                  },
                };
                setModelBenchmarks(updated);
                localStorage.setItem('llm-model-benchmarks', JSON.stringify(updated));
              }
              // N13: Update session stats
              if (promptModel) {
                setSessionStats(prev => {
                  const cur = prev[promptModel] || { prompts: 0, tokens: 0 };
                  return {
                    ...prev,
                    [promptModel]: {
                      prompts: cur.prompts + 1,
                      tokens: cur.tokens + chunk.eval_count,
                    },
                  };
                });
              }
            }
          } catch {
            /* skip */
          }
        }
      }
    } catch (err) {
      if (!controller.signal.aborted) {
        setChatMessages(prev => [...prev, { role: 'assistant', content: `Error: ${err}` }]);
      }
    }
    abortRef.current = null;
    setPromptLoading(false);
  };

  const handleSend = chatMode ? handleChat : handlePrompt;

  const isRunning = (name: string) => ollama?.running.some(r => r.name === name);
  const getRunningInfo = (name: string) => ollama?.running.find(r => r.name === name);

  // S3: This dashboard is local-only (localhost:3000). No CORS or auth is implemented.
  // All API routes are accessible to any local process. This is acceptable for a dev tool.

  return (
    <div className="min-h-screen p-3 sm:p-6 max-w-[1600px] mx-auto">
      {/* Header */}
      <header className="flex items-center justify-between mb-8">
        <div className="flex items-center gap-4">
          <div
            className="w-10 h-10 rounded-xl flex items-center justify-center"
            style={{ background: 'var(--accent-primary)' }}
          >
            <Brain className="w-6 h-6 text-white" />
          </div>
          <div>
            <h1 className="text-2xl font-bold" style={{ color: 'var(--text-primary)' }}>
              Local LLM Mission Control
            </h1>
            <p className="text-sm" style={{ color: 'var(--text-tertiary)' }}>
              {system?.chip || 'Loading...'} &middot; {formatBytes(system?.memory.total || 0)}{' '}
              &middot; {system?.platform || 'macOS'}
            </p>
          </div>
        </div>
        <div className="flex items-center gap-3">
          <span className="text-xs hidden sm:inline" style={{ color: 'var(--text-tertiary)' }}>
            Last refresh: {lastRefresh.toLocaleTimeString()}
          </span>
          {/* F30: Inference log toggle */}
          <button
            onClick={() => setShowInferenceLog(s => !s)}
            className="p-2 rounded-lg transition-colors relative"
            style={{
              background: showInferenceLog ? 'var(--accent-primary)' : 'var(--surface-card)',
              border: '1px solid var(--border-subtle)',
              color: showInferenceLog ? 'white' : 'var(--text-secondary)',
            }}
            title="Inference history"
          >
            <FileText className="w-4 h-4" />
            {inferenceLog.length > 0 && (
              <span
                className="absolute -top-1 -right-1 w-4 h-4 rounded-full text-[9px] flex items-center justify-center"
                style={{ background: 'var(--accent-secondary)', color: 'var(--bg-canvas)' }}
              >
                {inferenceLog.length > 99 ? '99' : inferenceLog.length}
              </span>
            )}
          </button>
          {/* F29/F31: Settings popover toggle */}
          <div className="relative">
            <button
              onClick={() => setShowSettings(s => !s)}
              className="p-2 rounded-lg transition-colors"
              style={{
                background: showSettings ? 'var(--accent-primary)' : 'var(--surface-card)',
                border: '1px solid var(--border-subtle)',
                color: showSettings ? 'white' : 'var(--text-secondary)',
              }}
              title="Dashboard settings"
            >
              <Settings className="w-4 h-4" />
            </button>
            {showSettings && (
              <div
                className="absolute right-0 top-12 w-60 p-4 rounded-lg shadow-xl z-50 space-y-3"
                style={{
                  background: 'var(--bg-elevated)',
                  border: '1px solid var(--border-default)',
                }}
              >
                <h3 className="text-sm font-semibold" style={{ color: 'var(--text-primary)' }}>
                  Settings
                </h3>
                <button
                  onClick={exportSettings}
                  className="w-full text-left text-xs px-3 py-2 rounded transition-colors"
                  style={{ background: 'var(--surface-muted)', color: 'var(--text-secondary)' }}
                >
                  Export settings (JSON)
                </button>
                <label
                  className="block w-full text-left text-xs px-3 py-2 rounded transition-colors cursor-pointer"
                  style={{ background: 'var(--surface-muted)', color: 'var(--text-secondary)' }}
                >
                  Import settings...
                  <input
                    type="file"
                    accept=".json"
                    className="hidden"
                    onChange={e => {
                      const f = e.target.files?.[0];
                      if (f) importSettings(f);
                      e.target.value = '';
                      setShowSettings(false);
                    }}
                  />
                </label>
                <hr style={{ borderColor: 'var(--border-subtle)' }} />
                <button
                  onClick={() => {
                    if (
                      confirm(
                        'This will clear all tags, history, benchmarks, and preferences. Continue?'
                      )
                    ) {
                      factoryReset();
                    }
                  }}
                  className="w-full text-left text-xs px-3 py-2 rounded transition-colors"
                  style={{ background: 'rgba(255,110,110,0.1)', color: 'var(--danger)' }}
                >
                  Reset all dashboard data
                </button>
              </div>
            )}
          </div>
          <button
            onClick={toggleTheme}
            className="p-2 rounded-lg transition-colors"
            style={{
              background: 'var(--surface-card)',
              border: '1px solid var(--border-subtle)',
              color: 'var(--text-secondary)',
            }}
            title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`}
          >
            {theme === 'dark' ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
          </button>
          <button
            onClick={fetchAll}
            disabled={loading}
            className="flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors"
            style={{
              background: 'var(--surface-card)',
              border: '1px solid var(--border-subtle)',
              color: 'var(--text-secondary)',
            }}
          >
            <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
            <span className="hidden sm:inline">Refresh</span>
          </button>
        </div>
      </header>

      {/* Toast Notifications */}
      <div className="fixed top-4 right-4 z-50 space-y-2">
        {toasts.map(toast => (
          <div
            key={toast.id}
            className="flex items-center gap-2 px-4 py-3 rounded-lg text-sm font-medium shadow-lg animate-in slide-in-from-right"
            style={{
              background:
                toast.type === 'success'
                  ? 'var(--success)'
                  : toast.type === 'error'
                    ? 'var(--danger)'
                    : 'var(--accent-primary)',
              color: 'white',
              minWidth: '260px',
            }}
          >
            {toast.type === 'success' ? (
              <Check className="w-4 h-4 shrink-0" />
            ) : toast.type === 'error' ? (
              <X className="w-4 h-4 shrink-0" />
            ) : (
              <Terminal className="w-4 h-4 shrink-0" />
            )}
            <span className="truncate">{toast.message}</span>
          </div>
        ))}
      </div>

      {/* F30: Inference History Log Panel */}
      {showInferenceLog && (
        <div
          className="mb-6 card p-4"
          style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border-default)' }}
        >
          <div className="flex items-center justify-between mb-3">
            <h3
              className="text-sm font-semibold flex items-center gap-2"
              style={{ color: 'var(--text-primary)' }}
            >
              <FileText className="w-4 h-4" style={{ color: 'var(--accent-primary)' }} />
              Inference History ({inferenceLog.length})
            </h3>
            <div className="flex items-center gap-2">
              <input
                type="text"
                value={inferenceSearch}
                onChange={e => setInferenceSearch(e.target.value)}
                placeholder="Search..."
                className="px-2 py-1 rounded text-xs font-mono focus:outline-none"
                style={{
                  background: 'var(--surface-muted)',
                  border: '1px solid var(--border-subtle)',
                  color: 'var(--text-primary)',
                  width: '160px',
                }}
              />
              <button
                onClick={() => setShowInferenceLog(false)}
                className="p-1 rounded"
                style={{ color: 'var(--text-tertiary)' }}
              >
                <X className="w-4 h-4" />
              </button>
            </div>
          </div>
          <div className="max-h-60 overflow-y-auto space-y-2">
            {inferenceLog
              .filter(
                e =>
                  !inferenceSearch ||
                  e.prompt.toLowerCase().includes(inferenceSearch.toLowerCase()) ||
                  e.model.toLowerCase().includes(inferenceSearch.toLowerCase())
              )
              .map((entry, i) => (
                <div
                  key={i}
                  className="p-2 rounded text-xs"
                  style={{ background: 'var(--surface-muted)' }}
                >
                  <div className="flex items-center justify-between mb-1">
                    <span
                      className="font-mono font-medium"
                      style={{ color: 'var(--accent-primary)' }}
                    >
                      {entry.model}
                    </span>
                    <span style={{ color: 'var(--text-tertiary)' }}>
                      {entry.tokPerSec ? `${entry.tokPerSec.toFixed(1)} tok/s · ` : ''}
                      {new Date(entry.timestamp).toLocaleString()}
                    </span>
                  </div>
                  <p
                    className="truncate"
                    style={{ color: 'var(--text-secondary)' }}
                    title={entry.prompt}
                  >
                    <strong>Q:</strong> {entry.prompt}
                  </p>
                  <p
                    className="truncate mt-0.5"
                    style={{ color: 'var(--text-tertiary)' }}
                    title={entry.response}
                  >
                    <strong>A:</strong> {entry.response.slice(0, 200)}
                  </p>
                  <button
                    onClick={() => {
                      setPromptModel(entry.model);
                      setPromptText(entry.prompt);
                      setShowInferenceLog(false);
                    }}
                    className="text-[10px] mt-1 px-2 py-0.5 rounded transition-colors"
                    style={{ background: 'var(--surface-card)', color: 'var(--accent-secondary)' }}
                  >
                    Replay
                  </button>
                </div>
              ))}
            {inferenceLog.length === 0 && (
              <p className="text-center py-4 text-xs" style={{ color: 'var(--text-tertiary)' }}>
                No inference history yet. Send a prompt to start recording.
              </p>
            )}
          </div>
        </div>
      )}

      {/* Top Stats Row (CQ5: skeleton loading when data not yet loaded) */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3 sm:gap-4 mb-6">
        {loading && !ollama ? (
          <>
            {[1, 2, 3, 4].map(i => (
              <div key={i} className="card p-4">
                <div className="skeleton h-3 w-16 mb-3" />
                <div className="skeleton h-6 w-24 mb-2" />
                <div className="skeleton h-2 w-32" />
              </div>
            ))}
          </>
        ) : (
          <>
            <div className="card p-4">
              <div className="flex items-center gap-2 mb-2">
                <Server className="w-4 h-4" style={{ color: 'var(--accent-primary)' }} />
                <span className="text-xs font-medium" style={{ color: 'var(--text-tertiary)' }}>
                  OLLAMA
                </span>
              </div>
              <div className="flex items-center gap-2">
                <StatusDot status={ollama?.status === 'online' ? 'online' : 'offline'} />
                <span className="text-lg font-bold">
                  {ollama?.status === 'online' ? 'Online' : 'Offline'}
                </span>
                {ollama?.version && (
                  <span
                    className="text-[10px] px-1.5 py-0.5 rounded font-mono"
                    style={{ background: 'var(--surface-muted)', color: 'var(--text-tertiary)' }}
                  >
                    v{ollama.version}
                  </span>
                )}
              </div>
              <p className="text-xs mt-1" style={{ color: 'var(--text-tertiary)' }}>
                {ollama?.url}
              </p>
            </div>

            <div className="card p-4">
              <div className="flex items-center gap-2 mb-2">
                <Download className="w-4 h-4" style={{ color: 'var(--accent-secondary)' }} />
                <span className="text-xs font-medium" style={{ color: 'var(--text-tertiary)' }}>
                  MODELS
                </span>
              </div>
              <span className="text-lg font-bold">{ollama?.totalModels || 0}</span>
              <span className="text-sm ml-2" style={{ color: 'var(--text-tertiary)' }}>
                ({formatBytes(ollama?.totalSize || 0)})
              </span>
              <p className="text-xs mt-1" style={{ color: 'var(--success)' }}>
                {ollama?.runningCount || 0} loaded in RAM
              </p>
            </div>

            <div className="card p-4">
              <div className="flex items-center gap-2 mb-2">
                <Mic className="w-4 h-4" style={{ color: 'var(--purple)' }} />
                <span className="text-xs font-medium" style={{ color: 'var(--text-tertiary)' }}>
                  WHISPER
                </span>
              </div>
              <div className="flex items-center gap-2">
                <StatusDot status={whisper?.installed ? 'online' : 'offline'} />
                <span className="text-lg font-bold">
                  {whisper?.installed ? 'Installed' : 'Not Found'}
                </span>
              </div>
              <p className="text-xs mt-1" style={{ color: 'var(--text-tertiary)' }}>
                {whisper?.models.length || 0} model(s)
              </p>
            </div>

            <div
              className="card p-4 cursor-pointer transition-all"
              onClick={() => setShowMemoryDrilldown(prev => !prev)}
              style={{
                outline: showMemoryDrilldown ? '2px solid var(--warning)' : 'none',
                outlineOffset: '-1px',
              }}
              title="Click to see memory drilldown"
            >
              <div className="flex items-center gap-2 mb-2">
                <MemoryStick className="w-4 h-4" style={{ color: 'var(--warning)' }} />
                <span className="text-xs font-medium" style={{ color: 'var(--text-tertiary)' }}>
                  MEMORY
                </span>
                <span className="text-[9px] ml-auto" style={{ color: 'var(--text-tertiary)' }}>
                  {showMemoryDrilldown ? '▲ hide' : '▼ drilldown'}
                </span>
              </div>
              <span className="text-lg font-bold">
                {formatBytes(system?.memory.appMemory || 0)}
              </span>
              <span className="text-sm ml-1" style={{ color: 'var(--text-tertiary)' }}>
                used / {formatBytes(system?.memory.total || 0)}
              </span>
              <p className="text-[10px] mt-0.5 font-medium" style={{ color: 'var(--success)' }}>
                {formatBytes((system?.memory.free || 0) + (system?.memory.cached || 0) * 0.9)}{' '}
                available for models
              </p>
              <div className="mt-2">
                <ProgressBar
                  value={system?.memory.appMemory || 0}
                  max={system?.memory.total || 1}
                  color={
                    system?.memory.pressure === 'critical'
                      ? 'var(--danger)'
                      : system?.memory.pressure === 'warning'
                        ? 'var(--warning)'
                        : 'var(--accent-primary)'
                  }
                />
              </div>
              {/* F7: Memory sparkline */}
              {memoryHistory.length > 1 && (
                <div className="mt-2">
                  <Sparkline
                    data={memoryHistory}
                    width={160}
                    height={24}
                    color="var(--warning)"
                    fillColor="var(--warning)"
                  />
                </div>
              )}
            </div>
          </>
        )}
      </div>

      {/* Memory Drilldown Panel */}
      {showMemoryDrilldown && (
        <div className="card p-6">
          <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
            <MemoryStick className="w-5 h-5" style={{ color: 'var(--warning)' }} />
            Memory Drilldown
          </h2>
          <MemoryDrilldown />
        </div>
      )}

      {/* Main Grid */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        {/* Ollama Models — 2 cols */}
        <div className="lg:col-span-2 card p-6">
          <div className="flex items-center justify-between mb-4">
            <h2 className="text-lg font-semibold flex items-center gap-2">
              <Brain className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
              Ollama Models
            </h2>
            {ollama?.status === 'online' && (
              <span
                className="text-xs px-2 py-1 rounded-full"
                style={{ background: 'var(--surface-muted)', color: 'var(--success)' }}
              >
                {ollama.runningCount} active
                {ollama.running.length > 0 &&
                  (() => {
                    const totalVram = ollama.running.reduce((sum, r) => sum + r.size_vram, 0);
                    return ` · ${formatBytes(totalVram)} VRAM`;
                  })()}
              </span>
            )}
          </div>

          {/* Model Search (F2) + Sort (N9) */}
          {ollama?.status === 'online' && ollama.models.length > 3 && (
            <div className="flex gap-2 mb-3">
              <div className="relative flex-1">
                <Search
                  className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4"
                  style={{ color: 'var(--text-tertiary)' }}
                />
                <input
                  data-model-search
                  type="text"
                  value={modelSearch}
                  onChange={e => setModelSearch(e.target.value)}
                  placeholder="Filter models... (press / to focus)"
                  className="w-full pl-9 pr-3 py-2 rounded-lg text-sm font-mono focus:outline-none focus:ring-2"
                  style={{
                    background: 'var(--surface-muted)',
                    border: '1px solid var(--border-subtle)',
                    color: 'var(--text-primary)',
                    caretColor: 'var(--accent-primary)',
                  }}
                />
              </div>
              <select
                value={modelSort}
                onChange={e => {
                  const v = e.target.value as typeof modelSort;
                  setModelSort(v);
                  localStorage.setItem('llm-model-sort', v);
                }}
                className="px-2 py-2 rounded-lg text-xs font-medium focus:outline-none focus:ring-2"
                style={{
                  background: 'var(--surface-muted)',
                  border: '1px solid var(--border-subtle)',
                  color: 'var(--text-secondary)',
                }}
                title="Sort models"
              >
                <option value="name">A–Z</option>
                <option value="size">Size</option>
                <option value="params">Params</option>
                <option value="running">Running</option>
                <option value="modified">Recent</option>
              </select>
            </div>
          )}

          {/* Pull Model Input + F28: Library Link */}
          {ollama?.status === 'online' && (
            <div className="flex gap-2 mb-4">
              <input
                type="text"
                value={pullInput}
                onChange={e => setPullInput(e.target.value)}
                onKeyDown={e => {
                  if (e.key === 'Enter') handlePull();
                }}
                placeholder="Pull a model... e.g. deepseek-r1:32b"
                className="flex-1 px-3 py-2 rounded-lg text-sm font-mono focus:outline-none focus:ring-2"
                style={{
                  background: 'var(--surface-muted)',
                  border: '1px solid var(--border-subtle)',
                  color: 'var(--text-primary)',
                  caretColor: 'var(--accent-primary)',
                }}
              />
              <button
                onClick={handlePull}
                disabled={pullLoading || !pullInput.trim()}
                className="flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-colors disabled:opacity-40"
                style={{ background: 'var(--accent-primary)', color: 'white' }}
              >
                {pullLoading ? (
                  <RefreshCw className="w-4 h-4 animate-spin" />
                ) : (
                  <Plus className="w-4 h-4" />
                )}
                Pull
              </button>
              <a
                href="https://ollama.com/library"
                target="_blank"
                rel="noopener noreferrer"
                className="flex items-center gap-1 px-3 py-2 rounded-lg text-xs font-medium transition-colors shrink-0"
                style={{ background: 'var(--surface-muted)', color: 'var(--text-tertiary)' }}
                title="Browse Ollama model library"
              >
                Library
              </a>
            </div>
          )}

          {/* Pull Progress Bar */}
          {pullProgress && (
            <div className="mb-4 p-3 rounded-lg" style={{ background: 'var(--surface-muted)' }}>
              <div className="flex items-center justify-between mb-1.5">
                <span className="text-xs font-medium" style={{ color: 'var(--text-secondary)' }}>
                  {pullProgress.status}
                </span>
                {pullProgress.total > 0 && (
                  <span className="text-xs font-mono" style={{ color: 'var(--text-tertiary)' }}>
                    {formatBytes(pullProgress.completed)} / {formatBytes(pullProgress.total)}
                    {' · '}
                    {Math.round((pullProgress.completed / pullProgress.total) * 100)}%
                  </span>
                )}
              </div>
              <ProgressBar
                value={pullProgress.completed}
                max={pullProgress.total || 1}
                color="var(--accent-primary)"
              />
            </div>
          )}

          {ollama?.status !== 'online' ? (
            <div
              className="flex items-center gap-3 p-4 rounded-lg"
              style={{ background: 'var(--surface-muted)' }}
            >
              <WifiOff className="w-5 h-5" style={{ color: 'var(--danger)' }} />
              <div>
                <p className="font-medium" style={{ color: 'var(--danger)' }}>
                  Ollama is offline
                </p>
                <p className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                  Run &quot;ollama serve&quot; to start
                </p>
              </div>
            </div>
          ) : (
            <div className="space-y-3">
              {/* N4: RAM Budget Bar */}
              {system && ollama.running.length > 0 && (
                <RamBudgetBar
                  totalRam={system.memory.total}
                  appMemory={system.memory.appMemory}
                  runningModels={ollama.running}
                  freeRam={system.memory.free + system.memory.cached}
                />
              )}
              {ollama.models
                .filter(
                  m =>
                    !modelSearch ||
                    m.name.toLowerCase().includes(modelSearch.toLowerCase()) ||
                    m.details?.family?.toLowerCase().includes(modelSearch.toLowerCase()) ||
                    m.details?.quantization_level?.toLowerCase().includes(modelSearch.toLowerCase())
                )
                .sort((a, b) => {
                  switch (modelSort) {
                    case 'size':
                      return b.size - a.size;
                    case 'params': {
                      const pa = parseFloat(a.details?.parameter_size || '0');
                      const pb = parseFloat(b.details?.parameter_size || '0');
                      return pb - pa;
                    }
                    case 'running': {
                      const ra = isRunning(a.name) ? 0 : 1;
                      const rb = isRunning(b.name) ? 0 : 1;
                      return ra !== rb ? ra - rb : a.name.localeCompare(b.name);
                    }
                    case 'modified':
                      return new Date(b.modified_at).getTime() - new Date(a.modified_at).getTime();
                    default:
                      return a.name.localeCompare(b.name);
                  }
                })
                .map(model => {
                  const running = isRunning(model.name);
                  const expanded = expandedModel === model.name;
                  const estRam = estimateRam(model.size, model.details?.quantization_level);
                  const badges = getModelBadges(model.name, model.details?.family);
                  const fitStatus = system
                    ? checkMemoryFit(estRam, system.memory.free, system.memory.cached)
                    : null;
                  return (
                    <div
                      key={model.name}
                      className="rounded-lg p-4 transition-colors"
                      style={{
                        background: running ? 'rgba(52, 211, 153, 0.05)' : 'var(--surface-muted)',
                        border: running
                          ? '1px solid rgba(52, 211, 153, 0.2)'
                          : '1px solid transparent',
                      }}
                    >
                      <div className="flex items-center justify-between">
                        <div className="flex items-center gap-3 flex-1 min-w-0">
                          <div
                            className={`w-8 h-8 rounded-lg flex items-center justify-center ${running ? 'bg-[rgba(52,211,153,0.15)]' : 'bg-[var(--surface-card)]'}`}
                          >
                            {running ? (
                              <Zap className="w-4 h-4" style={{ color: 'var(--success)' }} />
                            ) : (
                              <Brain
                                className="w-4 h-4"
                                style={{ color: 'var(--text-tertiary)' }}
                              />
                            )}
                          </div>
                          <div className="min-w-0 flex-1">
                            <div className="flex items-center gap-2">
                              <span className="font-mono text-sm font-semibold truncate">
                                {model.name}
                              </span>
                              {running && (
                                <span
                                  className="text-[10px] px-1.5 py-0.5 rounded font-medium"
                                  style={{
                                    background: 'rgba(52, 211, 153, 0.15)',
                                    color: 'var(--success)',
                                  }}
                                >
                                  LOADED
                                </span>
                              )}
                              {badges.map(b => (
                                <span
                                  key={b.label}
                                  className="text-[10px] px-1.5 py-0.5 rounded font-medium"
                                  style={{
                                    background: `color-mix(in srgb, ${b.color} 15%, transparent)`,
                                    color: b.color,
                                  }}
                                  title={b.tooltip}
                                >
                                  {b.label}
                                </span>
                              ))}
                              {model.details?.family && (
                                <span
                                  className="text-[10px] px-1.5 py-0.5 rounded font-mono"
                                  style={{
                                    background: 'var(--surface-muted)',
                                    color: 'var(--text-tertiary)',
                                  }}
                                >
                                  {model.details.family}
                                </span>
                              )}
                            </div>
                            {/* Metrics row */}
                            <div
                              className="flex items-center gap-2 text-xs mt-1 flex-wrap"
                              style={{ color: 'var(--text-tertiary)' }}
                            >
                              <span className="inline-flex items-center gap-1" title="Disk size">
                                <HardDrive className="w-3 h-3" />
                                {formatBytes(model.size)}
                              </span>
                              {model.details?.parameter_size && (
                                <span
                                  className="inline-flex items-center gap-1"
                                  title="Parameter count"
                                >
                                  <Cpu className="w-3 h-3" />
                                  {model.details.parameter_size}
                                </span>
                              )}
                              {model.details?.quantization_level && (
                                <span
                                  className="px-1.5 py-0.5 rounded font-mono text-[10px]"
                                  style={{
                                    background: 'var(--surface-card)',
                                    color: 'var(--text-tertiary)',
                                  }}
                                  title="Quantization level — lower bits = smaller & faster but less accurate"
                                >
                                  {model.details.quantization_level}
                                </span>
                              )}
                              {(() => {
                                const ctx = modelMetadata[model.name]?.contextLength;
                                return ctx ? (
                                  <span title="Context window size">
                                    {ctx >= 1024 ? `${Math.round(ctx / 1024)}k` : ctx} ctx
                                  </span>
                                ) : null;
                              })()}
                              {modelBenchmarks[model.name] && (
                                <span
                                  style={{ color: 'var(--accent-secondary)' }}
                                  title={`Last benchmarked: ${new Date(modelBenchmarks[model.name].timestamp).toLocaleString()}`}
                                >
                                  ~{modelBenchmarks[model.name].tokPerSec.toFixed(1)} tok/s
                                </span>
                              )}
                              {(() => {
                                const ps = parseFloat(model.details?.parameter_size || '0');
                                const tier =
                                  ps <= 3
                                    ? { label: 'Tiny · Instant', color: 'var(--success)' }
                                    : ps <= 8
                                      ? { label: 'Small · Fast', color: 'var(--accent-secondary)' }
                                      : ps <= 14
                                        ? { label: 'Medium', color: 'var(--accent-primary)' }
                                        : ps <= 34
                                          ? { label: 'Large · Slow', color: 'var(--warning)' }
                                          : { label: 'XL · Very Slow', color: 'var(--danger)' };
                                return (
                                  <span
                                    className="text-[10px] px-1.5 py-0.5 rounded font-medium"
                                    style={{
                                      background: `color-mix(in srgb, ${tier.color} 12%, transparent)`,
                                      color: tier.color,
                                    }}
                                    title="Speed tier based on parameter count"
                                  >
                                    {tier.label}
                                  </span>
                                );
                              })()}
                            </div>
                            {/* Memory fit — only for non-running models */}
                            {!running &&
                              system &&
                              (() => {
                                const avail = system.memory.free + system.memory.cached * 0.9;
                                const gap = avail - estRam;
                                const fitColor =
                                  fitStatus === 'fits'
                                    ? 'var(--success)'
                                    : fitStatus === 'tight'
                                      ? 'var(--warning)'
                                      : 'var(--danger)';
                                return (
                                  <div
                                    className="mt-2 p-2 rounded-md"
                                    style={{ background: 'var(--surface-card)' }}
                                  >
                                    <div className="flex items-center justify-between mb-1">
                                      <span
                                        className="text-[11px]"
                                        style={{ color: 'var(--text-tertiary)' }}
                                      >
                                        Needs ~{formatBytes(estRam)} · {formatBytes(avail)}{' '}
                                        available
                                      </span>
                                      <span
                                        className="text-[10px] px-1.5 py-0.5 rounded-full font-medium"
                                        style={{
                                          background: `color-mix(in srgb, ${fitColor} 15%, transparent)`,
                                          color: fitColor,
                                        }}
                                      >
                                        {fitStatus === 'fits'
                                          ? `✓ ${formatBytes(gap)} to spare`
                                          : fitStatus === 'tight'
                                            ? `⚠ Tight — ${formatBytes(gap)} to spare`
                                            : `✗ ${formatBytes(Math.abs(gap))} short`}
                                      </span>
                                    </div>
                                    <div
                                      className="h-1.5 rounded-full overflow-hidden"
                                      style={{ background: 'var(--surface-muted)' }}
                                    >
                                      <div
                                        className="h-full rounded-full transition-all"
                                        style={{
                                          width: `${Math.min(100, Math.round((estRam / avail) * 100))}%`,
                                          background: fitColor,
                                        }}
                                      />
                                    </div>
                                  </div>
                                );
                              })()}
                            {running &&
                              (() => {
                                const rm = ollama?.running.find(r => r.name === model.name);
                                if (!rm?.expires_at) return null;
                                const remaining = new Date(rm.expires_at).getTime() - Date.now();
                                void countdownTick;
                                if (remaining <= 0) return null;
                                const mins = Math.floor(remaining / 60000);
                                const secs = Math.floor((remaining % 60000) / 1000);
                                const isUrgent = remaining < 60000;
                                const isWarning = remaining < 300000;
                                return (
                                  <div
                                    className="text-[10px] mt-0.5"
                                    style={{
                                      color: isUrgent
                                        ? 'var(--danger)'
                                        : isWarning
                                          ? 'var(--warning)'
                                          : 'var(--text-tertiary)',
                                    }}
                                  >
                                    {isUrgent ? 'Unloading soon' : `Unloads in ${mins}m ${secs}s`}
                                  </div>
                                );
                              })()}
                          </div>
                        </div>
                        <div className="flex items-center gap-2 ml-3">
                          {running ? (
                            <>
                              <button
                                onClick={() => {
                                  setPromptModel(model.name);
                                  setPromptResponse('');
                                }}
                                className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors"
                                style={{ background: 'var(--accent-primary)', color: 'white' }}
                              >
                                <Zap className="w-3 h-3" /> Prompt
                              </button>
                              <button
                                onClick={() => handleModelAction('unload', model.name)}
                                disabled={actionLoading === `unload-${model.name}`}
                                className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors"
                                style={{
                                  background: 'rgba(255, 110, 110, 0.1)',
                                  color: 'var(--danger)',
                                }}
                              >
                                {actionLoading === `unload-${model.name}` ? (
                                  <RefreshCw className="w-3 h-3 animate-spin" />
                                ) : (
                                  <Square className="w-3 h-3" />
                                )}
                                Unload
                              </button>
                            </>
                          ) : (
                            <div className="flex items-center gap-2">
                              <button
                                onClick={() => handleModelAction('load', model.name)}
                                disabled={actionLoading === `load-${model.name}`}
                                className="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors"
                                style={{
                                  background: 'rgba(52, 211, 153, 0.1)',
                                  color: 'var(--success)',
                                }}
                              >
                                {actionLoading === `load-${model.name}` ? (
                                  <RefreshCw className="w-3 h-3 animate-spin" />
                                ) : (
                                  <Play className="w-3 h-3" />
                                )}
                                Load
                              </button>
                            </div>
                          )}
                          <button
                            onClick={() => {
                              const next = expanded ? null : model.name;
                              setExpandedModel(next);
                              if (next) fetchModelfile(model.name);
                            }}
                            className="p-1.5 rounded-md transition-colors"
                            style={{ color: 'var(--text-tertiary)' }}
                          >
                            {expanded ? (
                              <ChevronUp className="w-4 h-4" />
                            ) : (
                              <ChevronDown className="w-4 h-4" />
                            )}
                          </button>
                        </div>
                      </div>
                      {/* Running model VRAM info */}
                      {running &&
                        (() => {
                          const info = getRunningInfo(model.name);
                          return info ? (
                            <div
                              className="flex items-center gap-4 mt-2 text-[11px]"
                              style={{ color: 'var(--text-tertiary)' }}
                            >
                              {info.size_vram > 0 && (
                                <span>VRAM: {formatBytes(info.size_vram)}</span>
                              )}
                              {info.expires_at && (
                                <span>
                                  Expires: {new Date(info.expires_at).toLocaleTimeString()}
                                </span>
                              )}
                            </div>
                          ) : null;
                        })()}
                      {expanded && (
                        <div
                          className="mt-3 pt-3 text-xs font-mono space-y-1"
                          style={{
                            borderTop: '1px solid var(--border-subtle)',
                            color: 'var(--text-tertiary)',
                          }}
                        >
                          <p>Digest: {model.digest?.substring(0, 16)}...</p>
                          <p>Modified: {new Date(model.modified_at).toLocaleString()}</p>
                          {model.details?.family && <p>Family: {model.details.family}</p>}
                          {sessionStats[model.name] && (
                            <p style={{ color: 'var(--accent-secondary)' }}>
                              Session: {sessionStats[model.name].prompts} prompt
                              {sessionStats[model.name].prompts !== 1 ? 's' : ''} ·{' '}
                              {sessionStats[model.name].tokens.toLocaleString()} tokens
                            </p>
                          )}
                          {/* Model Tags (F14) */}
                          <div className="flex flex-wrap items-center gap-1.5 mt-2 font-sans">
                            {['coding', 'chat', 'fast', 'vision', 'reasoning'].map(tag => (
                              <button
                                key={tag}
                                onClick={() => toggleTag(model.name, tag)}
                                className="flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-medium transition-colors"
                                style={{
                                  background: (modelTags[model.name] || []).includes(tag)
                                    ? 'var(--accent-primary)'
                                    : 'var(--surface-muted)',
                                  color: (modelTags[model.name] || []).includes(tag)
                                    ? 'white'
                                    : 'var(--text-tertiary)',
                                  border: '1px solid var(--border-subtle)',
                                }}
                              >
                                <Tag className="w-2.5 h-2.5" />
                                {tag}
                              </button>
                            ))}
                          </div>
                          {/* Auto-load toggle (F16) */}
                          <button
                            onClick={() =>
                              setPreferredModel(autoLoadModel === model.name ? null : model.name)
                            }
                            className="flex items-center gap-1.5 mt-2 text-[11px] font-sans transition-colors"
                            style={{
                              color:
                                autoLoadModel === model.name
                                  ? 'var(--warning)'
                                  : 'var(--text-tertiary)',
                            }}
                          >
                            <Star
                              className="w-3 h-3"
                              style={{
                                fill: autoLoadModel === model.name ? 'var(--warning)' : 'none',
                              }}
                            />
                            {autoLoadModel === model.name
                              ? 'Preferred (auto-loads)'
                              : 'Set as preferred'}
                          </button>
                          {modelfileData[model.name] && (
                            <details className="mt-2">
                              <summary
                                className="cursor-pointer flex items-center gap-1"
                                style={{ color: 'var(--accent-secondary)' }}
                              >
                                <FileText className="w-3 h-3" /> Modelfile
                              </summary>
                              <pre
                                className="mt-1 p-2 rounded text-[11px] max-h-40 overflow-auto whitespace-pre-wrap"
                                style={{
                                  background: 'var(--bg-canvas)',
                                  color: 'var(--text-tertiary)',
                                }}
                              >
                                {modelfileData[model.name]}
                              </pre>
                            </details>
                          )}
                          <div
                            className="mt-3 pt-2"
                            style={{ borderTop: '1px solid var(--border-subtle)' }}
                          >
                            {deleteConfirm === model.name ? (
                              <div className="flex items-center gap-2">
                                <span className="text-xs" style={{ color: 'var(--danger)' }}>
                                  Delete {model.name}? Reclaim {formatBytes(model.size)}
                                </span>
                                <button
                                  onClick={() => handleModelAction('delete', model.name)}
                                  disabled={actionLoading === `delete-${model.name}`}
                                  className="px-2 py-1 rounded text-xs font-medium"
                                  style={{ background: 'var(--danger)', color: 'white' }}
                                >
                                  {actionLoading === `delete-${model.name}`
                                    ? 'Deleting...'
                                    : 'Yes, delete'}
                                </button>
                                <button
                                  onClick={() => setDeleteConfirm(null)}
                                  className="px-2 py-1 rounded text-xs"
                                  style={{ color: 'var(--text-tertiary)' }}
                                >
                                  Cancel
                                </button>
                              </div>
                            ) : (
                              <button
                                onClick={() => setDeleteConfirm(model.name)}
                                className="flex items-center gap-1.5 text-xs transition-colors"
                                style={{ color: 'var(--danger)' }}
                              >
                                <Trash2 className="w-3 h-3" /> Remove from disk
                              </button>
                            )}
                          </div>
                        </div>
                      )}
                    </div>
                  );
                })}
              {ollama.models.length === 0 && (
                <p className="text-center py-8 text-sm" style={{ color: 'var(--text-tertiary)' }}>
                  No models installed. Run &quot;ollama pull &lt;model&gt;&quot; to get started.
                </p>
              )}
              {/* N14: Co-load suggestions */}
              {system &&
                ollama.running.length > 0 &&
                (() => {
                  const usedVram = ollama.running.reduce((sum, r) => sum + r.size_vram, 0);
                  const freeForModels =
                    system.memory.free + system.memory.cached * 0.9 - usedVram * 0.1;
                  const suggestions = ollama.models
                    .filter(m => !isRunning(m.name))
                    .map(m => ({
                      name: m.name,
                      est: estimateRam(m.size, m.details?.quantization_level),
                    }))
                    .filter(m => m.est < freeForModels)
                    .sort((a, b) => b.est - a.est)
                    .slice(0, 3);
                  if (suggestions.length === 0) return null;
                  return (
                    <div
                      className="mt-3 p-3 rounded-lg"
                      style={{
                        background: 'var(--surface-muted)',
                        border: '1px solid var(--border-subtle)',
                      }}
                    >
                      <span className="text-[11px]" style={{ color: 'var(--text-tertiary)' }}>
                        Can also load:{' '}
                      </span>
                      {suggestions.map((s, i) => (
                        <span
                          key={s.name}
                          className="text-[11px] font-mono"
                          style={{ color: 'var(--accent-secondary)' }}
                        >
                          {i > 0 && ', '}
                          {s.name.split(':')[0]} (~{formatBytes(s.est)})
                        </span>
                      ))}
                    </div>
                  );
                })()}
            </div>
          )}
        </div>

        {/* Right Column */}
        <div className="space-y-6">
          {/* System Info */}
          <div className="card p-6">
            <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
              <Monitor className="w-5 h-5" style={{ color: 'var(--accent-secondary)' }} />
              System
            </h2>
            <div className="space-y-4">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Cpu className="w-4 h-4" style={{ color: 'var(--accent-primary)' }} />
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    Chip
                  </span>
                </div>
                <span className="text-sm font-mono font-medium">{system?.chip || '...'}</span>
              </div>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Activity className="w-4 h-4" style={{ color: 'var(--accent-secondary)' }} />
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    Cores
                  </span>
                </div>
                <span className="text-sm font-mono font-medium">{system?.cpuCores || '...'}</span>
              </div>
              <div>
                <div className="flex items-center justify-between mb-1">
                  <div className="flex items-center gap-2">
                    <MemoryStick className="w-4 h-4" style={{ color: 'var(--warning)' }} />
                    <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                      RAM
                    </span>
                  </div>
                  <span className="text-xs font-mono" style={{ color: 'var(--success)' }}>
                    {formatBytes((system?.memory.free || 0) + (system?.memory.cached || 0) * 0.9)}{' '}
                    avail
                  </span>
                </div>
                <ProgressBar
                  value={system?.memory.appMemory || 0}
                  max={system?.memory.total || 1}
                  color={
                    system?.memory.pressure === 'critical'
                      ? 'var(--danger)'
                      : system?.memory.pressure === 'warning'
                        ? 'var(--warning)'
                        : 'var(--accent-secondary)'
                  }
                />
                <div
                  className="flex justify-between mt-1 text-[10px]"
                  style={{ color: 'var(--text-tertiary)' }}
                >
                  <span>Used: {formatBytes(system?.memory.appMemory || 0)}</span>
                  <span>Total: {formatBytes(system?.memory.total || 0)}</span>
                </div>
              </div>
              <div>
                <div className="flex items-center justify-between mb-1">
                  <div className="flex items-center gap-2">
                    <HardDrive className="w-4 h-4" style={{ color: 'var(--purple)' }} />
                    <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                      Disk
                    </span>
                  </div>
                  <span className="text-xs font-mono" style={{ color: 'var(--text-tertiary)' }}>
                    {formatBytes(system?.disk.free || 0)} free
                  </span>
                </div>
                <ProgressBar
                  value={system?.disk.used || 0}
                  max={system?.disk.total || 1}
                  color="var(--purple)"
                />
              </div>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Clock className="w-4 h-4" style={{ color: 'var(--text-tertiary)' }} />
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    Uptime
                  </span>
                </div>
                <span className="text-sm font-mono font-medium">
                  {formatUptime(system?.uptime || 0)}
                </span>
              </div>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <HardDrive className="w-4 h-4" style={{ color: 'var(--accent-primary)' }} />
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    Ollama on disk
                  </span>
                </div>
                <span className="text-sm font-mono font-medium">
                  {formatBytes(system?.ollamaDiskUsage || 0)}
                </span>
              </div>
            </div>
          </div>

          {/* Whisper */}
          <div className="card p-6">
            <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
              <Mic className="w-5 h-5" style={{ color: 'var(--purple)' }} />
              Whisper.cpp
            </h2>
            {whisper?.installed ? (
              <div className="space-y-4">
                <div className="flex items-center justify-between">
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    Status
                  </span>
                  <div className="flex items-center gap-2">
                    <StatusDot status="online" />
                    <span className="text-sm font-medium" style={{ color: 'var(--success)' }}>
                      Installed
                    </span>
                  </div>
                </div>
                <div>
                  <span
                    className="text-xs font-medium block mb-2"
                    style={{ color: 'var(--text-tertiary)' }}
                  >
                    BINARIES
                  </span>
                  <div className="flex flex-wrap gap-1.5">
                    {whisper.binaries.map(b => (
                      <span
                        key={b}
                        className="text-[11px] px-2 py-1 rounded font-mono"
                        style={{
                          background: 'var(--surface-muted)',
                          color: 'var(--text-secondary)',
                        }}
                      >
                        {b}
                      </span>
                    ))}
                  </div>
                </div>
                <div>
                  <span
                    className="text-xs font-medium block mb-2"
                    style={{ color: 'var(--text-tertiary)' }}
                  >
                    MODELS
                  </span>
                  {whisper.models.length > 0 ? (
                    <div className="space-y-2">
                      {whisper.models.map(m => (
                        <div
                          key={m.name}
                          className="flex items-center justify-between p-2 rounded-md"
                          style={{ background: 'var(--surface-muted)' }}
                        >
                          <span className="text-sm font-mono">{m.name}</span>
                          <span className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                            {formatBytes(m.size)}
                          </span>
                        </div>
                      ))}
                    </div>
                  ) : (
                    <div
                      className="p-3 rounded-lg text-center"
                      style={{ background: 'var(--surface-muted)' }}
                    >
                      <p className="text-xs" style={{ color: 'var(--warning)' }}>
                        No models downloaded
                      </p>
                      <p className="text-[11px] mt-1" style={{ color: 'var(--text-tertiary)' }}>
                        Download off-network from huggingface.co
                      </p>
                    </div>
                  )}
                </div>
                {/* Whisper Transcription Test (F12) */}
                <div className="mt-3 pt-3" style={{ borderTop: '1px solid var(--border-subtle)' }}>
                  <label
                    className="flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium cursor-pointer transition-colors"
                    style={{ background: 'var(--surface-muted)', color: 'var(--text-secondary)' }}
                  >
                    <Mic className="w-3.5 h-3.5" />
                    {whisperTestLoading ? 'Transcribing...' : 'Test transcription'}
                    <input
                      type="file"
                      accept="audio/*"
                      className="hidden"
                      onChange={e => {
                        const f = e.target.files?.[0];
                        if (f) handleWhisperTest(f);
                      }}
                      disabled={whisperTestLoading}
                    />
                  </label>
                  {whisperTestResult && (
                    <div
                      className="mt-2 p-2 rounded text-xs"
                      style={{ background: 'var(--surface-muted)' }}
                    >
                      <p style={{ color: 'var(--text-secondary)' }}>{whisperTestResult.text}</p>
                      <p className="mt-1 text-[10px]" style={{ color: 'var(--text-tertiary)' }}>
                        {whisperTestResult.latencyMs}ms
                      </p>
                    </div>
                  )}
                </div>
              </div>
            ) : (
              <div
                className="flex items-center gap-3 p-4 rounded-lg"
                style={{ background: 'var(--surface-muted)' }}
              >
                <WifiOff className="w-5 h-5" style={{ color: 'var(--danger)' }} />
                <div>
                  <p className="font-medium" style={{ color: 'var(--danger)' }}>
                    Not installed
                  </p>
                  <p className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                    Run &quot;brew install whisper-cpp&quot;
                  </p>
                </div>
              </div>
            )}
          </div>

          {/* Speech — TTS Engines */}
          <div className="card p-6">
            <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
              <Volume2 className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
              Speech (TTS)
            </h2>
            {ttsData ? (
              <div className="space-y-3">
                {ttsData.engines.map(engine => (
                  <div
                    key={engine.name}
                    className="p-3 rounded-lg"
                    style={{ background: 'var(--surface-muted)' }}
                  >
                    <div className="flex items-center justify-between mb-1">
                      <div className="flex items-center gap-2">
                        <StatusDot
                          status={
                            engine.status === 'ready'
                              ? 'online'
                              : engine.status === 'partial'
                                ? 'warning'
                                : 'offline'
                          }
                        />
                        <span className="text-sm font-semibold">{engine.name}</span>
                        <span
                          className="text-[10px] px-1.5 py-0.5 rounded font-mono"
                          style={{
                            background:
                              engine.type === 'ollama' ? 'var(--accent-primary)' : 'var(--purple)',
                            color: '#fff',
                            opacity: 0.85,
                          }}
                        >
                          {engine.type === 'ollama' ? 'Ollama' : 'Python'}
                        </span>
                      </div>
                      {engine.size && (
                        <span
                          className="text-[11px] font-mono"
                          style={{ color: 'var(--text-tertiary)' }}
                        >
                          {engine.size}
                        </span>
                      )}
                    </div>
                    <p className="text-xs ml-5" style={{ color: 'var(--text-tertiary)' }}>
                      {engine.model}
                    </p>
                    <p
                      className="text-xs ml-5 mt-0.5"
                      style={{
                        color:
                          engine.status === 'ready'
                            ? 'var(--success)'
                            : engine.status === 'partial'
                              ? 'var(--warning)'
                              : 'var(--text-tertiary)',
                      }}
                    >
                      {engine.details}
                    </p>
                    {engine.voices && engine.status === 'ready' && (
                      <div className="flex flex-wrap gap-1 mt-2 ml-5">
                        {engine.voices.map(v => (
                          <span
                            key={v}
                            className="text-[10px] px-1.5 py-0.5 rounded font-mono"
                            style={{
                              background: 'var(--bg-elevated)',
                              color: 'var(--text-secondary)',
                            }}
                          >
                            {v}
                          </span>
                        ))}
                      </div>
                    )}
                  </div>
                ))}
                {/* Venv status */}
                <div
                  className="flex items-center justify-between text-xs pt-2"
                  style={{ borderTop: '1px solid var(--border-subtle)' }}
                >
                  <span style={{ color: 'var(--text-tertiary)' }}>Python venv</span>
                  <span
                    style={{ color: ttsData.venv.exists ? 'var(--success)' : 'var(--warning)' }}
                  >
                    {ttsData.venv.exists ? (
                      <>✓ {ttsData.venv.packages?.join(' · ') || 'installed'}</>
                    ) : (
                      'Not found — run setup-tts.sh'
                    )}
                  </span>
                </div>
              </div>
            ) : (
              <div
                className="p-3 rounded-lg text-center"
                style={{ background: 'var(--surface-muted)' }}
              >
                <p className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                  Loading TTS status...
                </p>
              </div>
            )}
          </div>

          {/* Extraction Service (F15) */}
          <div className="card p-6">
            <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
              <Activity className="w-5 h-5" style={{ color: 'var(--accent-secondary)' }} />
              Extraction Service
            </h2>
            <div className="flex items-center justify-between mb-3">
              <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                Status
              </span>
              <div className="flex items-center gap-2">
                <StatusDot status={extractionHealth?.status === 'ok' ? 'online' : 'offline'} />
                <span
                  className="text-sm font-medium"
                  style={{
                    color: extractionHealth?.status === 'ok' ? 'var(--success)' : 'var(--danger)',
                  }}
                >
                  {extractionHealth?.status === 'ok'
                    ? 'Online'
                    : extractionHealth
                      ? 'Offline'
                      : 'Checking...'}
                </span>
              </div>
            </div>
            <div className="flex items-center justify-between mb-2">
              <span className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                Endpoint
              </span>
              <span className="text-xs font-mono" style={{ color: 'var(--text-tertiary)' }}>
                localhost:4005
              </span>
            </div>
            {extractionHealth?.service && (
              <div className="flex items-center justify-between">
                <span className="text-xs" style={{ color: 'var(--text-tertiary)' }}>
                  Service
                </span>
                <span className="text-xs font-mono" style={{ color: 'var(--text-secondary)' }}>
                  {extractionHealth.service}
                </span>
              </div>
            )}
          </div>

          {/* Brew Packages */}
          <div className="card p-6">
            <h2 className="text-lg font-semibold flex items-center gap-2 mb-4">
              <Download className="w-5 h-5" style={{ color: 'var(--accent-secondary)' }} />
              Brew Packages
            </h2>
            <div className="space-y-2">
              {system?.brewPackages?.map(pkg => (
                <div
                  key={pkg.name}
                  className="flex items-center justify-between p-2 rounded-md"
                  style={{ background: 'var(--surface-muted)' }}
                >
                  <span className="text-sm font-mono font-medium">{pkg.name}</span>
                  <span className="text-xs font-mono" style={{ color: 'var(--text-tertiary)' }}>
                    v{pkg.version}
                  </span>
                </div>
              ))}
              {!system && (
                <p className="text-sm text-center py-4" style={{ color: 'var(--text-tertiary)' }}>
                  Loading...
                </p>
              )}
              {system && (!system.brewPackages || system.brewPackages.length === 0) && (
                <p className="text-sm text-center py-4" style={{ color: 'var(--text-tertiary)' }}>
                  No tracked packages found
                </p>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* Ollama Logs Viewer (F8) */}
      <div className="mt-6">
        <div className="flex items-center gap-2 mb-3">
          <button
            onClick={() => {
              setShowLogs(!showLogs);
              if (!showLogs) fetchLogs();
            }}
            className="flex items-center gap-2 text-sm font-medium transition-colors"
            style={{ color: 'var(--text-tertiary)' }}
          >
            <Terminal className="w-4 h-4" />
            {showLogs ? 'Hide' : 'Show'} Ollama Logs
            <ChevronDown
              className={`w-3 h-3 transition-transform ${showLogs ? 'rotate-180' : ''}`}
            />
          </button>
          {showLogs && (
            <button
              onClick={fetchLogs}
              className="p-1 rounded transition-colors"
              style={{ color: 'var(--text-tertiary)' }}
              title="Refresh logs"
            >
              <RefreshCw className="w-3.5 h-3.5" />
            </button>
          )}
        </div>
        {showLogs && (
          <div
            className="card p-4 max-h-60 overflow-y-auto font-mono text-[11px]"
            style={{ background: 'var(--bg-canvas)', border: '1px solid var(--border-subtle)' }}
          >
            {ollamaLogs.length === 0 ? (
              <p style={{ color: 'var(--text-tertiary)' }}>No logs available</p>
            ) : (
              ollamaLogs.map((line, i) => (
                <div
                  key={i}
                  className="py-0.5"
                  style={{
                    color:
                      line.includes('ERR') || line.includes('error')
                        ? 'var(--danger)'
                        : line.includes('WARN') || line.includes('warn')
                          ? 'var(--warning)'
                          : 'var(--text-tertiary)',
                  }}
                >
                  {line}
                </div>
              ))
            )}
          </div>
        )}
      </div>

      {/* Prompt Panel (slide-up modal with streaming) */}
      {promptModel && (
        <div
          className="fixed inset-0 z-50 flex items-end justify-center p-4 sm:p-6"
          style={{ background: 'rgba(0,0,0,0.6)' }}
          onClick={e => {
            if (e.target === e.currentTarget && !promptLoading) {
              abortRef.current?.abort();
              compareAbortRef.current?.abort();
              setPromptModel(null);
              setPromptResponse('');
              setPromptText('');
              setCompareModel(null);
              setCompareResponse('');
            }
          }}
        >
          <div
            className="card w-full max-w-3xl p-6 mb-4"
            style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border-default)' }}
          >
            <div className="flex items-center justify-between mb-4">
              <h3 className="text-lg font-semibold flex items-center gap-2">
                <Zap className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
                <span className="font-mono text-sm" style={{ color: 'var(--accent-secondary)' }}>
                  {promptModel}
                </span>
              </h3>
              <div className="flex items-center gap-2">
                {/* Chat mode toggle (F4) */}
                <button
                  onClick={() => {
                    setChatMode(!chatMode);
                    setChatMessages([]);
                    setPromptResponse('');
                    setStreamMetrics(null);
                  }}
                  className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium transition-colors"
                  style={{
                    background: chatMode ? 'var(--accent-primary)' : 'var(--surface-card)',
                    color: chatMode ? 'white' : 'var(--text-tertiary)',
                  }}
                  title={chatMode ? 'Switch to single prompt' : 'Switch to chat mode'}
                >
                  <MessageSquare className="w-3.5 h-3.5" />
                  {chatMode ? 'Chat' : 'Prompt'}
                </button>
                {(promptResponse || chatMessages.length > 0) && (
                  <button
                    onClick={copyResponse}
                    className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium transition-colors"
                    style={{
                      background: 'var(--surface-card)',
                      color: copied ? 'var(--success)' : 'var(--text-tertiary)',
                    }}
                  >
                    {copied ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
                    {copied ? 'Copied' : 'Copy'}
                  </button>
                )}
                <button
                  onClick={() => {
                    if (!promptLoading) {
                      abortRef.current?.abort();
                      compareAbortRef.current?.abort();
                      setPromptModel(null);
                      setPromptResponse('');
                      setPromptText('');
                      setCompareModel(null);
                      setCompareResponse('');
                    }
                  }}
                  className="p-2 rounded-lg transition-colors hover:bg-[var(--surface-card)]"
                  style={{ color: 'var(--text-tertiary)' }}
                >
                  <X className="w-4 h-4" />
                </button>
              </div>
            </div>
            {/* System prompt for chat mode (F4) */}
            {chatMode && (
              <input
                type="text"
                value={systemPrompt}
                onChange={e => setSystemPrompt(e.target.value)}
                placeholder="System prompt (optional)..."
                className="w-full px-3 py-2 mb-3 rounded-lg text-xs font-mono focus:outline-none focus:ring-2"
                style={{
                  background: 'var(--surface-card)',
                  border: '1px solid var(--border-subtle)',
                  color: 'var(--text-secondary)',
                  caretColor: 'var(--accent-primary)',
                }}
              />
            )}
            {/* Prompt History (F3) */}
            {showHistory && (
              <div
                className="mb-3 max-h-40 overflow-y-auto rounded-lg"
                style={{
                  background: 'var(--surface-card)',
                  border: '1px solid var(--border-subtle)',
                }}
              >
                {getPromptHistory().length === 0 ? (
                  <p className="text-xs p-3 text-center" style={{ color: 'var(--text-tertiary)' }}>
                    No history yet
                  </p>
                ) : (
                  getPromptHistory().map((h, i) => (
                    <button
                      key={i}
                      onClick={() => {
                        setPromptText(h.prompt);
                        setShowHistory(false);
                      }}
                      className="w-full text-left px-3 py-2 text-xs hover:bg-[var(--surface-muted)] transition-colors flex items-center justify-between gap-2"
                      style={{
                        color: 'var(--text-secondary)',
                        borderBottom: '1px solid var(--border-subtle)',
                      }}
                    >
                      <span className="truncate flex-1">{h.prompt}</span>
                      <span
                        className="text-[10px] shrink-0"
                        style={{ color: 'var(--text-tertiary)' }}
                      >
                        {h.model}
                      </span>
                    </button>
                  ))
                )}
              </div>
            )}
            <div className="flex gap-2">
              <div className="flex-1 relative">
                <textarea
                  value={promptText}
                  onChange={e => setPromptText(e.target.value)}
                  placeholder="Type your prompt here..."
                  rows={2}
                  className="w-full p-3 pr-10 rounded-lg text-sm resize-none focus:outline-none focus:ring-2"
                  style={{
                    background: 'var(--surface-card)',
                    border: '1px solid var(--border-subtle)',
                    color: 'var(--text-primary)',
                    caretColor: 'var(--accent-primary)',
                  }}
                  onKeyDown={e => {
                    if (e.key === 'Enter' && e.metaKey) handleSend();
                  }}
                  disabled={promptLoading}
                />
                <button
                  onClick={() => setShowHistory(s => !s)}
                  className="absolute right-2 top-2 p-1 rounded transition-colors"
                  style={{ color: showHistory ? 'var(--accent-primary)' : 'var(--text-tertiary)' }}
                  title="Prompt history"
                >
                  <History className="w-4 h-4" />
                </button>
              </div>
              <button
                onClick={handleSend}
                disabled={promptLoading || !promptText.trim()}
                className="flex items-center justify-center w-12 rounded-lg transition-colors disabled:opacity-40 shrink-0"
                style={{ background: 'var(--accent-primary)', color: 'white' }}
              >
                {promptLoading ? (
                  <RefreshCw className="w-5 h-5 animate-spin" />
                ) : (
                  <Send className="w-5 h-5" />
                )}
              </button>
            </div>
            <div className="flex items-center gap-3 mt-2">
              {/* F24: Vision image upload for vision models */}
              {promptModel && getModelBadges(promptModel).some(b => b.label.includes('Vision')) && (
                <div className="flex items-center gap-2">
                  {visionImages.length > 0 && (
                    <div className="flex items-center gap-1">
                      {visionImages.map((_, idx) => (
                        <span
                          key={idx}
                          className="text-[10px] px-1.5 py-0.5 rounded"
                          style={{
                            background: 'var(--surface-muted)',
                            color: 'var(--accent-secondary)',
                          }}
                        >
                          img{idx + 1}
                          <button
                            onClick={() =>
                              setVisionImages(prev => prev.filter((__, i) => i !== idx))
                            }
                            className="ml-1"
                            style={{ color: 'var(--danger)' }}
                          >
                            &times;
                          </button>
                        </span>
                      ))}
                    </div>
                  )}
                  <label
                    className="cursor-pointer text-[11px] px-2 py-1 rounded transition-colors"
                    style={{ background: 'var(--surface-muted)', color: 'var(--accent-secondary)' }}
                  >
                    + Image
                    <input
                      type="file"
                      accept="image/*"
                      className="hidden"
                      onChange={e => {
                        const file = e.target.files?.[0];
                        if (!file) return;
                        const reader = new FileReader();
                        reader.onload = () => {
                          const base64 = (reader.result as string).split(',')[1];
                          if (base64) setVisionImages(prev => [...prev, base64]);
                        };
                        reader.readAsDataURL(file);
                        e.target.value = '';
                      }}
                    />
                  </label>
                </div>
              )}
              <span className="text-[11px]" style={{ color: 'var(--text-tertiary)' }}>
                &#8984;+Enter to send &middot; Esc to close
              </span>
              {promptLoading && (
                <span
                  className="text-[11px] flex items-center gap-1"
                  style={{ color: 'var(--accent-secondary)' }}
                >
                  <span
                    className="inline-block w-1.5 h-1.5 rounded-full pulse-dot"
                    style={{ background: 'var(--accent-secondary)' }}
                  />
                  Streaming...
                </span>
              )}
              {streamMetrics && !promptLoading && (
                <span className="text-[11px] font-mono" style={{ color: 'var(--text-tertiary)' }}>
                  {streamMetrics.tokensPerSec.toFixed(1)} tok/s &middot; {streamMetrics.totalTokens}{' '}
                  tokens &middot; {(streamMetrics.durationMs / 1000).toFixed(1)}s
                </span>
              )}
            </div>
            {/* Chat messages (F4) */}
            {chatMode && chatMessages.length > 0 && (
              <div ref={responseRef} className="mt-4 max-h-80 overflow-y-auto space-y-3">
                {chatMessages.map((msg, i) => (
                  <div
                    key={i}
                    className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
                  >
                    <div
                      className="max-w-[80%] px-3 py-2 rounded-lg text-sm whitespace-pre-wrap"
                      style={{
                        background:
                          msg.role === 'user' ? 'var(--accent-primary)' : 'var(--surface-card)',
                        color: msg.role === 'user' ? 'white' : 'var(--text-secondary)',
                        border:
                          msg.role === 'assistant' ? '1px solid var(--border-subtle)' : 'none',
                      }}
                    >
                      {msg.role === 'assistant' ? (
                        <MarkdownResponse
                          content={msg.content}
                          isThinkModel={
                            promptModel
                              ? getModelBadges(promptModel).some(b => b.label === '<think>')
                              : false
                          }
                          streaming={i === chatMessages.length - 1 && promptLoading}
                        />
                      ) : (
                        msg.content
                      )}
                    </div>
                  </div>
                ))}
              </div>
            )}
            {/* Single prompt response + comparison (F5) */}
            {!chatMode && promptResponse && (
              <div className="mt-4 space-y-3">
                <div className={compareModel ? 'grid grid-cols-1 sm:grid-cols-2 gap-3' : ''}>
                  <div
                    ref={responseRef}
                    className="p-4 rounded-lg max-h-80 overflow-y-auto"
                    style={{
                      background: 'var(--surface-card)',
                      border: '1px solid var(--border-subtle)',
                    }}
                  >
                    {compareModel && (
                      <p
                        className="text-[10px] font-medium mb-2"
                        style={{ color: 'var(--accent-primary)' }}
                      >
                        {promptModel}
                      </p>
                    )}
                    <MarkdownResponse
                      content={promptResponse}
                      isThinkModel={
                        promptModel
                          ? getModelBadges(promptModel).some(b => b.label === '<think>')
                          : false
                      }
                      streaming={promptLoading}
                    />
                  </div>
                  {compareModel && (
                    <div
                      className="p-4 rounded-lg max-h-80 overflow-y-auto"
                      style={{
                        background: 'var(--surface-card)',
                        border: '1px solid var(--border-subtle)',
                      }}
                    >
                      <p
                        className="text-[10px] font-medium mb-2"
                        style={{ color: 'var(--accent-secondary)' }}
                      >
                        {compareModel}
                      </p>
                      <MarkdownResponse
                        content={compareResponse || 'Generating...'}
                        isThinkModel={
                          compareModel
                            ? getModelBadges(compareModel).some(b => b.label === '<think>')
                            : false
                        }
                      />
                    </div>
                  )}
                </div>
                {/* Compare with another model (F5) — BN1: only show loaded models */}
                {!promptLoading && !compareModel && ollama && ollama.running.length > 0 && (
                  <div className="flex items-center gap-2 flex-wrap">
                    <span className="text-[11px]" style={{ color: 'var(--text-tertiary)' }}>
                      Compare with:
                    </span>
                    {ollama.running.filter(r => r.name !== promptModel).slice(0, 3).length === 0 ? (
                      <span className="text-[11px]" style={{ color: 'var(--text-tertiary)' }}>
                        Load another model to compare
                      </span>
                    ) : (
                      ollama.running
                        .filter(r => r.name !== promptModel)
                        .slice(0, 3)
                        .map(r => (
                          <button
                            key={r.name}
                            onClick={() => handleCompare(promptText, r.name)}
                            className="text-[11px] px-2 py-1 rounded font-mono transition-colors"
                            style={{
                              background: 'var(--surface-muted)',
                              color: 'var(--accent-secondary)',
                            }}
                          >
                            {r.name}
                          </button>
                        ))
                    )}
                  </div>
                )}
              </div>
            )}
          </div>
        </div>
      )}

      {/* Keyboard Shortcuts Modal (F11) */}
      {showShortcuts && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center p-4"
          style={{ background: 'rgba(0,0,0,0.6)' }}
          onClick={e => {
            if (e.target === e.currentTarget) setShowShortcuts(false);
          }}
        >
          <div
            className="card w-full max-w-sm p-6"
            style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border-default)' }}
          >
            <div className="flex items-center justify-between mb-4">
              <h3 className="text-lg font-semibold flex items-center gap-2">
                <Keyboard className="w-5 h-5" style={{ color: 'var(--accent-primary)' }} />
                Keyboard Shortcuts
              </h3>
              <button
                onClick={() => setShowShortcuts(false)}
                className="p-1.5 rounded-lg"
                style={{ color: 'var(--text-tertiary)' }}
              >
                <X className="w-4 h-4" />
              </button>
            </div>
            <div className="space-y-2">
              {[
                ['?', 'Toggle this panel'],
                ['R', 'Refresh all data'],
                ['/', 'Focus model search'],
                ['Esc', 'Close modal / cancel stream'],
                ['⌘+Enter', 'Send prompt'],
              ].map(([key, desc]) => (
                <div key={key} className="flex items-center justify-between py-1.5">
                  <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                    {desc}
                  </span>
                  <kbd
                    className="text-xs font-mono px-2 py-1 rounded"
                    style={{ background: 'var(--surface-muted)', color: 'var(--text-primary)' }}
                  >
                    {key}
                  </kbd>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
