Index: lan-frontend/.env
===================================================================
--- lan-frontend/.env	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/.env	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,1 @@
+VITE_API_BASE=http://192.168.11.232:5555
Index: lan-frontend/src/App.jsx
===================================================================
--- lan-frontend/src/App.jsx	(revision 640ed89213ad0086dc8379de00b8d102057ead1f)
+++ lan-frontend/src/App.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -1,1169 +1,171 @@
-import React, { useEffect, useState } from "react";
-import './App.css';
+import React from "react";
+import {useEffect, useMemo, useState} from "react";
+import "./styles/theme.css";
 
-function App() {
-  const [computers, setComputers] = useState([]);
-  const [filteredComputers, setFilteredComputers] = useState([]);
-  const [stats, setStats] = useState(null);
-  const [selectedComputer, setSelectedComputer] = useState(null);
-  const [computerDetails, setComputerDetails] = useState(null);
-  const [loadingDetails, setLoadingDetails] = useState(false);
-  const [performanceData, setPerformanceData] = useState([]);
-  const [eventDistribution, setEventDistribution] = useState([]);
-  const [networkTraffic, setNetworkTraffic] = useState([]);
-  const [topProcesses, setTopProcesses] = useState([]);
-   // Бои за графикони
-  const CHART_COLORS = {
-    primary: '#2563eb',
-    secondary: '#7c3aed',
-    accentGreen: '#10b981',
-    accentOrange: '#f59e0b',
-    accentRed: '#ef4444',
-    lightBlue: '#93c5fd',
-    lightPurple: '#c4b5fd'
-  };
+import TopBar from "./components/TopBar";
+import ComputerCard from "./components/ComputerCard";
+import ComputerDetailsModal from "./components/ComputerDetailsModal";
+import AIAssistantDrawer from "./components/AIAssistantDrawer";
 
-  // Функција за вчитување на податоци за графикони
-  const loadChartData = () => {
-    // Перформанс податоци (симулирани, треба да ги земеш од API)
-    const mockPerfData = Array.from({ length: 24 }, (_, i) => ({
-      hour: `${i}:00`,
-      cpu: 20 + Math.random() * 30,
-      memory: 30 + Math.random() * 40,
-      network: Math.random() * 100
-    }));
-    setPerformanceData(mockPerfData);
+export default function App() {
+    const [stats, setStats] = useState(null);
+    const [computers, setComputers] = useState([]);
+    const [loading, setLoading] = useState(true);
+    const [lastUpdated, setLastUpdated] = useState(null);
 
-    // Дистрибуција на настани
-    const mockEventDist = [
-      { name: 'Process Create', value: 45, color: CHART_COLORS.primary },
-      { name: 'Network', value: 25, color: CHART_COLORS.secondary },
-      { name: 'File Create', value: 15, color: CHART_COLORS.accentGreen },
-      { name: 'Registry', value: 10, color: CHART_COLORS.accentOrange },
-      { name: 'Other', value: 5, color: CHART_COLORS.accentRed }
-    ];
-    setEventDistribution(mockEventDist);
+    const [query, setQuery] = useState("");
+    const [status, setStatus] = useState("");
 
-    // Мрежен сообраќај
-    const mockNetworkData = Array.from({ length: 12 }, (_, i) => ({
-      time: `${i * 2}:00`,
-      inbound: 50 + Math.random() * 200,
-      outbound: 30 + Math.random() * 150
-    }));
-    setNetworkTraffic(mockNetworkData);
+    const [selectedComputer, setSelectedComputer] = useState(null);
+    const [aiOpen, setAiOpen] = useState(false);
+    const [aiScope, setAiScope] = useState(null);
+    const [selectedEnv, setSelectedEnv] = useState("default");
 
-    // Топ процеси
-    const mockTopProcesses = [
-      { name: 'chrome.exe', cpu: 25, memory: 1200 },
-      { name: 'python.exe', cpu: 18, memory: 800 },
-      { name: 'explorer.exe', cpu: 12, memory: 300 },
-      { name: 'svchost.exe', cpu: 8, memory: 200 },
-      { name: 'node.exe', cpu: 6, memory: 500 }
-    ];
-    setTopProcesses(mockTopProcesses);
-  };
+    async function refresh() {
+        try {
+            setLoading(true);
+            const [s, c] = await Promise.all([
+                fetch("/api/stats").then((r) => r.json()),
+                fetch("/api/computers", {
+                    headers: {"X-Env": selectedEnv},
+                }).then((r) => r.json()),
+            ]);
+            setStats(s);
+            setComputers(Array.isArray(c) ? c : []);
+            setLastUpdated(new Date().toISOString());
+        } catch (e) {
+            console.error("refresh error", e);
+        } finally {
+            setLoading(false);
+        }
+    }
 
-  useEffect(() => {
-    loadChartData();
-    // Автоматско освежување на графикони
-    const chartInterval = setInterval(loadChartData, 30000);
-    return () => clearInterval(chartInterval);
-  }, []);
-  // Filter state
-  const [filters, setFilters] = useState({
-    status: "all",
-    search: "",
-    os: "all",
-    minCpu: 0,
-    minRam: 0
-  });
+    useEffect(() => {
+        refresh();
+    }, [selectedEnv]);
 
-  // AI chat state
-  const [chatQuestion, setChatQuestion] = useState("");
-  const [chatAnswer, setChatAnswer] = useState("");
-  const [chatLoading, setChatLoading] = useState(false);
-  const [chatComputer, setChatComputer] = useState("");
-  const [chatHistory, setChatHistory] = useState([]);
-  const [isChatOpen, setIsChatOpen] = useState(true);
-  const [showWelcome, setShowWelcome] = useState(true);
+    const filtered = useMemo(() => {
+        const q = query.trim().toLowerCase();
+        return (computers || [])
+            .filter((c) => (status ? c.status === status : true))
+            .filter((c) => {
+                if (!q) return true;
+                const hay = [c.name, c.user, c.ip, c.os, c.status].filter(Boolean).join(" ").toLowerCase();
+                return hay.includes(q);
+            });
+    }, [computers, query, status]);
 
-  // Modal tabs state
-  const [activeTab, setActiveTab] = useState('security');
-  const [sysmonData, setSysmonData] = useState(null);
-  const [networkData, setNetworkData] = useState(null);
-  const [loadingSysmon, setLoadingSysmon] = useState(false);
-  const [loadingNetwork, setLoadingNetwork] = useState(false);
-  const [selectedSysmonProcess, setSelectedSysmonProcess] = useState(null);
-  const [selectedNetworkItem, setSelectedNetworkItem] = useState(null);
+    return (
+        <div className="app-wrap">
+            <TopBar
+                onOpenAI={() => setAiOpen(true)}
+                onRefresh={refresh}
+                lastUpdated={lastUpdated}
+                onEnvChange={(env) => setSelectedEnv(env)}
+                selectedEnv={selectedEnv}
+            />
 
-  // Load main data
-  useEffect(() => {
-    loadComputers();
-    loadStats();
-    const interval = setInterval(() => {
-      loadComputers();
-      loadStats();
-    }, 30000);
-    return () => clearInterval(interval);
-  }, []);
+            {/* STATS */}
+            <div className="stats-grid">
+                <div className="stat-card">
+                    <div className="label">Total Computers</div>
+                    <div className="value">{computers.length}</div>
+                    <div className="sub">Monitored Systems</div>
+                </div>
 
-  // Apply filters
-  useEffect(() => {
-    let result = computers;
-    if (filters.status !== "all") {
-      result = result.filter(c => c.status === filters.status);
-    }
-    if (filters.os !== "all") {
-      result = result.filter(c => c.os && c.os.toLowerCase().includes(filters.os.toLowerCase()));
-    }
-    if (filters.minCpu > 0) {
-      result = result.filter(c => (c.avg_cpu || 0) >= filters.minCpu);
-    }
-    if (filters.minRam > 0) {
-      result = result.filter(c => (c.avg_ram || 0) >= filters.minRam);
-    }
-    if (filters.search.trim()) {
-      const searchTerm = filters.search.toLowerCase();
-      result = result.filter(c =>
-        c.name.toLowerCase().includes(searchTerm) ||
-        (c.user && c.user.toLowerCase().includes(searchTerm)) ||
-        (c.ip && c.ip.includes(searchTerm))
-      );
-    }
-    setFilteredComputers(result);
-  }, [computers, filters]);
+                <div className="stat-card">
+                    <div className="label">Sysmon Events (24h)</div>
+                    <div className="value">{stats?.total_sysmon_events ?? "—"}</div>
+                    <div className="sub">Security Events</div>
+                </div>
 
-  const loadComputers = () => {
-    fetch("/api/computers")
-      .then((r) => r.json())
-      .then(data => setComputers(data))
-      .catch((err) => console.error("API error:", err));
-  };
-
-  const loadStats = () => {
-    fetch("/api/stats")
-      .then((r) => r.json())
-      .then(setStats)
-      .catch((err) => console.error("API error:", err));
-  };
-
-  const openComputer = (name) => {
-    setSelectedComputer(name);
-    setLoadingDetails(true);
-    setComputerDetails(null);
-    setActiveTab('security');
-    setSysmonData(null);
-    setNetworkData(null);
-
-    fetch(`/api/computer/${encodeURIComponent(name)}`)
-      .then((r) => r.json())
-      .then((data) => {
-        setComputerDetails(data);
-        setLoadingDetails(false);
-      })
-      .catch((err) => {
-        console.error("API error:", err);
-        setLoadingDetails(false);
-      });
-  };
-
-  // Load Sysmon processes
-const loadSysmonProcesses = () => {
-  if (!selectedComputer) return;
-  setLoadingSysmon(true);
-  setSelectedSysmonProcess(null);
-
-  // КОРИГИРАЈ ГО ОВА URL
-  fetch(`/api/computer/${encodeURIComponent(selectedComputer)}/sysmon`)
-    .then((r) => {
-      if (!r.ok) throw new Error(`HTTP ${r.status}`);
-      return r.json();
-    })
-    .then((data) => {
-      console.log("Sysmon data received:", data);
-
-      // Ако data.processes е празна, пробај со детален endpoint
-      if (!data.processes || data.processes.length === 0) {
-        return fetch(`/api/computer/${encodeURIComponent(selectedComputer)}/detailed-sysmon`)
-          .then(r => r.json())
-          .then(detailedData => {
-            setSysmonData(detailedData);
-            setLoadingSysmon(false);
-          });
-      }
-
-      setSysmonData(data);
-      setLoadingSysmon(false);
-    })
-    .catch((err) => {
-      console.error("Sysmon API error:", err);
-
-      // Fallback - пробај да земеш Sysmon events од општиот endpoint
-      fetch(`/api/computer/${encodeURIComponent(selectedComputer)}`)
-        .then(r => r.json())
-        .then(fullData => {
-          if (fullData.sysmon_events) {
-            // Конвертирај Sysmon events во процес формат
-            const processes = fullData.sysmon_events.map((ev, idx) => ({
-              process_id: ev.event_id,
-              process_name: ev.event_type,
-              timestamp: ev.timestamp,
-              message: ev.message,
-              details: ev.details || {}
-            }));
-
-            setSysmonData({
-              computer_name: selectedComputer,
-              processes: processes,
-              count: processes.length
-            });
-          }
-          setLoadingSysmon(false);
-        })
-        .catch(() => {
-          setLoadingSysmon(false);
-        });
-    });
-};
-  // Load Network connections
-const loadNetworkConnections = () => {
-  if (!selectedComputer) return;
-  setLoadingNetwork(true);
-  setSelectedNetworkItem(null);
-
-  // КОРИГИРАЈ ГО ОВА URL
-  fetch(`/api/computer/${encodeURIComponent(selectedComputer)}/network`)
-    .then((r) => {
-      if (!r.ok) throw new Error(`HTTP ${r.status}`);
-      return r.json();
-    })
-    .then((data) => {
-      console.log("Network data received:", data);
-
-      setNetworkData(data);
-      setLoadingNetwork(false);
-    })
-    .catch((err) => {
-      console.error("Network API error:", err);
-
-      // Fallback - пробај да земеш network од општиот endpoint
-      fetch(`/api/computer/${encodeURIComponent(selectedComputer)}`)
-        .then(r => r.json())
-        .then(fullData => {
-          if (fullData.network_connections) {
-            const connections = fullData.network_connections.map((conn, idx) => ({
-              pid: conn.pid,
-              process_name: conn.process_name,
-              local_address: conn.local_address,
-              remote_address: conn.remote_address,
-              status: conn.status,
-              timestamp: conn.timestamp,
-              // Додади ги овие полиња за да match-не со очекуваниот формат
-              local_port: conn.local_address?.split(':')?.[1] || '',
-              remote_port: conn.remote_address?.split(':')?.[1] || '',
-              protocol: 'TCP'
-            }));
-
-            setNetworkData({
-              computer_name: selectedComputer,
-              connections: connections,
-              count: connections.length
-            });
-          }
-          setLoadingNetwork(false);
-        })
-        .catch(() => {
-          setLoadingNetwork(false);
-        });
-    });
-};
-  const formatTime = (iso) => {
-    if (!iso) return "-";
-    const d = new Date(iso);
-    return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
-  };
-
-  const formatDate = (iso) => {
-    if (!iso) return "-";
-    const d = new Date(iso);
-    return d.toLocaleDateString();
-  };
-
-  const askAssistant = async () => {
-    if (!chatQuestion.trim()) return;
-    const question = chatQuestion;
-    setChatLoading(true);
-    setChatQuestion("");
-    setShowWelcome(false);
-
-    const newHistory = [...chatHistory, {
-      type: "question",
-      content: question,
-      computer: chatComputer,
-      timestamp: new Date().toISOString()
-    }];
-    setChatHistory(newHistory);
-
-    try {
-      const res = await fetch("/api/chat", {
-        method: "POST",
-        headers: { "Content-Type": "application/json" },
-        body: JSON.stringify({
-          question: question,
-          computer_name: chatComputer || null,
-        }),
-      });
-
-      const answer = await res.json();
-      const finalAnswer = answer.answer || answer.error || "I couldn't process that request.";
-
-      setChatHistory([...newHistory, {
-        type: "answer",
-        content: finalAnswer,
-        timestamp: new Date().toISOString()
-      }]);
-    } catch (err) {
-      setChatHistory([...newHistory, {
-        type: "answer",
-        content: "⚠️ Connection error. Please check the server.",
-        timestamp: new Date().toISOString()
-      }]);
-    } finally {
-      setChatLoading(false);
-    }
-  };
-
-  const resetFilters = () => {
-    setFilters({
-      status: "all",
-      search: "",
-      os: "all",
-      minCpu: 0,
-      minRam: 0
-    });
-  };
-
-  const clearChat = () => {
-    setChatHistory([]);
-    setChatAnswer("");
-    setShowWelcome(true);
-  };
-
-  const closeModal = () => {
-    setSelectedComputer(null);
-    setComputerDetails(null);
-    setSysmonData(null);
-    setNetworkData(null);
-    setSelectedSysmonProcess(null);
-    setSelectedNetworkItem(null);
-  };
-
-  const quickQuestions = [
-    "Show security alerts from yesterday",
-    "Which devices have high CPU usage?",
-    "List suspicious network connections",
-    "Show recent Sysmon events",
-    "Display offline devices"
-  ];
-
-  return (
-    <div className="app-container">
-      {/* Main Content */}
-      <main className="main-content">
-        {/* Top Navigation */}
-        <nav className="top-nav">
-          <div className="nav-left">
-            <div className="logo">
-              <div className="logo-icon">🛡️</div>
-              <div className="logo-text">
-                <h1>LAN Sentinel</h1>
-                <span className="logo-subtitle">Enterprise Security Dashboard</span>
-              </div>
-            </div>
-          </div>
-
-          <div className="nav-center">
-            <div className="search-bar">
-              <input
-                type="text"
-                placeholder="Search devices..."
-                value={filters.search}
-                onChange={(e) => setFilters({...filters, search: e.target.value})}
-              />
-              <span className="search-icon">🔍</span>
-            </div>
-          </div>
-
-          <div className="nav-right">
-            <button
-              className={`ai-toggle-btn ${isChatOpen ? 'active' : ''}`}
-              onClick={() => setIsChatOpen(!isChatOpen)}
-            >
-              <span className="ai-icon">🤖</span>
-              <span>AI Assistant</span>
-            </button>
-            <button className="refresh-btn" onClick={loadComputers}>
-              <span className="refresh-icon">↻</span>
-              Refresh
-            </button>
-            <div className="user-profile">
-              <div className="profile-avatar">AD</div>
-            </div>
-          </div>
-        </nav>
-
-        {/* Stats Overview */}
-        <section className="stats-overview">
-          <div className="stats-header">
-            <h2>Network Overview</h2>
-            <div className="stats-time">
-              Last updated: {new Date().toLocaleTimeString()}
-            </div>
-          </div>
-
-          <div className="stats-grid">
-            <div className="stat-card">
-              <div className="stat-main">
-                <span className="stat-number">{computers.length}</span>
-                <span className="stat-label">Total Devices</span>
-              </div>
-              <div className="stat-icon">🖥️</div>
+                <div className="stat-card">
+                    <div className="label">Network Connections (24h)</div>
+                    <div className="value">{stats?.total_network_connections ?? "—"}</div>
+                    <div className="sub">Captured Connections</div>
+                </div>
             </div>
 
-            <div className="stat-card online">
-              <div className="stat-main">
-                <span className="stat-number">{computers.filter(c => c.status === 'online').length}</span>
-                <span className="stat-label">Online</span>
-              </div>
-              <div className="stat-trend">
-                <span className="trend-up">↑</span>
-                <span>{computers.length > 0 ? Math.round((computers.filter(c => c.status === 'online').length/computers.length)*100) : 0}%</span>
-              </div>
+            {/* FILTERS */}
+            <div className="panel" style={{marginBottom: 14}}>
+                <div className="panel-head">
+                    <div className="panel-title">Filters</div>
+                    <div style={{color: "var(--dim)", fontSize: 12}}>
+                        {loading ? "Refreshing…" : `${filtered.length} shown`}
+                    </div>
+                </div>
+                <div className="panel-body">
+                    <div className="filters">
+                        <input
+                            className="input"
+                            value={query}
+                            onChange={(e) => setQuery(e.target.value)}
+                            placeholder="Search by name / user / ip / os…"
+                        />
+
+                        <select className="select" value={status} onChange={(e) => setStatus(e.target.value)}>
+                            <option value="">All status</option>
+                            <option value="online">Online</option>
+                            <option value="idle">Idle</option>
+                            <option value="offline">Offline</option>
+                        </select>
+
+                        <button className="btn primary" onClick={refresh}>Refresh</button>
+                    </div>
+                </div>
             </div>
 
-            <div className="stat-card idle">
-              <div className="stat-main">
-                <span className="stat-number">{computers.filter(c => c.status === 'idle').length}</span>
-                <span className="stat-label">Idle</span>
-              </div>
-              <div className="stat-icon">⏸️</div>
+            {/* COMPUTERS */}
+            <div className="panel">
+                <div className="panel-head">
+                    <div className="panel-title">Connected Computers</div>
+                    <div style={{display: "flex", gap: 10, alignItems: "center"}}>
+                        <span className="pill">{filtered.length} computers</span>
+                        <button className="btn" onClick={() => {
+                            setQuery("");
+                            setStatus("");
+                        }}>Reset
+                        </button>
+                    </div>
+                </div>
+                <div className="panel-body">
+                    <div className="computers-grid">
+                        {filtered.map((c) => (
+                            <ComputerCard
+                                key={c.name}
+                                c={c}
+                                onOpen={(pc) => {
+                                    setSelectedComputer(pc);
+                                    setAiScope(pc.name);
+                                }}
+                            />
+                        ))}
+                        {filtered.length === 0 && (
+                            <div style={{color: "var(--dim)"}}>No computers match filter.</div>
+                        )}
+                    </div>
+                </div>
             </div>
 
-            <div className="stat-card offline">
-              <div className="stat-main">
-                <span className="stat-number">{computers.filter(c => c.status === 'offline').length}</span>
-                <span className="stat-label">Offline</span>
-              </div>
-              <div className="stat-icon">🔴</div>
-            </div>
+            {/* DETAILS MODAL */}
+            <ComputerDetailsModal
+                open={!!selectedComputer}
+                computer={selectedComputer}
+                onClose={() => setSelectedComputer(null)}
+                onAskAI={(name) => {
+                    setAiScope(name);
+                    setAiOpen(true);
+                }}
+            />
 
-            <div className="stat-card security">
-              <div className="stat-main">
-                <span className="stat-number">
-                  {stats ? stats.total_sysmon_events.toLocaleString() : "0"}
-                </span>
-                <span className="stat-label">Sysmon Events</span>
-              </div>
-              <div className="stat-badge">24h</div>
-            </div>
-
-            <div className="stat-card alerts">
-              <div className="stat-main">
-                <span className="stat-number">
-                  {stats ? stats.security_alerts || 0 : "0"}
-                </span>
-                <span className="stat-label">Alerts</span>
-              </div>
-              <div className="stat-icon">🚨</div>
-            </div>
-          </div>
-        </section>
-
-        {/* Main Dashboard */}
-        <div className="dashboard-grid">
-          {/* Left Sidebar - Filters */}
-          <aside className="filter-sidebar">
-            <div className="filter-section">
-              <h3>Filters</h3>
-              <div className="filter-group">
-                <label>Status</label>
-                <div className="filter-tags">
-                  {["all", "online", "idle", "offline"].map(status => (
-                    <button
-                      key={status}
-                      className={`filter-tag ${filters.status === status ? 'active' : ''}`}
-                      onClick={() => setFilters({...filters, status})}
-                    >
-                      {status === 'all' ? 'All' : status}
-                    </button>
-                  ))}
-                </div>
-              </div>
-
-              <div className="filter-group">
-                <label>Operating System</label>
-                <select
-                  value={filters.os}
-                  onChange={(e) => setFilters({...filters, os: e.target.value})}
-                  className="filter-select"
-                >
-                  <option value="all">All OS</option>
-                  {[...new Set(computers.map(c => c.os).filter(Boolean))].map(os => (
-                    <option key={os} value={os}>{os}</option>
-                  ))}
-                </select>
-              </div>
-
-              <div className="filter-group">
-                <label>CPU Usage ≥ {filters.minCpu}%</label>
-                <input
-                  type="range"
-                  min="0"
-                  max="100"
-                  value={filters.minCpu}
-                  onChange={(e) => setFilters({...filters, minCpu: parseInt(e.target.value)})}
-                  className="range-slider"
-                />
-              </div>
-
-              <div className="filter-group">
-                <label>RAM Usage ≥ {filters.minRam}%</label>
-                <input
-                  type="range"
-                  min="0"
-                  max="100"
-                  value={filters.minRam}
-                  onChange={(e) => setFilters({...filters, minRam: parseInt(e.target.value)})}
-                  className="range-slider"
-                />
-              </div>
-
-              <button onClick={resetFilters} className="reset-filters-btn">
-                Reset Filters
-              </button>
-            </div>
-
-            <div className="filter-section">
-              <h3>Quick Actions</h3>
-              <button className="action-btn" onClick={loadComputers}>
-                <span className="action-icon">↻</span>
-                Refresh Data
-              </button>
-              <button className="action-btn" onClick={() => setIsChatOpen(true)}>
-                <span className="action-icon">🤖</span>
-                Open AI Assistant
-              </button>
-            </div>
-          </aside>
-
-          {/* Main Content Area */}
-          <div className="content-area">
-            <div className="content-header">
-              <div>
-                <h2>Network Devices</h2>
-                <p className="content-subtitle">{filteredComputers.length} devices found</p>
-              </div>
-              <div className="header-actions">
-                <select className="sort-select">
-                  <option>Last Seen</option>
-                  <option>Name</option>
-                  <option>CPU Usage</option>
-                  <option>Status</option>
-                </select>
-              </div>
-            </div>
-
-            {/* Devices Grid */}
-            <div className="devices-grid">
-              {filteredComputers.map((c) => (
-                <div
-                  key={c.name}
-                  className={`device-card ${c.status}`}
-                  onClick={() => openComputer(c.name)}
-                >
-                  <div className="device-header">
-                    <div className="device-title">
-                      <div className="device-avatar">
-                        {c.os?.includes("Windows") ? "🪟" :
-                         c.os?.includes("Linux") ? "🐧" :
-                         c.os?.includes("Mac") ? "🍎" : "💻"}
-                      </div>
-                      <div>
-                        <h3>{c.name}</h3>
-                        <p className="device-user">{c.user || "Unknown"}</p>
-                      </div>
-                    </div>
-                    <div className={`status-indicator ${c.status}`}>
-                      <div className="status-dot"></div>
-                      {c.status}
-                    </div>
-                  </div>
-
-                  <div className="device-info">
-                    <div className="info-row">
-                      <span className="info-label">IP</span>
-                      <span className="info-value">{c.ip}</span>
-                    </div>
-                    <div className="info-row">
-                      <span className="info-label">OS</span>
-                      <span className="info-value">{c.os}</span>
-                    </div>
-                    <div className="info-row">
-                      <span className="info-label">Last Active</span>
-                      <span className="info-value">{formatTime(c.last_seen)}</span>
-                    </div>
-                  </div>
-
-                  <div className="device-metrics">
-                    <div className="metric">
-                      <div className="metric-header">
-                        <span>CPU</span>
-                        <span className="metric-value">{c.avg_cpu || 0}%</span>
-                      </div>
-                      <div className="progress-bar">
-                        <div
-                          className="progress-fill cpu"
-                          style={{ width: `${Math.min(c.avg_cpu || 0, 100)}%` }}
-                        ></div>
-                      </div>
-                    </div>
-                    <div className="metric">
-                      <div className="metric-header">
-                        <span>Memory</span>
-                        <span className="metric-value">{c.avg_ram || 0}%</span>
-                      </div>
-                      <div className="progress-bar">
-                        <div
-                          className="progress-fill memory"
-                          style={{ width: `${Math.min(c.avg_ram || 0, 100)}%` }}
-                        ></div>
-                      </div>
-                    </div>
-                  </div>
-
-                  <div className="device-footer">
-                    {c.sysmon_available && (
-                      <div className="sysmon-badge">
-                        <span className="sysmon-icon">🛡️</span>
-                        {c.recent_sysmon || 0} events
-                      </div>
-                    )}
-                  </div>
-                </div>
-              ))}
-
-              {filteredComputers.length === 0 && (
-                <div className="empty-state">
-                  <div className="empty-icon">🔍</div>
-                  <h3>No devices match your filters</h3>
-                  <button onClick={resetFilters} className="empty-action-btn">
-                    Reset All Filters
-                  </button>
-                </div>
-              )}
-            </div>
-          </div>
+            {/* AI DRAWER */}
+            <AIAssistantDrawer
+                open={aiOpen}
+                onClose={() => setAiOpen(false)}
+                computers={computers}
+                scope={aiScope}
+                setScope={setAiScope}
+            />
         </div>
-      </main>
-
-      {/* AI Assistant Sidebar */}
-      <div className={`ai-sidebar ${isChatOpen ? 'open' : ''}`}>
-        <div className="ai-header">
-          <div className="ai-title">
-            <div className="ai-avatar">🤖</div>
-            <div>
-              <h3>Security AI Assistant</h3>
-              <p className="ai-subtitle">Powered by LAN Sentinel</p>
-            </div>
-          </div>
-          <button className="close-ai" onClick={() => setIsChatOpen(false)}>
-            ✕
-          </button>
-        </div>
-
-        <div className="ai-content">
-          <div className="chat-container">
-            <div className="chat-history">
-              {showWelcome && (
-                <div className="welcome-message">
-                  <div className="welcome-avatar">🤖</div>
-                  <div className="welcome-content">
-                    <h4>Welcome to Security AI Assistant!</h4>
-                    <p>I can help you analyze security events, monitor network activity, and investigate incidents.</p>
-                    <div className="welcome-questions">
-                      {quickQuestions.map((q, idx) => (
-                        <button
-                          key={idx}
-                          className="quick-question"
-                          onClick={() => {
-                            setChatQuestion(q);
-                            setTimeout(() => askAssistant(), 100);
-                          }}
-                        >
-                          {q}
-                        </button>
-                      ))}
-                    </div>
-                  </div>
-                </div>
-              )}
-
-              {chatHistory.map((msg, idx) => (
-                <div key={idx} className={`chat-message ${msg.type}`}>
-                  <div className="message-avatar">
-                    {msg.type === 'question' ? '👤' : '🤖'}
-                  </div>
-                  <div className="message-content">
-                    <div className="message-header">
-                      <span className="message-sender">
-                        {msg.type === 'question' ? 'You' : 'Security AI'}
-                      </span>
-                      {msg.computer && (
-                        <span className="message-context">
-                          Computer: {msg.computer}
-                        </span>
-                      )}
-                    </div>
-                    <div className="message-text">
-                      {msg.content}
-                    </div>
-                    <div className="message-time">
-                      {new Date(msg.timestamp).toLocaleTimeString([], {
-                        hour: '2-digit',
-                        minute: '2-digit'
-                      })}
-                    </div>
-                  </div>
-                </div>
-              ))}
-
-              {chatLoading && (
-                <div className="chat-message answer">
-                  <div className="message-avatar">🤖</div>
-                  <div className="message-content">
-                    <div className="typing-indicator">
-                      <div></div>
-                      <div></div>
-                      <div></div>
-                    </div>
-                  </div>
-                </div>
-              )}
-            </div>
-
-            <div className="chat-input-area">
-              <select
-                value={chatComputer}
-                onChange={(e) => setChatComputer(e.target.value)}
-                className="chat-select"
-              >
-                <option value="">🌐 All Network</option>
-                {computers.map((c) => (
-                  <option key={c.name} value={c.name}>
-                    {c.name}
-                  </option>
-                ))}
-              </select>
-
-              <div className="input-wrapper">
-                <input
-                  type="text"
-                  placeholder="Ask about security events..."
-                  value={chatQuestion}
-                  onChange={(e) => setChatQuestion(e.target.value)}
-                  onKeyDown={(e) => e.key === 'Enter' && askAssistant()}
-                  className="chat-input"
-                />
-                <button
-                  onClick={askAssistant}
-                  disabled={chatLoading || !chatQuestion.trim()}
-                  className="send-btn"
-                >
-                  {chatLoading ? '...' : '➤'}
-                </button>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      {/* Enhanced Computer Details Modal */}
-      {selectedComputer && (
-        <div className="modal-overlay" onClick={closeModal}>
-          <div className="modal large" onClick={(e) => e.stopPropagation()}>
-            <div className="modal-header">
-              <div className="modal-title">
-                <h2>{selectedComputer}</h2>
-                <div className="modal-subtitle">
-                  <span className="ip-badge">{computerDetails?.computer?.ip}</span>
-                  <span className="os-badge">{computerDetails?.computer?.os}</span>
-                  <span className={`status-badge ${computerDetails?.computer?.status}`}>
-                    {computerDetails?.computer?.status}
-                  </span>
-                </div>
-              </div>
-              <div className="modal-actions">
-                <button className="modal-btn">
-                  <span className="btn-icon">📊</span>
-                  Report
-                </button>
-                <button className="modal-close" onClick={closeModal}>
-                  ✕
-                </button>
-              </div>
-            </div>
-
-            <div className="modal-content">
-              {loadingDetails ? (
-                <div className="loading-state">
-                  <div className="loader"></div>
-                  <p>Loading device details...</p>
-                </div>
-              ) : (
-                <>
-                  {/* Quick Stats */}
-                  <div className="quick-stats">
-                    <div className="quick-stat">
-                      <div className="stat-icon">⚡</div>
-                      <div className="stat-info">
-                        <div className="stat-value">
-                          {computerDetails?.current_metrics?.cpu_usage || 0}%
-                        </div>
-                        <div className="stat-label">CPU</div>
-                      </div>
-                    </div>
-                    <div className="quick-stat">
-                      <div className="stat-icon">💾</div>
-                      <div className="stat-info">
-                        <div className="stat-value">
-                          {computerDetails?.current_metrics?.ram_usage || 0}%
-                        </div>
-                        <div className="stat-label">RAM</div>
-                      </div>
-                    </div>
-                    <div className="quick-stat">
-                      <div className="stat-icon">📡</div>
-                      <div className="stat-info">
-                        <div className="stat-value">
-                          {computerDetails?.current_metrics?.network_usage || 0}%
-                        </div>
-                        <div className="stat-label">Network</div>
-                      </div>
-                    </div>
-                    <div className="quick-stat">
-                      <div className="stat-icon">🛡️</div>
-                      <div className="stat-info">
-                        <div className="stat-value">
-                          {computerDetails?.recent_sysmon || 0}
-                        </div>
-                        <div className="stat-label">Sysmon Events</div>
-                      </div>
-                    </div>
-                  </div>
-
-                  {/* Tabs */}
-                  <div className="modal-tabs">
-                    <div className="tabs-header">
-                      <button
-                        className={`tab ${activeTab === 'security' ? 'active' : ''}`}
-                        onClick={() => setActiveTab('security')}
-                      >
-                        Security Events
-                      </button>
-                      <button
-                        className={`tab ${activeTab === 'sysmon' ? 'active' : ''}`}
-                        onClick={() => {
-                          setActiveTab('sysmon');
-                          if (!sysmonData) loadSysmonProcesses();
-                        }}
-                      >
-                        Sysmon Processes
-                      </button>
-                      <button
-                        className={`tab ${activeTab === 'network' ? 'active' : ''}`}
-                        onClick={() => {
-                          setActiveTab('network');
-                          if (!networkData) loadNetworkConnections();
-                        }}
-                      >
-                        Network Connections
-                      </button>
-                      <button
-                        className={`tab ${activeTab === 'details' ? 'active' : ''}`}
-                        onClick={() => setActiveTab('details')}
-                      >
-                        System Details
-                      </button>
-                    </div>
-
-                    <div className="tabs-content">
-                      {/* Security Events Tab */}
-                      {activeTab === 'security' && (
-                        <div className="tab-pane active">
-                          <div className="data-table-container">
-                            <table className="data-table">
-                              <thead>
-                                <tr>
-                                  <th>Time</th>
-                                  <th>Event ID</th>
-                                  <th>Type</th>
-                                  <th>Description</th>
-                                </tr>
-                              </thead>
-                              <tbody>
-                                {(computerDetails?.sysmon_events || []).map((ev, idx) => (
-                                  <tr key={idx}>
-                                    <td>{formatTime(ev.timestamp)}</td>
-                                    <td>
-                                      <span className="event-id">#{ev.event_id}</span>
-                                    </td>
-                                    <td>
-                                      <span className={`event-type ${ev.event_type?.toLowerCase()}`}>
-                                        {ev.event_type}
-                                      </span>
-                                    </td>
-                                    <td className="event-desc">
-                                      {ev.message?.slice(0, 100) || 'No description'}
-                                    </td>
-                                  </tr>
-                                ))}
-                              </tbody>
-                            </table>
-                          </div>
-                        </div>
-                      )}
-
-                      {/* Sysmon Processes Tab */}
-                      {activeTab === 'sysmon' && (
-                        <div className="tab-pane active">
-                          <div className="pane-header">
-                            <h3>Sysmon Processes</h3>
-                            <button
-                              onClick={loadSysmonProcesses}
-                              className="refresh-btn-small"
-                              disabled={loadingSysmon}
-                            >
-                              {loadingSysmon ? 'Loading...' : '↻ Refresh'}
-                            </button>
-                          </div>
-
-                          {loadingSysmon ? (
-                            <div className="loading-state">
-                              <div className="loader"></div>
-                              <p>Loading Sysmon processes...</p>
-                            </div>
-                          ) : sysmonData ? (
-                            <div className="data-grid">
-                              <div className="grid-container">
-                                {sysmonData.processes && sysmonData.processes.length > 0 ? (
-                                  <>
-                                    <div className="grid-sidebar">
-                                      <div className="process-list">
-                                        {sysmonData.processes.map((proc, idx) => (
-                                          <div
-                                            key={idx}
-                                            className={`process-item ${selectedSysmonProcess === idx ? 'active' : ''}`}
-                                            onClick={() => setSelectedSysmonProcess(idx)}
-                                          >
-                                            <div className="process-name">
-                                              <strong>{proc.process_name || 'Unknown'}</strong>
-                                            </div>
-                                            <div className="process-pid">
-                                              PID: {proc.process_id}
-                                            </div>
-                                            <div className="process-time">
-                                              {formatTime(proc.timestamp)}
-                                            </div>
-                                          </div>
-                                        ))}
-                                      </div>
-                                    </div>
-                                    <div className="grid-content">
-                                      {selectedSysmonProcess !== null ? (
-                                        <div className="json-viewer">
-                                          <div className="json-header">
-                                            <h4>Process Details (JSON)</h4>
-                                            <button
-                                              className="copy-json"
-                                              onClick={() => navigator.clipboard.writeText(
-                                                JSON.stringify(sysmonData.processes[selectedSysmonProcess], null, 2)
-                                              )}
-                                            >
-                                              📋 Copy JSON
-                                            </button>
-                                          </div>
-                                          <pre className="json-content">
-                                            {JSON.stringify(sysmonData.processes[selectedSysmonProcess], null, 2)}
-                                          </pre>
-                                        </div>
-                                      ) : (
-                                        <div className="select-prompt">
-                                          <div className="prompt-icon">👈</div>
-                                          <p>Select a process from the list to view its JSON data</p>
-                                        </div>
-                                      )}
-                                    </div>
-                                  </>
-                                ) : (
-                                  <div className="empty-data">
-                                    <div className="empty-icon">📄</div>
-                                    <p>No Sysmon processes found</p>
-                                  </div>
-                                )}
-                              </div>
-                            </div>
-                          ) : (
-                            <div className="empty-data">
-                              <p>Click refresh to load Sysmon processes</p>
-                              <button onClick={loadSysmonProcesses} className="load-btn">
-                                Load Sysmon Data
-                              </button>
-                            </div>
-                          )}
-                        </div>
-                      )}
-
-                      {/* Network Connections Tab */}
-                      {activeTab === 'network' && (
-                        <div className="tab-pane active">
-                          <div className="pane-header">
-                            <h3>Network Connections</h3>
-                            <button
-                              onClick={loadNetworkConnections}
-                              className="refresh-btn-small"
-                              disabled={loadingNetwork}
-                            >
-                              {loadingNetwork ? 'Loading...' : '↻ Refresh'}
-                            </button>
-                          </div>
-
-                          {loadingNetwork ? (
-                            <div className="loading-state">
-                              <div className="loader"></div>
-                              <p>Loading network connections...</p>
-                            </div>
-                          ) : networkData ? (
-                            <div className="data-grid">
-                              <div className="grid-container">
-                                {networkData.connections && networkData.connections.length > 0 ? (
-                                  <>
-                                    <div className="grid-sidebar">
-                                      <div className="network-list">
-                                        {networkData.connections.map((conn, idx) => (
-                                          <div
-                                            key={idx}
-                                            className={`network-item ${selectedNetworkItem === idx ? 'active' : ''}`}
-                                            onClick={() => setSelectedNetworkItem(idx)}
-                                          >
-                                            <div className="network-address">
-                                              <strong>{conn.local_address}:{conn.local_port}</strong>
-                                              <div className="arrow">→</div>
-                                              <strong>{conn.remote_address}:{conn.remote_port}</strong>
-                                            </div>
-                                            <div className="network-protocol">
-                                              {conn.protocol} • {conn.state}
-                                            </div>
-                                          </div>
-                                        ))}
-                                      </div>
-                                    </div>
-                                    <div className="grid-content">
-                                      {selectedNetworkItem !== null ? (
-                                        <div className="json-viewer">
-                                          <div className="json-header">
-                                            <h4>Connection Details (JSON)</h4>
-                                            <button
-                                              className="copy-json"
-                                              onClick={() => navigator.clipboard.writeText(
-                                                JSON.stringify(networkData.connections[selectedNetworkItem], null, 2)
-                                              )}
-                                            >
-                                              📋 Copy JSON
-                                            </button>
-                                          </div>
-                                          <pre className="json-content">
-                                            {JSON.stringify(networkData.connections[selectedNetworkItem], null, 2)}
-                                          </pre>
-                                        </div>
-                                      ) : (
-                                        <div className="select-prompt">
-                                          <div className="prompt-icon">👈</div>
-                                          <p>Select a connection to view its JSON data</p>
-                                        </div>
-                                      )}
-                                    </div>
-                                  </>
-                                ) : (
-                                  <div className="empty-data">
-                                    <div className="empty-icon">🌐</div>
-                                    <p>No network connections found</p>
-                                  </div>
-                                )}
-                              </div>
-                            </div>
-                          ) : (
-                            <div className="empty-data">
-                              <p>Click refresh to load network connections</p>
-                              <button onClick={loadNetworkConnections} className="load-btn">
-                                Load Network Data
-                              </button>
-                            </div>
-                          )}
-                        </div>
-                      )}
-
-                      {/* System Details Tab */}
-                      {activeTab === 'details' && computerDetails && (
-                        <div className="tab-pane active">
-                          <div className="details-grid">
-                            <div className="detail-group">
-                              <h4>System Information</h4>
-                              <div className="detail-item">
-                                <span className="detail-label">Hostname:</span>
-                                <span className="detail-value">{computerDetails.computer.name}</span>
-                              </div>
-                              <div className="detail-item">
-                                <span className="detail-label">IP Address:</span>
-                                <span className="detail-value">{computerDetails.computer.ip}</span>
-                              </div>
-                              <div className="detail-item">
-                                <span className="detail-label">OS Version:</span>
-                                <span className="detail-value">{computerDetails.computer.os}</span>
-                              </div>
-                              <div className="detail-item">
-                                <span className="detail-label">Last Seen:</span>
-                                <span className="detail-value">{formatTime(computerDetails.computer.last_seen)}</span>
-                              </div>
-                            </div>
-
-                            <div className="detail-group">
-                              <h4>Current Metrics</h4>
-                              <div className="detail-item">
-                                <span className="detail-label">CPU Usage:</span>
-                                <span className="detail-value">{computerDetails.current_metrics?.cpu_usage || 0}%</span>
-                              </div>
-                              <div className="detail-item">
-                                <span className="detail-label">Memory Usage:</span>
-                                <span className="detail-value">{computerDetails.current_metrics?.ram_usage || 0}%</span>
-                              </div>
-                              <div className="detail-item">
-                                <span className="detail-label">Disk Usage:</span>
-                                <span className="detail-value">{computerDetails.current_metrics?.disk_usage || 0}%</span>
-                              </div>
-                            </div>
-                          </div>
-                        </div>
-                      )}
-                    </div>
-                  </div>
-                </>
-              )}
-            </div>
-          </div>
-        </div>
-      )}
-    </div>
-  );
+    );
 }
-
-export default App;
Index: lan-frontend/src/components/AIAssistantDrawer.jsx
===================================================================
--- lan-frontend/src/components/AIAssistantDrawer.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/src/components/AIAssistantDrawer.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,101 @@
+import React from "react";
+
+import { useEffect, useRef, useState } from "react";
+
+export default function AIAssistantDrawer({ open, onClose, computers, scope, setScope }) {
+  const [messages, setMessages] = useState([
+    { role: "assistant", text: "Постави прашање за Sysmon, процеси, мрежа. Можеш да избереш компјутер (scope) горе." },
+  ]);
+  const [input, setInput] = useState("");
+  const [sending, setSending] = useState(false);
+  const bottomRef = useRef(null);
+
+  useEffect(() => {
+    if (bottomRef.current) bottomRef.current.scrollIntoView({ behavior: "smooth" });
+  }, [messages, open]);
+
+  async function send() {
+    const q = input.trim();
+    if (!q || sending) return;
+
+    setInput("");
+    setMessages((m) => [...m, { role: "user", text: q }]);
+    setSending(true);
+
+    try {
+      const r = await fetch("/api/chat", {
+        method: "POST",
+        headers: { "Content-Type": "application/json" },
+        body: JSON.stringify({ question: q, computer_name: scope || null }),
+      });
+      const data = await r.json();
+      setMessages((m) => [...m, { role: "assistant", text: data.answer || "(no answer)" }]);
+    } catch (e) {
+      setMessages((m) => [...m, { role: "assistant", text: `Грешка: ${String(e.message || e)}` }]);
+    } finally {
+      setSending(false);
+    }
+  }
+
+  if (!open) return null;
+
+  return (
+    <>
+      <div className="drawer-backdrop" onMouseDown={onClose} />
+      <div className="drawer" onMouseDown={(e) => e.stopPropagation()}>
+        <div className="drawer-head">
+          <div>
+            <div className="drawer-title">🤖 AI Assistant</div>
+            <div className="drawer-sub">Uses /api/chat (RAG over your logs)</div>
+          </div>
+          <button className="btn" onClick={onClose}>Close</button>
+        </div>
+
+        <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
+          <div className="row">
+            <select className="select" value={scope || ""} onChange={(e) => setScope(e.target.value || null)}>
+              <option value="">All computers</option>
+              {(computers || []).map((c) => (
+                <option key={c.name} value={c.name}>{c.name}</option>
+              ))}
+            </select>
+          </div>
+          <div className="small">Tip: “Top процеси по RAM” или “сомнителни sysmon events”</div>
+        </div>
+
+        <div className="drawer-body">
+          {messages.map((m, i) => (
+            <div key={i} className={`bubble ${m.role === "user" ? "user" : ""}`}>
+              <div style={{ fontSize: 11, color: "var(--dim)", marginBottom: 6 }}>
+                {m.role === "user" ? "You" : "Assistant"}
+              </div>
+              <div style={{ whiteSpace: "pre-wrap" }}>{m.text}</div>
+            </div>
+          ))}
+          <div ref={bottomRef} />
+        </div>
+
+        <div className="drawer-foot">
+          <div className="row">
+            <textarea
+              className="textarea"
+              rows={2}
+              value={input}
+              placeholder="Постави прашање… (Enter = send, Shift+Enter нов ред)"
+              onChange={(e) => setInput(e.target.value)}
+              onKeyDown={(e) => {
+                if (e.key === "Enter" && !e.shiftKey) {
+                  e.preventDefault();
+                  send();
+                }
+              }}
+            />
+            <button className="btn primary" onClick={send} disabled={sending || !input.trim()}>
+              {sending ? "Sending…" : "Send"}
+            </button>
+          </div>
+        </div>
+      </div>
+    </>
+  );
+}
Index: lan-frontend/src/components/ComputerCard.jsx
===================================================================
--- lan-frontend/src/components/ComputerCard.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/src/components/ComputerCard.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,42 @@
+import React from "react";
+
+function dotClass(status) {
+  if (status === "online") return "dot online";
+  if (status === "idle") return "dot idle";
+  return "dot offline";
+}
+
+export default function ComputerCard({ c, onOpen }) {
+  return (
+    <button className="card" onClick={() => onOpen?.(c)}>
+      <div className="card-head">
+        <div className="card-title">{c.name}</div>
+        <div className={dotClass(c.status)} />
+      </div>
+
+      <div className="meta">
+        <div><span className="dim">🏷 Env:</span> {c.env_name || "default"}</div>
+        <div><span className="dim">👤 User:</span> {c.user || "—"}</div>
+        <div><span className="dim">🌐 IP:</span> {c.ip || "—"}</div>
+        <div><span className="dim">💻 OS:</span> {(c.os || "—").slice(0, 60)}</div>
+        <div><span className="dim">⏰ Last:</span> {c.last_seen ? new Date(c.last_seen).toLocaleTimeString() : "—"}</div>
+        <div><span className="dim">🛡 Sysmon:</span> {c.sysmon_available ? "Yes" : "No"} • {c.recent_sysmon ?? 0} events</div>
+      </div>
+
+      <div className="kpis">
+        <div className="kpi">
+          <div className="k">CPU avg</div>
+          <div className="v">{c.avg_cpu ?? 0}%</div>
+        </div>
+        <div className="kpi">
+          <div className="k">RAM avg</div>
+          <div className="v">{c.avg_ram ?? 0}%</div>
+        </div>
+        <div className="kpi">
+          <div className="k">Logs 24h</div>
+          <div className="v">{c.recent_logs ?? 0}</div>
+        </div>
+      </div>
+    </button>
+  );
+}
Index: lan-frontend/src/components/ComputerDetailsModal.jsx
===================================================================
--- lan-frontend/src/components/ComputerDetailsModal.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/src/components/ComputerDetailsModal.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,384 @@
+import React, { useEffect, useMemo, useState } from "react";
+
+function severityForEventId(eventId) {
+  const id = parseInt(eventId, 10);
+  const high = new Set([1, 3, 8, 10]);
+  const med = new Set([5, 6, 7, 11, 22]);
+  if (high.has(id)) return "high";
+  if (med.has(id)) return "medium";
+  return "low";
+}
+
+export default function ComputerDetailsModal({ open, computer, onClose, onAskAI }) {
+  const [tab, setTab] = useState("overview");
+  const [loading, setLoading] = useState(false);
+  const [details, setDetails] = useState(null);
+  const [search, setSearch] = useState("");
+
+  // JSON viewer state
+  const [jsonOpen, setJsonOpen] = useState(false);
+  const [jsonTitle, setJsonTitle] = useState("");
+  const [jsonData, setJsonData] = useState(null);
+
+  useEffect(() => {
+    if (!open || !computer?.name) return;
+    let cancelled = false;
+
+    async function load() {
+      try {
+        setLoading(true);
+        const r = await fetch(`/api/computer/${encodeURIComponent(computer.name)}`);
+        const d = await r.json();
+        if (!cancelled) setDetails(d);
+      } catch (e) {
+        console.error("details error", e);
+      } finally {
+        if (!cancelled) setLoading(false);
+      }
+    }
+
+    load();
+    return () => {
+      cancelled = true;
+    };
+  }, [open, computer?.name]);
+
+  const filteredSysmon = useMemo(() => {
+    const list = details?.sysmon_events || [];
+    const q = search.trim().toLowerCase();
+    if (!q) return list;
+    return list.filter((ev) => {
+      const msg = (ev.message || "").toLowerCase();
+      const type = (ev.event_type || "").toLowerCase();
+      const id = String(ev.event_id ?? "");
+      return msg.includes(q) || type.includes(q) || id.includes(q);
+    });
+  }, [details, search]);
+
+  function openJson(ev) {
+    // server ти враќа: { event_id, event_type, message, timestamp, details }
+    setJsonTitle(`Sysmon Event ${ev?.event_id ?? ""} • ${ev?.event_type ?? ""}`);
+    setJsonData(ev?.details ?? ev); // ако нема details, покажи цел event
+    setJsonOpen(true);
+  }
+
+  function closeJson() {
+    setJsonOpen(false);
+    setJsonTitle("");
+    setJsonData(null);
+  }
+
+  if (!open) return null;
+
+  return (
+    <>
+      <div className="backdrop" onMouseDown={onClose}>
+        <div className="modal" onMouseDown={(e) => e.stopPropagation()}>
+          <div className="modal-head">
+            <div>
+              <div className="modal-title">{computer.name}</div>
+              <div className="modal-sub">
+                {computer.ip || "—"} • {computer.user || "—"} • {computer.status || "—"}
+              </div>
+            </div>
+
+            <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
+              <div className="tabs">
+                <button className={`tab ${tab === "overview" ? "active" : ""}`} onClick={() => setTab("overview")}>
+                  Overview
+                </button>
+                <button className={`tab ${tab === "sysmon" ? "active" : ""}`} onClick={() => setTab("sysmon")}>
+                  Sysmon
+                </button>
+                <button className={`tab ${tab === "processes" ? "active" : ""}`} onClick={() => setTab("processes")}>
+                  Processes
+                </button>
+                <button className={`tab ${tab === "network" ? "active" : ""}`} onClick={() => setTab("network")}>
+                  Network
+                </button>
+              </div>
+              <button className="btn" onClick={onClose}>
+                Close
+              </button>
+            </div>
+          </div>
+
+          <div style={{ padding: 16 }}>
+            {loading && <div style={{ color: "#94a3b8" }}>Loading…</div>}
+
+            {!loading && details && tab === "overview" && (
+              <div className="panel">
+                <div className="panel-head">
+                  <div className="panel-title">System Overview</div>
+                  <button className="btn primary" onClick={() => onAskAI(computer.name)}>
+                    Ask AI about this PC
+                  </button>
+                </div>
+                <div className="panel-body" style={{ display: "grid", gap: 10 }}>
+                  <div>
+                    <b>User:</b> {details.computer?.user}
+                  </div>
+                  <div>
+                    <b>IP:</b> {details.computer?.ip}
+                  </div>
+                  <div>
+                    <b>OS:</b> {details.computer?.os}
+                  </div>
+                  <div>
+                    <b>First seen:</b> {details.computer?.first_seen}
+                  </div>
+                  <div>
+                    <b>Last seen:</b> {details.computer?.last_seen}
+                  </div>
+                  <div>
+                    <b>Total logs:</b> {details.computer?.total_logs}
+                  </div>
+                </div>
+              </div>
+            )}
+
+            {!loading && details && tab === "sysmon" && (
+              <div className="panel">
+                <div className="panel-head" style={{ alignItems: "center" }}>
+                  <div className="panel-title">Sysmon Events</div>
+                  <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
+                    <span className="pill">{filteredSysmon.length} events</span>
+                    <input
+                      className="input"
+                      style={{ maxWidth: 380 }}
+                      placeholder="Search by id / type / message…"
+                      value={search}
+                      onChange={(e) => setSearch(e.target.value)}
+                    />
+                  </div>
+                </div>
+
+                <div className="panel-body">
+                  <div className="table-wrap">
+                    <table>
+                      <thead>
+                        <tr>
+                          <th style={{ width: 90 }}>Time</th>
+                          <th style={{ width: 70 }}>ID</th>
+                          <th style={{ width: 170 }}>Type</th>
+                          <th style={{ width: 110 }}>Severity</th>
+                          <th>Message</th>
+                          <th style={{ width: 90 }}>View</th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                        {filteredSysmon.slice(0, 200).map((ev, idx) => {
+                          const sev = severityForEventId(ev.event_id);
+                          return (
+                            <tr key={idx}>
+                              <td>{ev.timestamp ? new Date(ev.timestamp).toLocaleTimeString() : "—"}</td>
+                              <td>
+                                <b>{ev.event_id}</b>
+                              </td>
+                              <td>{ev.event_type}</td>
+                              <td>
+                                <span className={`badge ${sev}`}>{sev.toUpperCase()}</span>
+                              </td>
+                              <td
+                                style={{
+                                  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
+                                  fontSize: 12,
+                                }}
+                              >
+                                {(ev.message || "").slice(0, 140)}
+                              </td>
+                              <td>
+                                <button className="btn" onClick={() => openJson(ev)}>
+                                  View
+                                </button>
+                              </td>
+                            </tr>
+                          );
+                        })}
+
+                        {filteredSysmon.length === 0 && (
+                          <tr>
+                            <td colSpan="6" style={{ color: "#94a3b8" }}>
+                              No sysmon events.
+                            </td>
+                          </tr>
+                        )}
+                      </tbody>
+                    </table>
+                  </div>
+
+                  {filteredSysmon.length > 200 && (
+                    <div style={{ marginTop: 10, color: "#94a3b8", fontSize: 12 }}>
+                      Showing first 200 (for speed).
+                    </div>
+                  )}
+                </div>
+              </div>
+            )}
+
+            {!loading && details && tab === "processes" && (
+              <div className="panel">
+                <div className="panel-head">
+                  <div className="panel-title">Recent Processes</div>
+                </div>
+                <div className="panel-body">
+                  <div className="table-wrap">
+                    <table>
+                      <thead>
+                        <tr>
+                          <th>Time</th>
+                          <th>PID</th>
+                          <th>Name</th>
+                          <th>User</th>
+                          <th>CPU%</th>
+                          <th>RAM MB</th>
+                          <th>Cmdline</th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                        {(details.recent_processes || []).slice(0, 150).map((p, idx) => (
+                          <tr key={idx}>
+                            <td>{p.timestamp ? new Date(p.timestamp).toLocaleTimeString() : "—"}</td>
+                            <td>
+                              <b>{p.pid}</b>
+                            </td>
+                            <td>{p.name}</td>
+                            <td>{p.username || "—"}</td>
+                            <td>{p.cpu_percent ?? 0}</td>
+                            <td>{p.memory_mb ?? 0}</td>
+                            <td
+                              style={{
+                                fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
+                                fontSize: 12,
+                              }}
+                            >
+                              {(p.cmdline || "").slice(0, 120)}
+                            </td>
+                          </tr>
+                        ))}
+                        {(details.recent_processes || []).length === 0 && (
+                          <tr>
+                            <td colSpan="7" style={{ color: "#94a3b8" }}>
+                              No process data.
+                            </td>
+                          </tr>
+                        )}
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
+              </div>
+            )}
+
+            {!loading && details && tab === "network" && (
+              <div className="panel">
+                <div className="panel-head">
+                  <div className="panel-title">Network Connections</div>
+                </div>
+                <div className="panel-body">
+                  <div className="table-wrap">
+                    <table>
+                      <thead>
+                        <tr>
+                          <th>Time</th>
+                          <th>PID</th>
+                          <th>Process</th>
+                          <th>Local</th>
+                          <th>Remote</th>
+                          <th>Status</th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                        {(details.network_connections || []).slice(0, 150).map((n, idx) => (
+                          <tr key={idx}>
+                            <td>{n.timestamp ? new Date(n.timestamp).toLocaleTimeString() : "—"}</td>
+                            <td>
+                              <b>{n.pid}</b>
+                            </td>
+                            <td>{n.process_name || "—"}</td>
+                            <td
+                              style={{
+                                fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
+                                fontSize: 12,
+                              }}
+                            >
+                              {n.local_address || "—"}
+                            </td>
+                            <td
+                              style={{
+                                fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
+                                fontSize: 12,
+                              }}
+                            >
+                              {n.remote_address || "—"}
+                            </td>
+                            <td>{n.status || "—"}</td>
+                          </tr>
+                        ))}
+                        {(details.network_connections || []).length === 0 && (
+                          <tr>
+                            <td colSpan="6" style={{ color: "#94a3b8" }}>
+                              No network data.
+                            </td>
+                          </tr>
+                        )}
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
+              </div>
+            )}
+
+            {!loading && !details && <div style={{ color: "#94a3b8" }}>No details.</div>}
+          </div>
+        </div>
+      </div>
+
+      {/* JSON VIEWER MODAL */}
+      {jsonOpen && (
+        <div className="backdrop" onMouseDown={closeJson}>
+          <div className="modal" onMouseDown={(e) => e.stopPropagation()} style={{ width: "min(900px, 96vw)" }}>
+            <div className="modal-head">
+              <div>
+                <div className="modal-title">View JSON</div>
+                <div className="modal-sub">{jsonTitle}</div>
+              </div>
+              <div style={{ display: "flex", gap: 10 }}>
+                <button
+                  className="btn"
+                  onClick={() => {
+                    const txt = JSON.stringify(jsonData ?? {}, null, 2);
+                    navigator.clipboard.writeText(txt);
+                  }}
+                >
+                  Copy
+                </button>
+                <button className="btn" onClick={closeJson}>
+                  Close
+                </button>
+              </div>
+            </div>
+            <div style={{ padding: 16 }}>
+              <pre
+                style={{
+                  margin: 0,
+                  maxHeight: "70vh",
+                  overflow: "auto",
+                  padding: 14,
+                  borderRadius: 12,
+                  border: "1px solid rgba(148,163,184,0.25)",
+                  background: "rgba(2,6,23,0.35)",
+                  color: "rgba(226,232,240,0.95)",
+                  fontSize: 12,
+                  lineHeight: 1.5,
+                }}
+              >
+                {JSON.stringify(jsonData ?? {}, null, 2)}
+              </pre>
+            </div>
+          </div>
+        </div>
+      )}
+    </>
+  );
+}
Index: lan-frontend/src/components/TopBar.jsx
===================================================================
--- lan-frontend/src/components/TopBar.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/src/components/TopBar.jsx	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,532 @@
+import React, { useEffect, useMemo, useState } from "react";
+
+export default function TopBar({ onOpenAI, onRefresh, lastUpdated, onEnvChange }) {
+  const [open, setOpen] = useState(false);
+
+  // admin auth
+  const [adminKey, setAdminKey] = useState("");
+  const [adminSession, setAdminSession] = useState("");
+
+  // env + tokens
+  const [envs, setEnvs] = useState(["default"]);
+  const [selectedEnv, setSelectedEnv] = useState("default");
+  const [newEnvName, setNewEnvName] = useState("");
+  const [token, setToken] = useState("");
+
+  const [busy, setBusy] = useState(false);
+  const [error, setError] = useState("");
+
+  // helper: fetch env list after login
+  async function loadEnvironments(session) {
+    const r = await fetch("/api/admin/environments", {
+      headers: { "X-Admin-Session": session },
+    });
+    const data = await r.json();
+    if (!r.ok) throw new Error(data?.error || `HTTP ${r.status}`);
+
+    const list = data.environments?.length ? data.environments : ["default"];
+    setEnvs(list);
+
+    // ако тековниот не постои, стави првиот од листата
+    if (!list.includes(selectedEnv)) {
+      setSelectedEnv(list[0]);
+      onEnvChange?.(list[0]);
+    }
+  }
+
+  async function login() {
+    setError("");
+    setToken("");
+
+    if (!adminKey.trim()) {
+      setError("Внеси admin key.");
+      return;
+    }
+
+    setBusy(true);
+    try {
+      const r = await fetch("/api/admin/login", {
+        method: "POST",
+        headers: { "Content-Type": "application/json" },
+        body: JSON.stringify({ admin_key: adminKey.trim() }),
+      });
+      const data = await r.json();
+      if (!r.ok) throw new Error(data?.error || `HTTP ${r.status}`);
+
+      const session = data.session;
+      setAdminSession(session);
+      await loadEnvironments(session);
+    } catch (e) {
+      setError(String(e.message || e));
+    } finally {
+      setBusy(false);
+    }
+  }
+
+  function logout() {
+    setAdminSession("");
+    setAdminKey("");
+    setToken("");
+    setError("");
+    setEnvs(["default"]);
+    setSelectedEnv("default");
+    onEnvChange?.("default");
+  }
+
+  async function createEnvironment() {
+    setError("");
+    setToken("");
+    if (!newEnvName.trim()) {
+      setError("Внеси име за environment.");
+      return;
+    }
+
+    setBusy(true);
+    try {
+      const r = await fetch("/api/admin/environments", {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+          "X-Admin-Session": adminSession,
+        },
+        body: JSON.stringify({ name: newEnvName.trim() }),
+      });
+
+      const data = await r.json();
+      if (!r.ok) throw new Error(data?.error || `HTTP ${r.status}`);
+
+      // reload list, select new env
+      await loadEnvironments(adminSession);
+      setSelectedEnv(newEnvName.trim());
+      onEnvChange?.(newEnvName.trim());
+      setNewEnvName("");
+    } catch (e) {
+      setError(String(e.message || e));
+    } finally {
+      setBusy(false);
+    }
+  }
+
+  async function generateToken() {
+    setError("");
+    setToken("");
+    if (!selectedEnv) {
+      setError("Избери environment.");
+      return;
+    }
+
+    setBusy(true);
+    try {
+      const r = await fetch("/api/admin/tokens", {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+          "X-Admin-Session": adminSession,
+        },
+        body: JSON.stringify({ env: selectedEnv }),
+      });
+
+      const data = await r.json();
+      if (!r.ok) throw new Error(data?.error || `HTTP ${r.status}`);
+
+      setToken(data.token || "");
+    } catch (e) {
+      setError(String(e.message || e));
+    } finally {
+      setBusy(false);
+    }
+  }
+
+  async function copyToken() {
+    if (!token) return;
+    await navigator.clipboard.writeText(token);
+  }
+
+  function changeEnv(val) {
+    setSelectedEnv(val);
+    setToken("");
+    onEnvChange?.(val);
+  }
+
+  const prettyTime = useMemo(() => {
+    if (!lastUpdated) return "Loading…";
+    try {
+      return `Updated ${new Date(lastUpdated).toLocaleTimeString()}`;
+    } catch {
+      return "Updated";
+    }
+  }, [lastUpdated]);
+
+  return (
+    <>
+      <style>{`
+        /* ---- TopBar scoped theme ---- */
+        .tb-wrap{
+          position: sticky;
+          top: 0;
+          z-index: 20;
+          backdrop-filter: blur(10px);
+          background: linear-gradient(180deg, rgba(10,16,32,0.92), rgba(10,16,32,0.75));
+          border-bottom: 1px solid rgba(96,165,250,0.18);
+        }
+        .tb-inner{
+          max-width: 1200px;
+          margin: 0 auto;
+          padding: 14px 18px;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          gap: 14px;
+        }
+
+        .tb-brand{
+          display:flex;
+          align-items:center;
+          gap: 12px;
+          min-width: 260px;
+        }
+        .tb-badge{
+          width: 44px;
+          height: 44px;
+          border-radius: 14px;
+          display:flex;
+          align-items:center;
+          justify-content:center;
+          background:
+            radial-gradient(120px 80px at 30% 20%, rgba(96,165,250,0.55), transparent 60%),
+            linear-gradient(135deg, rgba(30,64,175,0.75), rgba(15,23,42,0.85));
+          border: 1px solid rgba(96,165,250,0.25);
+          box-shadow: 0 10px 25px rgba(0,0,0,0.25);
+        }
+        .tb-title{
+          margin:0;
+          font-size: 16px;
+          font-weight: 900;
+          letter-spacing: 0.4px;
+          color: rgba(255,255,255,0.95);
+          line-height: 1.2;
+        }
+        .tb-sub{
+          margin: 2px 0 0 0;
+          font-size: 12px;
+          color: rgba(191,219,254,0.85);
+        }
+        .tb-sub span{
+          color: rgba(96,165,250,0.95);
+          font-weight: 800;
+        }
+
+        .tb-actions{
+          display:flex;
+          align-items:center;
+          gap: 10px;
+          flex-wrap: wrap;
+          justify-content: flex-end;
+        }
+
+        .tb-pill{
+          display:inline-flex;
+          align-items:center;
+          gap: 8px;
+          padding: 8px 12px;
+          border-radius: 999px;
+          background: rgba(30,41,59,0.65);
+          border: 1px solid rgba(96,165,250,0.22);
+          color: rgba(255,255,255,0.9);
+          font-weight: 800;
+          font-size: 12px;
+          box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
+        }
+        .tb-pill .dot{
+          width: 8px;
+          height: 8px;
+          border-radius: 999px;
+          background: rgba(96,165,250,0.95);
+          box-shadow: 0 0 0 3px rgba(96,165,250,0.15);
+        }
+
+        .tb-btn{
+          appearance: none;
+          border: 1px solid rgba(96,165,250,0.22);
+          background: rgba(15,23,42,0.55);
+          color: rgba(255,255,255,0.9);
+          padding: 9px 12px;
+          border-radius: 12px;
+          font-weight: 800;
+          font-size: 13px;
+          cursor: pointer;
+          transition: transform .12s ease, background .12s ease, border-color .12s ease, opacity .12s ease;
+          box-shadow: 0 8px 20px rgba(0,0,0,0.18);
+        }
+        .tb-btn:hover{
+          transform: translateY(-1px);
+          background: rgba(30,41,59,0.65);
+          border-color: rgba(96,165,250,0.38);
+        }
+        .tb-btn:disabled{
+          opacity: .6;
+          cursor: not-allowed;
+          transform: none;
+        }
+        .tb-btn.primary{
+          background: linear-gradient(135deg, rgba(37,99,235,0.95), rgba(56,189,248,0.7));
+          border-color: rgba(147,197,253,0.35);
+          color: rgba(5,12,24,0.95);
+        }
+        .tb-btn.primary:hover{
+          background: linear-gradient(135deg, rgba(59,130,246,0.95), rgba(34,211,238,0.75));
+        }
+
+        /* ---- Modal ---- */
+        .tb-backdrop{
+          position: fixed;
+          inset: 0;
+          background: rgba(2,6,23,0.72);
+          backdrop-filter: blur(6px);
+          display:flex;
+          align-items: center;
+          justify-content: center;
+          padding: 20px;
+          z-index: 50;
+        }
+        .tb-modal{
+          width: min(860px, 96vw);
+          border-radius: 18px;
+          background:
+            radial-gradient(900px 420px at 20% 0%, rgba(96,165,250,0.18), transparent 60%),
+            radial-gradient(700px 350px at 80% 10%, rgba(34,211,238,0.10), transparent 55%),
+            rgba(10,16,32,0.92);
+          border: 1px solid rgba(96,165,250,0.22);
+          box-shadow: 0 30px 80px rgba(0,0,0,0.45);
+          overflow: hidden;
+        }
+        .tb-modal-head{
+          padding: 16px 18px;
+          display:flex;
+          align-items: center;
+          justify-content: space-between;
+          gap: 10px;
+          border-bottom: 1px solid rgba(96,165,250,0.18);
+        }
+        .tb-modal-title{
+          color: rgba(255,255,255,0.96);
+          font-weight: 900;
+          letter-spacing: .3px;
+        }
+        .tb-modal-sub{
+          color: rgba(191,219,254,0.8);
+          font-size: 12px;
+          margin-top: 2px;
+        }
+        .tb-grid{
+          padding: 16px;
+          display:grid;
+          gap: 12px;
+        }
+        .tb-card{
+          border-radius: 16px;
+          background: rgba(15,23,42,0.55);
+          border: 1px solid rgba(96,165,250,0.18);
+          padding: 14px;
+        }
+        .tb-card-head{
+          display:flex;
+          align-items:center;
+          justify-content: space-between;
+          margin-bottom: 10px;
+        }
+        .tb-card-title{
+          color: rgba(255,255,255,0.92);
+          font-weight: 900;
+        }
+        .tb-help{
+          color: rgba(148,163,184,0.95);
+          font-size: 12px;
+          margin-top: 8px;
+          line-height: 1.35;
+        }
+        .tb-input, .tb-select{
+          width: 100%;
+          padding: 10px 12px;
+          border-radius: 12px;
+          border: 1px solid rgba(96,165,250,0.18);
+          background: rgba(2,6,23,0.35);
+          color: rgba(255,255,255,0.92);
+          outline: none;
+          font-weight: 700;
+        }
+        .tb-input::placeholder{ color: rgba(148,163,184,0.75); }
+        .tb-row{
+          display:grid;
+          gap: 10px;
+        }
+        .tb-token{
+          display:flex;
+          align-items:center;
+          justify-content: space-between;
+          gap: 10px;
+          padding: 10px 12px;
+          border-radius: 14px;
+          background: rgba(2,6,23,0.38);
+          border: 1px dashed rgba(147,197,253,0.32);
+          color: rgba(255,255,255,0.92);
+        }
+        .tb-mono{
+          font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+          font-weight: 800;
+          color: rgba(191,219,254,0.95);
+        }
+        .tb-error{
+          border-color: rgba(239,68,68,0.45) !important;
+          background: rgba(127,29,29,0.15) !important;
+          color: rgba(254,202,202,0.95);
+        }
+      `}</style>
+
+      {/* TOPBAR */}
+      <div className="tb-wrap">
+        <div className="tb-inner">
+          <div className="tb-brand">
+            <div className="tb-badge">🛡️</div>
+            <div>
+              <h1 className="tb-title">LAN Security Monitor</h1>
+              <p className="tb-sub">
+                Sysmon Edition • <span>{prettyTime}</span>
+              </p>
+            </div>
+          </div>
+
+          <div className="tb-actions">
+            <span className="tb-pill">
+              <span className="dot" />
+              Env: {selectedEnv}
+            </span>
+            <button className="tb-btn" onClick={onRefresh} disabled={busy}>Refresh</button>
+            <button className="tb-btn" onClick={() => setOpen(true)}>Admin</button>
+            <button className="tb-btn primary" onClick={onOpenAI}>AI Assistant</button>
+          </div>
+        </div>
+      </div>
+
+      {/* MODAL */}
+      {open && (
+        <div className="tb-backdrop" onMouseDown={() => setOpen(false)}>
+          <div className="tb-modal" onMouseDown={(e) => e.stopPropagation()}>
+            <div className="tb-modal-head">
+              <div>
+                <div className="tb-modal-title">Admin Panel</div>
+                <div className="tb-modal-sub">Login ➜ Environments ➜ Generate token</div>
+              </div>
+              <button className="tb-btn" onClick={() => setOpen(false)}>Close</button>
+            </div>
+
+            <div className="tb-grid">
+              {/* 1) LOGIN */}
+              {!adminSession ? (
+                <div className="tb-card">
+                  <div className="tb-card-head">
+                    <div className="tb-card-title">Step 1: Admin login</div>
+                  </div>
+
+                  <div className="tb-row">
+                    <input
+                      className="tb-input"
+                      type="password"
+                      value={adminKey}
+                      onChange={(e) => setAdminKey(e.target.value)}
+                      placeholder="Enter admin key"
+                    />
+                    <button className="tb-btn primary" onClick={login} disabled={busy}>
+                      {busy ? "Logging in…" : "Login"}
+                    </button>
+                    <div className="tb-help">
+                      Admin key е само за логирање. После тоа користиме <b>session token</b>.
+                    </div>
+                  </div>
+                </div>
+              ) : (
+                <>
+                  {/* 2) ENV SELECT */}
+                  <div className="tb-card">
+                    <div className="tb-card-head">
+                      <div className="tb-card-title">Step 2: Choose environment</div>
+                      <button className="tb-btn" onClick={logout}>Logout</button>
+                    </div>
+
+                    <div className="tb-row">
+                      <select className="tb-select" value={selectedEnv} onChange={(e) => changeEnv(e.target.value)}>
+                        {envs.map((e) => (
+                          <option key={e} value={e}>{e}</option>
+                        ))}
+                      </select>
+                      <div className="tb-help">
+                        Овој env ќе се користи за dashboard филтрирање + token generation.
+                      </div>
+                    </div>
+                  </div>
+
+                  {/* 3) CREATE ENV */}
+                  <div className="tb-card">
+                    <div className="tb-card-head">
+                      <div className="tb-card-title">Create new environment</div>
+                    </div>
+
+                    <div className="tb-row">
+                      <input
+                        className="tb-input"
+                        value={newEnvName}
+                        onChange={(e) => setNewEnvName(e.target.value)}
+                        placeholder="e.g. school-lab / staging"
+                      />
+                      <button className="tb-btn" onClick={createEnvironment} disabled={busy}>
+                        {busy ? "Creating…" : "Create"}
+                      </button>
+                    </div>
+                  </div>
+
+                  {/* 4) TOKEN */}
+                  <div className="tb-card">
+                    <div className="tb-card-head">
+                      <div className="tb-card-title">Step 3: Generate token (for clients)</div>
+                    </div>
+
+                    <div className="tb-row">
+                      <button className="tb-btn primary" onClick={generateToken} disabled={busy}>
+                        {busy ? "Generating…" : `Generate token for "${selectedEnv}"`}
+                      </button>
+
+                      {token && (
+                        <>
+                          <div className="tb-token">
+                            <div>
+                              <div style={{ fontWeight: 900, color: "rgba(255,255,255,0.92)" }}>Token</div>
+                              <div className="tb-mono">
+                                {token.slice(0, 12)}…{token.slice(-10)}
+                              </div>
+                            </div>
+                            <button className="tb-btn" onClick={copyToken}>Copy</button>
+                          </div>
+
+                          <div className="tb-help">
+                            Клиентот мора да праќа header: <b>X-Env-Token</b> со овој token на <b>/receive</b>.
+                          </div>
+                        </>
+                      )}
+                    </div>
+                  </div>
+                </>
+              )}
+
+              {error && (
+                <div className="tb-card tb-error">
+                  <div style={{ fontWeight: 900 }}>Error</div>
+                  <div style={{ marginTop: 6 }}>{error}</div>
+                </div>
+              )}
+            </div>
+          </div>
+        </div>
+      )}
+    </>
+  );
+}
Index: lan-frontend/src/index.css
===================================================================
--- lan-frontend/src/index.css	(revision 640ed89213ad0086dc8379de00b8d102057ead1f)
+++ lan-frontend/src/index.css	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -1563,2 +1563,6 @@
   background: var(--secondary-color);
 }
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
Index: lan-frontend/src/styles/theme.css
===================================================================
--- lan-frontend/src/styles/theme.css	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/src/styles/theme.css	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,403 @@
+/* =========================
+   LAN Security Monitor Theme
+   Dark Navy + Light Blue
+   ========================= */
+
+:root{
+  --bg0: #070b16;
+  --bg1: #0a1020;
+  --bg2: #0e1730;
+
+  --panel: rgba(15,23,42,0.62);
+  --panel2: rgba(2,6,23,0.35);
+
+  --border: rgba(96,165,250,0.18);
+  --border2: rgba(147,197,253,0.28);
+
+  --text: rgba(255,255,255,0.92);
+  --muted: rgba(191,219,254,0.82);
+  --dim: rgba(148,163,184,0.85);
+
+  --accent: rgba(96,165,250,0.95);   /* light blue */
+  --accent2: rgba(34,211,238,0.80);  /* cyan */
+  --danger: rgba(239,68,68,0.95);
+  --warn: rgba(245,158,11,0.95);
+  --ok: rgba(34,197,94,0.95);
+
+  --shadow: 0 18px 45px rgba(0,0,0,0.32);
+  --shadow2: 0 10px 25px rgba(0,0,0,0.22);
+
+  --r12: 12px;
+  --r16: 16px;
+  --r18: 18px;
+}
+
+*{ box-sizing: border-box; }
+html, body { height: 100%; }
+body{
+  margin:0;
+  color: var(--text);
+  background:
+    radial-gradient(900px 460px at 20% -10%, rgba(96,165,250,0.18), transparent 60%),
+    radial-gradient(800px 420px at 80% 0%, rgba(34,211,238,0.10), transparent 55%),
+    radial-gradient(1000px 700px at 50% 120%, rgba(30,64,175,0.16), transparent 60%),
+    linear-gradient(180deg, var(--bg0), var(--bg1) 45%, #060913 100%);
+  font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
+}
+
+/* Layout */
+.app-wrap{
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 16px 18px 40px;
+}
+
+/* =========================
+   Panels
+   ========================= */
+.panel{
+  background: var(--panel);
+  border: 1px solid var(--border);
+  border-radius: var(--r18);
+  box-shadow: var(--shadow2);
+  overflow: hidden;
+  backdrop-filter: blur(10px);
+}
+.panel-head{
+  display:flex;
+  align-items:center;
+  justify-content: space-between;
+  gap: 12px;
+  padding: 14px 16px;
+  border-bottom: 1px solid rgba(96,165,250,0.14);
+  background: linear-gradient(180deg, rgba(2,6,23,0.32), rgba(2,6,23,0.12));
+}
+.panel-title{
+  font-weight: 900;
+  letter-spacing: 0.3px;
+  color: rgba(255,255,255,0.95);
+}
+.panel-body{
+  padding: 16px;
+}
+
+/* small helpers */
+.pill{
+  display:inline-flex;
+  align-items:center;
+  gap: 10px;
+  padding: 8px 12px;
+  border-radius: 999px;
+  background: rgba(30,41,59,0.60);
+  border: 1px solid rgba(96,165,250,0.22);
+  color: rgba(255,255,255,0.92);
+  font-weight: 800;
+  font-size: 12px;
+  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
+}
+.small{
+  font-size: 12px;
+  color: var(--dim);
+  line-height: 1.35;
+}
+.dim{ color: var(--dim); font-weight: 700; }
+
+/* =========================
+   Inputs / Selects / Buttons
+   ========================= */
+.input, .select{
+  width: 100%;
+  padding: 10px 12px;
+  border-radius: var(--r12);
+  border: 1px solid rgba(96,165,250,0.18);
+  background: rgba(2,6,23,0.35);
+  color: rgba(255,255,255,0.92);
+  outline: none;
+  font-weight: 700;
+  transition: border-color .12s ease, background .12s ease, transform .12s ease;
+}
+.input::placeholder{ color: rgba(148,163,184,0.75); }
+.input:focus, .select:focus{
+  border-color: rgba(96,165,250,0.42);
+  background: rgba(2,6,23,0.46);
+}
+
+.btn{
+  appearance: none;
+  border: 1px solid rgba(96,165,250,0.22);
+  background: rgba(15,23,42,0.55);
+  color: rgba(255,255,255,0.92);
+  padding: 9px 12px;
+  border-radius: var(--r12);
+  font-weight: 900;
+  font-size: 13px;
+  cursor: pointer;
+  transition: transform .12s ease, background .12s ease, border-color .12s ease, opacity .12s ease;
+  box-shadow: 0 8px 20px rgba(0,0,0,0.18);
+}
+.btn:hover{
+  transform: translateY(-1px);
+  background: rgba(30,41,59,0.65);
+  border-color: rgba(96,165,250,0.38);
+}
+.btn:disabled{
+  opacity: .6;
+  cursor: not-allowed;
+  transform: none;
+}
+.btn.primary{
+  background: linear-gradient(135deg, rgba(37,99,235,0.95), rgba(56,189,248,0.70));
+  border-color: rgba(147,197,253,0.35);
+  color: rgba(5,12,24,0.95);
+}
+.btn.primary:hover{
+  background: linear-gradient(135deg, rgba(59,130,246,0.95), rgba(34,211,238,0.75));
+}
+
+/* Filters row */
+.filters{
+  display:grid;
+  grid-template-columns: 1.6fr 0.6fr auto;
+  gap: 10px;
+}
+@media (max-width: 820px){
+  .filters{ grid-template-columns: 1fr; }
+}
+
+/* =========================
+   Stats grid
+   ========================= */
+.stats-grid{
+  display: grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 12px;
+  margin: 14px 0;
+}
+@media (max-width: 900px){
+  .stats-grid{ grid-template-columns: 1fr; }
+}
+
+.stat-card{
+  background:
+    radial-gradient(500px 180px at 20% 0%, rgba(96,165,250,0.16), transparent 55%),
+    rgba(15,23,42,0.58);
+  border: 1px solid rgba(96,165,250,0.18);
+  border-radius: var(--r18);
+  padding: 14px 16px;
+  box-shadow: var(--shadow2);
+  backdrop-filter: blur(10px);
+}
+.stat-card .label{
+  color: var(--dim);
+  font-size: 12px;
+  font-weight: 900;
+  letter-spacing: .8px;
+  text-transform: uppercase;
+}
+.stat-card .value{
+  font-size: 28px;
+  font-weight: 1000;
+  margin: 8px 0 2px;
+  color: rgba(255,255,255,0.95);
+}
+.stat-card .sub{
+  color: var(--muted);
+  font-size: 12px;
+}
+
+/* =========================
+   Computers grid + Card
+   ========================= */
+.computers-grid{
+  display:grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 12px;
+}
+@media (max-width: 980px){
+  .computers-grid{ grid-template-columns: 1fr; }
+}
+
+.card{
+  width:100%;
+  text-align:left;
+  border-radius: var(--r18);
+  border: 1px solid rgba(96,165,250,0.18);
+  background:
+    radial-gradient(520px 220px at 20% 0%, rgba(96,165,250,0.14), transparent 55%),
+    rgba(15,23,42,0.54);
+  box-shadow: var(--shadow2);
+  padding: 14px;
+  cursor: pointer;
+  transition: transform .14s ease, border-color .14s ease, background .14s ease;
+  backdrop-filter: blur(10px);
+}
+.card:hover{
+  transform: translateY(-3px);
+  border-color: rgba(96,165,250,0.38);
+  background:
+    radial-gradient(520px 220px at 20% 0%, rgba(34,211,238,0.12), transparent 55%),
+    rgba(15,23,42,0.62);
+}
+.card:active{ transform: translateY(-1px); }
+
+.card-head{
+  display:flex;
+  justify-content: space-between;
+  align-items:center;
+  gap: 10px;
+  margin-bottom: 10px;
+}
+.card-title{
+  font-weight: 1000;
+  color: rgba(255,255,255,0.95);
+  letter-spacing: .2px;
+}
+.dot{
+  width: 10px;
+  height: 10px;
+  border-radius: 999px;
+  box-shadow: 0 0 0 4px rgba(255,255,255,0.04);
+}
+.dot.online{ background: var(--ok); box-shadow: 0 0 0 4px rgba(34,197,94,0.12); }
+.dot.idle{ background: var(--warn); box-shadow: 0 0 0 4px rgba(245,158,11,0.12); }
+.dot.offline{ background: rgba(148,163,184,0.8); box-shadow: 0 0 0 4px rgba(148,163,184,0.10); }
+
+.meta{
+  display:grid;
+  gap: 6px;
+  color: rgba(226,232,240,0.92);
+  font-size: 13px;
+  line-height: 1.25;
+}
+
+.kpis{
+  display:grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 10px;
+  margin-top: 12px;
+  padding-top: 12px;
+  border-top: 1px solid rgba(96,165,250,0.14);
+}
+.kpi{
+  border-radius: var(--r16);
+  border: 1px solid rgba(96,165,250,0.14);
+  background: rgba(2,6,23,0.30);
+  padding: 10px 10px;
+}
+.kpi .k{
+  font-size: 11px;
+  color: var(--dim);
+  font-weight: 900;
+  letter-spacing: .6px;
+  text-transform: uppercase;
+}
+.kpi .v{
+  margin-top: 6px;
+  font-size: 18px;
+  font-weight: 1000;
+  color: rgba(255,255,255,0.95);
+}
+
+/* =========================
+   Modal (shared classes)
+   ========================= */
+.backdrop{
+  position: fixed;
+  inset: 0;
+  background: rgba(2,6,23,0.72);
+  backdrop-filter: blur(6px);
+  display:flex;
+  align-items:center;
+  justify-content:center;
+  padding: 20px;
+  z-index: 60;
+}
+.modal{
+  width: min(920px, 96vw);
+  border-radius: var(--r18);
+  background:
+    radial-gradient(900px 420px at 20% 0%, rgba(96,165,250,0.18), transparent 60%),
+    radial-gradient(700px 350px at 80% 10%, rgba(34,211,238,0.10), transparent 55%),
+    rgba(10,16,32,0.92);
+  border: 1px solid rgba(96,165,250,0.22);
+  box-shadow: 0 30px 80px rgba(0,0,0,0.45);
+  overflow: hidden;
+}
+.modal-head{
+  padding: 14px 16px;
+  display:flex;
+  align-items:center;
+  justify-content: space-between;
+  gap: 10px;
+  border-bottom: 1px solid rgba(96,165,250,0.18);
+}
+.modal-title{
+  font-weight: 1000;
+  color: rgba(255,255,255,0.95);
+  letter-spacing: .2px;
+}
+.modal-sub{
+  margin-top: 2px;
+  font-size: 12px;
+  color: var(--muted);
+}
+
+/* If you have drawer classes */
+.ai-drawer{
+  position: fixed;
+  top: 0;
+  right: 0;
+  height: 100vh;
+  width: min(420px, 92vw);
+  background:
+    radial-gradient(700px 400px at 20% 0%, rgba(96,165,250,0.18), transparent 55%),
+    rgba(10,16,32,0.94);
+  border-left: 1px solid rgba(96,165,250,0.20);
+  box-shadow: -20px 0 60px rgba(0,0,0,0.35);
+  z-index: 70;
+  padding: 14px;
+}
+.ai-header{
+  font-weight: 1000;
+  color: rgba(255,255,255,0.95);
+  margin-bottom: 10px;
+}
+.ai-messages{
+  height: calc(100vh - 160px);
+  overflow: auto;
+  border-radius: var(--r16);
+  border: 1px solid rgba(96,165,250,0.14);
+  background: rgba(2,6,23,0.28);
+  padding: 10px;
+}
+.ai-msg{
+  color: rgba(226,232,240,0.92);
+  font-size: 13px;
+  line-height: 1.35;
+  padding: 10px;
+  border-radius: 12px;
+  border: 1px solid rgba(96,165,250,0.12);
+  background: rgba(15,23,42,0.45);
+  margin-bottom: 10px;
+}
+.ai-input{
+  width: 100%;
+  margin-top: 10px;
+  padding: 10px 12px;
+  border-radius: var(--r12);
+  border: 1px solid rgba(96,165,250,0.18);
+  background: rgba(2,6,23,0.35);
+  color: rgba(255,255,255,0.92);
+  outline: none;
+  font-weight: 700;
+}
+.ai-input::placeholder{ color: rgba(148,163,184,0.75); }
+
+/* Scrollbars (nice) */
+*::-webkit-scrollbar{ width: 10px; height: 10px; }
+*::-webkit-scrollbar-thumb{
+  background: rgba(96,165,250,0.20);
+  border: 2px solid rgba(2,6,23,0.35);
+  border-radius: 999px;
+}
+*::-webkit-scrollbar-track{ background: rgba(2,6,23,0.25); }
Index: lan-frontend/tailwind.config.js
===================================================================
--- lan-frontend/tailwind.config.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
+++ lan-frontend/tailwind.config.js	(revision 2058e5c694bb6097866a5fe6503c519e8f74970f)
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+  content: [
+    "./index.html",
+    "./src/**/*.{js,ts,jsx,tsx}",
+  ],
+  theme: {
+    extend: {},
+  },
+  plugins: [],
+}
