{"uuid": "19ea3f7b-c231-4189-8041-105a31b6916d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2024-21412", "type": "seen", "source": "https://gist.github.com/ShaiOnionGod/bb378bb47a2626f2a0b85bb402724549", "content": "\n\n  \n    \n    \n    Axonius \u2014 Dashboard prototype\n    \n    \n      {\n        \"imports\": {\n          \"react\": \"https://esm.sh/react@18.3.1\",\n          \"react-dom\": \"https://esm.sh/react-dom@18.3.1\",\n          \"react-dom/client\": \"https://esm.sh/react-dom@18.3.1/client\",\n          \"lucide-react\": \"https://esm.sh/lucide-react@0.456.0?deps=react@18.3.1\"\n        }\n      }\n    \n    \n    \n      html, body { margin: 0; height: 100%; background: #191A23; font-family: 'Hanken Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; }\n      #root { height: 100%; }\n      .sb-tag { position: fixed; right: 14px; bottom: 12px; z-index: 99999; font: 700 10px/1 ui-monospace, monospace; letter-spacing: .14em; color: #0D5ED7; background: rgba(255,255,255,.9); border: 1px solid #E0E4EC; border-radius: 999px; padding: 6px 10px; box-shadow: 0 6px 18px rgba(27,32,70,.12); pointer-events: none; }\n    \n  \n  \n    \n\n    \nSANDBOX\n\n    \nimport React, { useState, useRef, useCallback, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { createPortal } from \"react-dom\";\nimport {\n  Search, Star, MoreHorizontal, Info, ListFilter, ArrowUpDown, Columns3,\n  Plus, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, ChevronDown,\n  ArrowUpRight, ArrowDownRight, LayoutGrid, Server, ShieldCheck, Boxes,\n  Workflow, Cable, Bell, CircleHelp, Clock, Play, Grip, PanelLeftClose,\n  PanelLeftOpen, Download, GripVertical, X, Settings, ShieldAlert, Crosshair,\n  Pin, PinOff, RotateCw, Filter, Table2, ChevronsUpDown, Pencil, Check, Trash2,\n  Users, Layers, RotateCcw, Save, Copy, Calendar, Menu, Zap, Link, BarChart3, Sun, Moon,\n  Globe, Lock, Folder, FolderOpen\n} from \"lucide-react\";\n\n/**\n * Axonius \u2014 Dashboard + Assets table views\n * Collapsible side panel \u00b7 resizable + reorderable dashboard tiles\n * Real Axonius header \u00b7 Twenty-style table \u00b7 tokens from Figma source\n */\n\nfunction AxoniusLogo() {\n  return `.replaceAll('fill=\"black\"', `fill=\"${T.ink}\"`) }} /&gt;;\n}\n\n// Data / highlight palette \u2014 bright fills used in both themes (light-mode text variants applied where colored text is needed)\nconst VIZ = { green: \"#5BC4BF\", greenDeep: \"#247D78\", mint: \"#8ED0FF\", lilac: \"#AD85FF\", lilacPale: \"#CDB4FF\", orange: \"#FF8C66\", amber: \"#FFB286\" };\nconst FONT = \"'Hanken Grotesk', -apple-system, sans-serif\", DISPLAY = \"'Schibsted Grotesk', -apple-system, sans-serif\";\nconst THEMES = {\n  dark: {\n    ink: \"#E4E8F0\", body: \"#AEB7CC\", muted: \"#8494B5\", faint: \"#69738C\",\n    line: \"rgba(132,148,181,0.20)\", lineSoft: \"rgba(132,148,181,0.12)\", hair: \"rgba(132,148,181,0.09)\", lineStrong: \"rgba(132,148,181,0.42)\",\n    bg100: \"#AEB7CC\", bg90: \"#9AA3BC\", bg80: \"#8494B5\", bg40: \"#8494B5\",\n    blue: \"#5C7CFF\", blueDeep: \"#4E6CF5\", green: \"#5BC4BF\", red: \"#FF8C66\",\n    shell: \"#191A23\", headerBg: \"#191A23\", canvas: \"#191A23\", surface: \"#222431\", surface2: \"#2A2D3C\", control: \"#262838\",\n    white: \"#FFFFFF\", onAccent: \"#191A23\", isLight: false, coin: \"#2A2D3C\", accentSoft: \"rgba(92,124,255,0.16)\", accentText: \"#5C7CFF\", accentBorder: \"rgba(92,124,255,0.50)\",\n    viz: VIZ, shadow: \"0 1px 2px rgba(0,0,0,0.4), 0 14px 34px rgba(0,0,0,0.5)\", font: FONT, display: DISPLAY,\n  },\n  light: {\n    ink: \"#2E3850\", body: \"#465472\", muted: \"#6B7894\", faint: \"#8494B5\",\n    line: \"rgba(132,148,181,0.30)\", lineSoft: \"rgba(132,148,181,0.18)\", hair: \"rgba(132,148,181,0.12)\", lineStrong: \"rgba(132,148,181,0.55)\",\n    bg100: \"#465472\", bg90: \"#5A6889\", bg80: \"#8494B5\", bg40: \"#8494B5\",\n    blue: \"#4361EE\", blueDeep: \"#3A55D6\", green: \"#247D78\", red: \"#BF4B26\",\n    shell: \"#F9FAFB\", headerBg: \"#F9FAFB\", canvas: \"#FFFFFF\", surface: \"#FFFFFF\", surface2: \"#F1F2F6\", control: \"#FFFFFF\",\n    white: \"#FFFFFF\", onAccent: \"#FFFFFF\", isLight: true, coin: \"#FFFFFF\", accentSoft: \"rgba(67,97,238,0.10)\", accentText: \"#4361EE\", accentBorder: \"rgba(67,97,238,0.45)\",\n    viz: VIZ, shadow: \"0 1px 2px rgba(70,84,114,0.06), 0 10px 26px rgba(70,84,114,0.08)\", font: FONT, display: DISPLAY,\n  },\n};\nlet T = THEMES.dark;\n// Severity scale stays semantic (red \u2192 orange \u2192 yellow \u2192 green, neutral for informational) in both themes\nconst SEVC = { critical: \"#E5484D\", high: \"#F0743E\", medium: \"#F5C28C\", low: \"#4F8FE3\", info: \"#CFC4F2\" };\nconst fmt = (n) =&gt; n.toLocaleString(\"en-US\");\nconst tnum = { fontFeatureSettings: '\"tnum\" 1', fontVariantNumeric: \"tabular-nums\" };\n\n/* ============ vendor logos ============ */\nfunction LogoMark({ brand, size = 32 }) {\n  const glyph = {\n    aws: aws,\n    microsoft: ,\n    azure: ,\n    googlecloud: ,\n    oracle: ,\n    vmware: vm,\n    okta: ,\n    crowdstrike: ,\n    active_directory: ,\n    sentinelone: ,\n    service_now: ,\n    cisco: cisco,\n    cisco_meraki: ,\n    cisco_ise: cisco,\n    checkpoint: ,\n    chef: ,\n    claroty: ,\n    cylance: ,\n    epo: ,\n    paloalto: ,\n    tanium: ,\n    tenable: ,\n    forescout: ,\n    zoom: ,\n    miro: ,\n    dropbox: ,\n    salesforce: ,\n    slack: ,\n    office365: ,\n    google_workspace: ,\n  }[brand] || {(brand || \"?\").slice(0, 2).toUpperCase()};\n  return \n{glyph};\n}\n\n/* ============ chips ============ */\n// Tags use the dashboard data palette (turquoise / purple / baby-blue / orange) \u2014 dark text variant in light mode, bright in dark\nconst TAGV = [{ b: \"#5BC4BF\", d: \"#247D78\" }, { b: \"#AD85FF\", d: \"#683CB5\" }, { b: \"#8ED0FF\", d: \"#1E75B3\" }, { b: \"#FF8C66\", d: \"#BF4B26\" }];\nfunction Tag({ label }) {\n  let n = 0; for (let i = 0; i &lt; label.length; i++) n = (n * 31 + label.charCodeAt(i)) &gt;&gt;&gt; 0;\n  const t = TAGV[n % TAGV.length];\n  return {label};\n}\n// Cursor-following tooltip (like sandbox 2) \u2014 surface card with readable dark text\nfunction FloatTip({ x, y, children }) {\n  return createPortal(\n{children}, document.body);\n}\nfunction UserAvatar({ name, size = 24 }) {\n  const s = name || \"?\"; let n = 0; for (let i = 0; i &lt; s.length; i++) n = (n * 31 + s.charCodeAt(i)) &gt;&gt;&gt; 0;\n  const t = TAGV[n % TAGV.length];\n  return {s.trim()[0] || \"?\"};\n}\nconst APP_LOGO_MAP = {\n  Zoom: \"zoom\", \"Zoom One Pro\": \"zoom\",\n  Miro: \"miro\",\n  Dropbox: \"dropbox\",\n  Salesforce: \"salesforce\",\n  Slack: \"slack\",\n  Google: \"googlecloud\", \"Google WS Ent. Starter\": \"google_workspace\", \"Google WS Ent. Standard\": \"google_workspace\", \"Google WS Business\": \"google_workspace\",\n  Office365: \"office365\", \"Microsoft 365 G3 GCC\": \"office365\", \"Microsoft 365 E5\": \"office365\", \"Microsoft Power Aut\u2026\": \"office365\",\n  Microsoft: \"microsoft\",\n};\nfunction AppAvatar({ name, size = 22 }) {\n  const brand = APP_LOGO_MAP[name];\n  if (brand) return ;\n  const s = name || \"?\"; let n = 0; for (let i = 0; i &lt; s.length; i++) n = (n * 31 + s.charCodeAt(i)) &gt;&gt;&gt; 0;\n  const t = TAGV[n % TAGV.length];\n  return {s.trim()[0] || \"?\"};\n}\nconst STATUS = { Active: T.green, Warning: T.viz.amber, Inactive: T.faint, Error: T.red };\nfunction StatusChip({ status }) {\n  return {status};\n}\nconst RISK = { Critical: SEVC.critical, High: SEVC.high, Medium: SEVC.medium, Low: SEVC.low };\nfunction RiskChip({ level }) {\n  const c = RISK[level];\n  return {level};\n}\nfunction Delta({ up, children }) {\n  const color = up ? T.green : T.red;\n  return {up ?  : }{children};\n}\nfunction IconBtn({ children, onClick }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ width: 26, height: 26, borderRadius: 6, border: \"none\", cursor: \"pointer\", background: h ? T.surface2 : \"transparent\", color: h ? T.ink : T.faint, display: \"flex\", alignItems: \"center\", justifyContent: \"center\" }}&gt;{children};\n}\n\n/* ============ KPIs ============ */\nconst KPIS = [\n  { label: \"Total Assets\", value: 84355, delta: \"12.4%\", up: true },\n  { label: \"Cloud Coverage\", value: 96.2, suffix: \"%\", delta: \"2.1%\", up: true },\n  { label: \"Open Vulnerabilities\", value: 382, delta: \"8.0%\", up: false },\n  { label: \"Unmanaged Devices\", value: 1204, delta: \"3.2%\", up: false },\n];\nfunction useCountUp(target, dur = 950) {\n  const [v, setV] = useState(0);\n  useEffect(() =&gt; {\n    let raf; const start = performance.now();\n    const tick = (now) =&gt; { const p = Math.min(1, (now - start) / dur); setV(target * (1 - Math.pow(1 - p, 3))); if (p &lt; 1) raf = requestAnimationFrame(tick); };\n    raf = requestAnimationFrame(tick); return () =&gt; cancelAnimationFrame(raf);\n  }, [target]);\n  return v;\n}\nfunction KpiValue({ value, suffix }) {\n  const v = useCountUp(value);\n  const text = suffix === \"%\" ? v.toFixed(1) : fmt(Math.round(v));\n  return {text}{suffix || \"\"};\n}\nfunction KpiRow() {\n  return (\n    \n\n      {KPIS.map((k, i) =&gt; (\n        \n\n          \n{k.label}\n          \n\n            \n            {k.delta}\n          \n        \n      ))}\n    \n  );\n}\n\n/* ============ chart bodies ============ */\nconst PIE = [\n  { c: \"green\", label: \"Windows Assets\", val: 1236 }, { c: \"greenDeep\", label: \"Active Directory\", val: 411 },\n  { c: \"mint\", label: \"SCCM Managed\", val: 198 }, { c: \"lilac\", label: \"Intune Managed\", val: 89 }, { c: \"lilacPale\", label: \"Unmanaged\", val: 43 },\n];\nfunction DonutBody() {\n  const [hi, setHi] = useState(null);\n  const [tip, setTip] = useState(null);\n  const [off, setOff] = useState(() =&gt; new Set());\n  const [drawn, setDrawn] = useState(false);\n  useEffect(() =&gt; { const id = setTimeout(() =&gt; setDrawn(true), 60); return () =&gt; clearTimeout(id); }, []);\n  const r = 46, sw = 14, C = 2 * Math.PI * r, gap = 5;\n  const toggle = (i) =&gt; setOff(s =&gt; { const n = new Set(s); n.has(i) ? n.delete(i) : n.add(i); return n; });\n  const visTotal = PIE.reduce((a, p, i) =&gt; a + (off.has(i) ? 0 : p.val), 0) || 1;\n  let acc = 0;\n  const segs = PIE.map((p, i) =&gt; {\n    if (off.has(i)) return null;\n    const len = (p.val / visTotal) * C;\n    const seg = Math.max(1, len - gap);\n    const active = hi === i, dim = hi !== null &amp;&amp; !off.has(hi) &amp;&amp; !active;\n    const node = (\n       { setHi(i); setTip({ i, x: e.clientX, y: e.clientY }); }} onMouseMove={(e) =&gt; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; { setHi(null); setTip(null); }} onClick={() =&gt; toggle(i)}\n        style={{ opacity: dim ? 0.35 : 1, cursor: \"pointer\", transition: \"stroke-dasharray .8s cubic-bezier(.22,1,.36,1), stroke-width .18s ease, opacity .18s ease\" }} /&gt;\n    );\n    acc += len; return node;\n  });\n  const liveHi = hi !== null &amp;&amp; !off.has(hi);\n  const center = liveHi ? { big: ((PIE[hi].val / visTotal) * 100).toFixed(1) + \"%\", small: PIE[hi].label } : { big: fmt(visTotal), small: \"Total Assets\" };\n  return (\n    \n\n      \n\n        \n          \n          {segs}\n        \n        \n\n          {center.big}\n          {center.small}\n        \n      \n      \n\n        {PIE.map((p, i) =&gt; {\n          const isOff = off.has(i);\n          return (\n            \n { setHi(i); if (!isOff) setTip({ i, x: e.clientX, y: e.clientY }); }} onMouseMove={(e) =&gt; !isOff &amp;&amp; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; { setHi(null); setTip(null); }} onClick={() =&gt; toggle(i)}\n              title={isOff ? \"Click to show\" : \"Click to hide\"}\n              style={{ display: \"flex\", alignItems: \"center\", gap: 9, padding: \"5px 6px\", borderRadius: 6, cursor: \"pointer\", background: hi === i &amp;&amp; !isOff ? T.canvas : \"transparent\", transition: \"background .12s\" }}&gt;\n              \n              {p.label}\n              {isOff ? \"Hidden\" : (hi === i ? fmt(p.val) : ((p.val / visTotal) * 100).toFixed(1) + \"%\")}\n            \n          );\n        })}\n      \n      {tip !== null &amp;&amp; !off.has(tip.i) &amp;&amp; \n        \n{PIE[tip.i].label}\n        \n{fmt(PIE[tip.i].val)}\n        \n{(PIE[tip.i].val / visTotal * 100).toFixed(1)}% of assets\n      }\n    \n  );\n}\nconst LINE_LABELS = [\"W1\", \"W2\", \"W3\", \"W4\", \"W5\", \"W6\", \"W7\", \"W8\", \"W9\", \"W10\", \"W11\", \"W12\"];\nconst LINE_SERIES = [\n  { key: \"all\", label: \"All Devices\", color: T.viz.green, pts: [38000, 41200, 44500, 47800, 51000, 55300, 59800, 63200, 68500, 72100, 78400, 84355] },\n  { key: \"managed\", label: \"Managed\", color: T.viz.lilac, pts: [29000, 31000, 33500, 35800, 38000, 41000, 44500, 47200, 50500, 54100, 58400, 61200] },\n  { key: \"cloud\", label: \"Cloud\", color: T.viz.orange, pts: [8000, 9200, 10500, 11800, 13000, 14300, 15800, 17200, 18500, 20100, 21400, 22097] },\n  { key: \"unmanaged\", label: \"Unmanaged\", color: T.viz.greenDeep, pts: [3200, 3100, 3300, 3000, 3500, 3300, 3100, 2900, 2700, 2500, 2300, 2100] },\n];\nfunction LineBody() {\n  const [off, setOff] = useState(() =&gt; new Set());\n  const [hov, setHov] = useState(null);\n  const w = 680, h = 196, pad = 12, n = LINE_LABELS.length, max = 90000;\n  const X = (i) =&gt; pad + (i / (n - 1)) * (w - pad * 2);\n  const Y = (v) =&gt; h - pad - (v / max) * (h - pad * 2);\n  const smooth = (pts) =&gt; { let d = `M ${X(0)} ${Y(pts[0])}`; for (let i = 1; i &lt; n; i++) { const cx = (X(i - 1) + X(i)) / 2; d += ` C ${cx} ${Y(pts[i - 1])}, ${cx} ${Y(pts[i])}, ${X(i)} ${Y(pts[i])}`; } return d; };\n  const toggle = (k) =&gt; setOff(s =&gt; { const x = new Set(s); x.has(k) ? x.delete(k) : x.add(k); return x; });\n  const visible = LINE_SERIES.filter(s =&gt; !off.has(s.key));\n  const xPct = (i) =&gt; (X(i) / w) * 100, yPct = (v) =&gt; (Y(v) / h) * 100;\n  return (\n    \n\n      \n setHov(null)}&gt;\n        \n          {[0, 1, 2, 3].map(i =&gt; )}\n          {hov &amp;&amp; }\n          {visible.map(s =&gt; )}\n        \n        {/* a hoverable dot on every point \u2014 hovering one shows just that series */}\n        {visible.map(s =&gt; s.pts.map((v, i) =&gt; {\n          const isHov = hov &amp;&amp; hov.s.key === s.key &amp;&amp; hov.i === i, dim = hov &amp;&amp; !isHov;\n          return (\n            \n setHov({ s, i })}\n              style={{ position: \"absolute\", left: `${xPct(i)}%`, top: `${yPct(v)}%`, transform: \"translate(-50%, -50%)\", width: 20, height: 20, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", cursor: \"pointer\", zIndex: isHov ? 4 : 2 }}&gt;\n              \n            \n          );\n        }))}\n        {hov &amp;&amp; (\n          \n n - 4 ? \"calc(-100% - 12px)\" : hov.i &lt; 3 ? \"12px\" : \"-50%\"}, calc(-100% - 14px))`, background: T.surface, border: `1px solid ${T.line}`, borderRadius: 10, boxShadow: T.shadow, padding: \"9px 12px\", whiteSpace: \"nowrap\", pointerEvents: \"none\", zIndex: 6 }}&gt;\n            \n{hov.s.label}\n            \n{fmt(hov.s.pts[hov.i])}\n            {hov.i &gt; 0 &amp;&amp; (() =&gt; { const pct = (hov.s.pts[hov.i] - hov.s.pts[hov.i - 1]) / hov.s.pts[hov.i - 1] * 100; const up = pct &gt;= 0; return \n{up ?  : }{up ? \"+\" : \"\"}{pct.toFixed(1)}% from previous; })()}\n            \n{LINE_LABELS[hov.i]}\n          \n        )}\n      \n      \n\n        {LINE_SERIES.map(s =&gt; {\n          const isOff = off.has(s.key);\n          return (\n             toggle(s.key)} title={isOff ? \"Show series\" : \"Hide series\"} className=\"ax-press\"\n              style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, padding: \"3px 9px\", borderRadius: 999, border: `1px solid ${isOff ? T.lineSoft : T.line}`, background: isOff ? \"transparent\" : T.canvas, cursor: \"pointer\", fontFamily: T.font, fontSize: 11.5, fontWeight: 500, color: isOff ? T.faint : T.body, transition: \"all .14s\" }}&gt;\n              \n              {s.label}\n            \n          );\n        })}\n      \n    \n  );\n}\nconst STACK = [[\"Jan\", 40, 25, 20], [\"Feb\", 35, 30, 15], [\"Mar\", 52, 22, 26], [\"Apr\", 30, 35, 18], [\"May\", 48, 28, 24], [\"Jun\", 58, 32, 28]];\nconst STACK_KEYS = [[\"Cloud\", T.viz.green], [\"On-prem\", T.viz.mint], [\"SaaS\", T.viz.lilac]];\nfunction StackedBody() {\n  const [hi, setHi] = useState(null);\n  const [tip, setTip] = useState(null);\n  const max = 130;\n  return (\n    \n\n      {tip &amp;&amp; \n{STACK[tip.i][0]}\n{fmt((STACK[tip.i][1] + STACK[tip.i][2] + STACK[tip.i][3]) * 86)}\ntotal assets}\n      \n\n        {[0, 1, 2, 3].map(i =&gt; \n)}\n        {STACK.map((s, i) =&gt; {\n          const total = s[1] + s[2] + s[3];\n          const active = hi === i, dim = hi !== null &amp;&amp; !active;\n          return (\n            \n { setHi(i); setTip({ i, x: e.clientX, y: e.clientY }); }} onMouseMove={(e) =&gt; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; { setHi(null); setTip(null); }}\n              style={{ flex: 1, display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap: 8, height: \"100%\", justifyContent: \"flex-end\", zIndex: 1, cursor: \"pointer\", position: \"relative\" }}&gt;\n              \n\n                \n\n                \n\n\n              \n              {s[0]}\n            \n          );\n        })}\n      \n      \n\n        {STACK_KEYS.map(([label, color], i) =&gt; (\n          {label}\n        ))}\n      \n    \n  );\n}\nconst SEV = [[\"Critical\", 142, 100, SEVC.critical], [\"High\", 98, 70, SEVC.high], [\"Medium\", 76, 54, SEVC.medium], [\"Low\", 45, 32, SEVC.low], [\"Info\", 21, 15, SEVC.info]];\nconst SEV_ASSETS = {\n  Critical: [[\"PC-CURTIS-WILLIAMS\", \"CVE-2024-21412 \u00b7 SmartScreen bypass\"], [\"srv-db-fin-04\", \"CVE-2024-3094 \u00b7 xz-utils backdoor\"], [\"esx-infranginx-5567897\", \"CVE-2023-46604 \u00b7 ActiveMQ RCE\"], [\"WIN-RUTHD\", \"CVE-2024-21413 \u00b7 Outlook RCE\"]],\n  High: [[\"macbook-pro-jdoe\", \"CVE-2024-23222 \u00b7 WebKit type confusion\"], [\"azure-infra9274676\", \"CVE-2024-21401 \u00b7 Entra ID elevation\"], [\"lablb-2918146-beta\", \"CVE-2024-1709 \u00b7 ScreenConnect auth bypass\"]],\n  Medium: [[\"iphone-asmith\", \"CVE-2024-23225 \u00b7 kernel memory\"], [\"sepio-external3026786\", \"CVE-2024-0519 \u00b7 V8 out-of-bounds\"]],\n  Low: [[\"android-pixel-7\", \"CVE-2024-0039 \u00b7 System component\"]],\n  Info: [[\"win-marychasse\", \"Informational \u00b7 TLS 1.0 enabled\"]],\n};\nfunction SeverityDrawer({ sev, close }) {\n  const list = SEV_ASSETS[sev[0]] || [];\n  return createPortal(&lt;&gt;\n    \n\n    \n\n      \n\n        \n        \n\n          \n{sev[0]} severity\n          \n{sev[1]} open vulnerabilities \u00b7 {list.length} assets shown\n        \n        \n      \n      \n\n        {list.map(([host, cve], i) =&gt; (\n          \n\n            \n\n              {host}\n              {sev[0]}\n            \n            {cve}\n          \n        ))}\n      \n      \n\n        View all {sev[1]} in Inventory\n      \n    \n  , document.body);\n}\nfunction SeverityBody() {\n  const [hi, setHi] = useState(null);\n  const [drill, setDrill] = useState(null);\n  const [tip, setTip] = useState(null);\n  const sevTotal = SEV.reduce((a, b) =&gt; a + b[1], 0);\n  return (&lt;&gt;\n    \n\n      {SEV.map((b, i) =&gt; {\n        const active = hi === i, dim = hi !== null &amp;&amp; !active;\n        return (\n          \n setDrill(b)} onMouseEnter={(e) =&gt; { setHi(i); setTip({ i, x: e.clientX, y: e.clientY }); }} onMouseMove={(e) =&gt; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; { setHi(null); setTip(null); }}\n            style={{ display: \"flex\", alignItems: \"center\", gap: 12, opacity: dim ? 0.5 : 1, transition: \"opacity .15s\", cursor: \"pointer\" }}&gt;\n            {b[0]}\n            \n\n\n            {active ? b[1] + \" open\" : b[1]}\n          \n        );\n      })}\n    \n    {tip &amp;&amp; \n{SEV[tip.i][0]}\n{fmt(SEV[tip.i][1])}\n{Math.round(SEV[tip.i][1] / sevTotal * 100)}% of open findings}\n    {drill &amp;&amp;  setDrill(null)} /&gt;}\n  );\n}\nconst ST_COLOR = { ok: T.viz.green, warn: T.viz.amber, err: T.red };\n\n/* ===== System Lifecycle (discovery cycle) ===== */\nconst LIFECYCLE = [\n  { name: \"Fetch\", pct: 100 }, { name: \"Clean\", pct: 100 }, { name: \"Correlate\", pct: 100 },\n  { name: \"Enrich\", pct: 78 }, { name: \"Calculate\", pct: 0 },\n];\nfunction LifecycleBody() {\n  const [tip, setTip] = useState(null);\n  const done = LIFECYCLE.filter(s =&gt; s.pct === 100).length, total = LIFECYCLE.length;\n  const overall = Math.round(LIFECYCLE.reduce((a, s) =&gt; a + s.pct, 0) / total);\n  const r = 42, sw = 11, C = 2 * Math.PI * r, dash = (overall / 100) * C;\n  return (\n    \n\n      {tip &amp;&amp; \n{LIFECYCLE[tip.i].name} stage\n{LIFECYCLE[tip.i].pct &gt; 0 ? LIFECYCLE[tip.i].pct + \"% complete\" : \"Not started\"}}\n      \n\n        \n\n          \n            \n            \n          \n          \n\n            {done}/{total}\n            Stages\n          \n        \n        \n\n          {LIFECYCLE.map((s, i) =&gt; {\n            const c = s.pct === 100 ? T.viz.green : s.pct &gt; 0 ? T.blue : T.bg40;\n            return (\n              \n setTip({ i, x: e.clientX, y: e.clientY })} onMouseMove={(e) =&gt; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; setTip(null)} style={{ display: \"flex\", alignItems: \"center\", gap: 10, cursor: \"default\" }}&gt;\n                {s.name}\n                \n\n                  \n\n                \n                 0 ? T.blue : T.bg40, ...tnum }}&gt;{s.pct &gt; 0 ? s.pct + \"%\" : \"\u2014\"}\n              \n            );\n          })}\n        \n      \n      \n\n        {[[\"Cycle started\", \"09:00:04\"], [\"Duration\", \"00:42:18\"], [\"Next cycle\", \"13:17:42\"]].map(([k, v], i) =&gt; (\n          \n\n            \n{k}\n            \n{v}\n          \n        ))}\n      \n    \n  );\n}\n\n/* ===== Discovery Log ===== */\nconst DISC_PHASES = [[\"Fetch\", T.viz.green], [\"Clean\", T.viz.mint], [\"Correlate\", T.viz.lilac], [\"Enrich\", T.viz.lilacPale], [\"Calculate\", T.viz.amber], [\"Save\", T.viz.orange]];\nconst DISCOVERY = [\n  { started: \"Jun 15, 09:00\", completed: \"Jun 15, 09:35\", duration: \"35min 22sec\", ph: [40, 12, 18, 10, 12, 8] },\n  { started: \"Jun 14, 09:00\", completed: \"Jun 14, 09:35\", duration: \"35min 59sec\", ph: [38, 14, 17, 11, 12, 8] },\n  { started: \"Jun 13, 09:00\", completed: \"Jun 13, 09:35\", duration: \"35min 29sec\", ph: [41, 11, 18, 10, 12, 8] },\n  { started: \"Jun 12, 09:00\", completed: \"Jun 12, 09:35\", duration: \"35min 11sec\", ph: [39, 13, 17, 11, 12, 8] },\n  { started: \"Jun 11, 09:00\", completed: \"Jun 11, 09:35\", duration: \"35min 58sec\", ph: [44, 12, 16, 9, 11, 8] },\n  { started: \"Jun 10, 09:00\", completed: \"Jun 10, 09:30\", duration: \"30min 54sec\", ph: [36, 12, 18, 12, 14, 8] },\n  { started: \"Jun 9, 09:00\", completed: \"Jun 9, 09:34\", duration: \"34min 02sec\", ph: [37, 13, 17, 11, 13, 9] },\n  { started: \"Jun 8, 09:00\", completed: \"Jun 8, 09:31\", duration: \"31min 38sec\", ph: [40, 12, 17, 10, 13, 8] },\n];\nfunction DiscoveryBody() {\n  const [hi, setHi] = useState(null);\n  const [tip, setTip] = useState(null);\n  const cols = \"minmax(70px, 1.6fr) 104px 92px\";\n  const durToSec = (s) =&gt; { let t = 0; const m = s.match(/(\\d+)\\s*min/), sec = s.match(/(\\d+)\\s*sec/); if (m) t += +m[1] * 60; if (sec) t += +sec[1]; return t; };\n  const fmtDur = (x) =&gt; { x = Math.round(x); const m = Math.floor(x / 60), s = x % 60; return m &gt; 0 ? `${m}min ${s}sec` : `${s}sec`; };\n  return (\n    \n\n      \n\n        {[\"Latest Discoveries\", \"Started\", \"Duration\"].map((c, i) =&gt; (\n          {c}\n        ))}\n      \n      \n\n        {DISCOVERY.map((d, i) =&gt; (\n          \n setHi(i)} onMouseLeave={() =&gt; { setHi(null); setTip(null); }}\n            style={{ display: \"grid\", gridTemplateColumns: cols, gap: 12, alignItems: \"center\", padding: \"7px 6px\", borderBottom: i &lt; DISCOVERY.length - 1 ? `1px solid ${T.hair}` : \"none\", background: hi === i ? T.canvas : \"transparent\", borderRadius: 6, transition: \"background .12s\" }}&gt;\n            \n\n              {d.ph.map((w, j) =&gt;  setTip({ d, j, x: e.clientX, y: e.clientY })} onMouseMove={(e) =&gt; setTip({ d, j, x: e.clientX, y: e.clientY })}\n                style={{ width: `${w}%`, height: \"100%\", background: DISC_PHASES[j][1], cursor: \"pointer\" }} /&gt;)}\n            \n            {d.started}\n            {d.duration}\n          \n        ))}\n      \n      {tip &amp;&amp; \n        \n{DISC_PHASES[tip.j][0]}\n        \n{fmtDur(durToSec(tip.d.duration) * tip.d.ph[tip.j] / 100)}\n        \n{tip.d.ph[tip.j]}% of cycle\n      }\n    \n  );\n}\n\n/* ===== Adapter Connections (status + logo coin + preview) ===== */\nconst ADAPTER_CONN = [\n  { brand: \"crowdstrike\", name: \"CrowdStrike\", st: \"ok\", desc: \"9,120 devices \u00b7 synced 4m ago\" },\n  { brand: \"aws\", name: \"Amazon Web Services\", st: \"ok\", desc: \"12,400 assets \u00b7 synced 6m ago\" },\n  { brand: \"azure\", name: \"Microsoft Azure\", st: \"ok\", desc: \"22,097 assets \u00b7 synced 5m ago\" },\n  { brand: \"okta\", name: \"Okta\", st: \"ok\", desc: \"9,870 users \u00b7 synced 8m ago\" },\n  { short: \"Jm\", color: \"#3B3B3B\", name: \"Jamf Pro\", st: \"err\", desc: \"Connection timeout \u2014 check credentials\" },\n  { short: \"now\", color: \"#2E8B57\", name: \"ServiceNow\", st: \"warn\", desc: \"Rate limited \u00b7 retrying\" },\n  { brand: \"googlecloud\", name: \"Google Cloud\", st: \"ok\", desc: \"9,055 assets \u00b7 synced 7m ago\" },\n  { brand: \"vmware\", name: \"VMware vCenter\", st: \"ok\", desc: \"4,310 VMs \u00b7 synced 11m ago\" },\n];\nconst ST_LABEL = { ok: \"Connected\", warn: \"Degraded\", err: \"Error\" };\nfunction AdapterCoin({ a }) {\n  if (a.brand) return ;\n  return {a.short};\n}\nfunction AdaptersBody() {\n  const connected = ADAPTER_CONN.filter(a =&gt; a.st === \"ok\").length;\n  const attention = ADAPTER_CONN.length - connected;\n  return (\n    \n\n      \n\n        {connected}\n        connected sources\n        {attention &gt; 0 &amp;&amp; {attention} need attention}\n      \n      \n\n        {ADAPTER_CONN.map((a, i) =&gt; {\n          const ring = ST_COLOR[a.st];\n          return (\n            \n\n              \n              \n\n                \n{a.name}\n                \n\n                  \n                  {a.desc}\n                \n              \n            \n          );\n        })}\n      \n    \n  );\n}\nconst HEALTH_OK_BRANDS = [\"crowdstrike\", \"aws\", \"azure\", \"okta\", \"googlecloud\", \"vmware\"];\nfunction HealthOkBody() {\n  return (\n    \n\n      \n\n        33\n        adapters syncing successfully\n      \n      \n\n        {HEALTH_OK_BRANDS.map((b, i) =&gt; )}\n        +27\n      \n      \n\n         100% healthy \u00b7 last full sync 4m ago\n      \n    \n  );\n}\nconst HEALTH_FAIL = [\n  { short: \"Jm\", color: \"#3B3B3B\", name: \"Jamf Pro\", reason: \"Connection timeout \u2014 check credentials\" },\n  { short: \"now\", color: \"#2E8B57\", name: \"ServiceNow\", reason: \"Rate limited \u00b7 retrying\" },\n];\nfunction HealthFailBody() {\n  return (\n    \n\n      \n\n        {HEALTH_FAIL.length}\n        adapters need attention\n      \n      \n\n        {HEALTH_FAIL.map((a, i) =&gt; (\n          \n\n            {a.short}\n            \n\n              \n{a.name}\n              \n{a.reason}\n            \n            Fix\n          \n        ))}\n      \n    \n  );\n}\n/* ===== Cost Optimization dashboard ===== */\nconst fmtMoney = (n) =&gt; n.toLocaleString(\"en-US\");\nfunction CostKPI({ value, unit }) {\n  return (\n    \n\n      {value}\n      {unit &amp;&amp; {unit}}\n    \n  );\n}\nfunction HBarList({ rows, accent, prefix, avatar, appLogo }) {\n  const max = Math.max(...rows.map(r =&gt; r[1]), 1);\n  const totalVal = rows.reduce((a, r) =&gt; a + r[1], 0) || 1;\n  const [tip, setTip] = useState(null);\n  return (\n    \n\n      {rows.map(([label, val], i) =&gt; {\n        const pct = Math.max(4, (val / max) * 100), inside = pct &gt; 24;\n        return (\n          \n setTip({ i, x: e.clientX, y: e.clientY })} onMouseMove={(e) =&gt; setTip({ i, x: e.clientX, y: e.clientY })} onMouseLeave={() =&gt; setTip(null)}\n            style={{ display: \"flex\", alignItems: \"center\", gap: 12, cursor: \"default\" }}&gt;\n            {avatar &amp;&amp; }{appLogo &amp;&amp; }{label}\n            \n\n              \n\n                {inside &amp;&amp; {prefix || \"\"}{fmtMoney(val)}}\n              \n              {!inside &amp;&amp; {prefix || \"\"}{fmtMoney(val)}}\n            \n          \n        );\n      })}\n      {tip &amp;&amp; \n        \n{rows[tip.i][0]}\n        \n{prefix || \"\"}{fmtMoney(rows[tip.i][1])}\n        \n{(rows[tip.i][1] / totalVal * 100).toFixed(1)}% of shown total\n      }\n    \n  );\n}\nconst COST_CATEGORY = [[\"Productivity\", 141993], [\"Video Conference\", 55928], [\"File Sharing\", 52943], [\"Authentication\", 11440], [\"CRM\", 9870]];\nconst COST_APP = [[\"Zoom\", 55928], [\"Miro\", 52885], [\"Dropbox\", 50431], [\"Salesforce\", 43176], [\"Slack\", 38420]];\nconst COST_TOPLIC = [[\"Zoom One Pro\", 70800], [\"Google WS Ent. Starter\", 27450], [\"Google WS Ent. Standard\", 12985], [\"Google WS Business\", 8640], [\"Microsoft 365 G3 GCC\", 1930]];\nconst COST_USAGE = [[\"aaron.church@demo.local\", 29], [\"aaron.daniels@demo.local\", 29], [\"ada.pires@demo.local\", 29], [\"adelaide.gercak@demo.local\", 29], [\"adrian.williams@demo.local\", 29]];\nconst COST_RENEWALS = [\n  { app: \"Office365\", name: \"Microsoft 365 E5\", date: \"2026-07-06\", cost: \"3,175.50\", term: \"Yearly\", unit: \"54.75\" },\n  { app: \"Office365\", name: \"Microsoft Power Aut\u2026\", date: \"2026-08-07\", cost: \"6,518.75\", term: \"Yearly\", unit: \"37.25\" },\n];\nfunction CostExpAllBody() { return ; }\nfunction CostLicAllBody() { return ; }\nfunction CostExp2024Body() { return ; }\nfunction CostLicTotalBody() { return ; }\nfunction CostCategoryBody() { return ; }\nfunction CostAppBody() { return ; }\nfunction CostTopLicBody() { return ; }\nfunction CostUsageBody() { return ; }\nfunction CostRenewalsBody() {\n  const cols = [\n    { label: \"Application\", render: r =&gt; {r.app} },\n    { label: \"Name\", render: r =&gt; {r.name} },\n    { label: \"Renewal Date\", render: r =&gt; {r.date} },\n    { label: \"Total Cost\", align: \"right\", render: r =&gt; ${r.cost} },\n    { label: \"Term\", render: r =&gt; {r.term} },\n    { label: \"Unit\", align: \"right\", render: r =&gt; ${r.unit} },\n  ];\n  return (\n    \n\n      \n\n        \n          \n            {cols.map((c, i) =&gt; {c.label})}\n          \n        \n        \n          {COST_RENEWALS.map((r, ri) =&gt; (\n            \n              {cols.map((c, i) =&gt; {c.render(r)})}\n            \n          ))}\n        \n      \n    \n  );\n}\nfunction ResetViewBtn({ disabled, onReset }) {\n  const [h, setH] = useState(false);\n  return (\n     setH(true)} onMouseLeave={() =&gt; setH(false)}\n      title={disabled ? \"Layout is at its default\" : \"Restore the default tile layout\"}\n      style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, height: 30, padding: \"0 13px\", borderRadius: 999, fontFamily: T.font, fontSize: 13, fontWeight: 600, cursor: disabled ? \"default\" : \"pointer\", border: `1px solid ${disabled ? T.lineSoft : h ? T.lineStrong : T.line}`, background: disabled ? \"transparent\" : h ? T.surface2 : T.control, color: disabled ? T.faint : T.body, transition: \"all .14s\" }}&gt;\n       Reset view\n    \n  );\n}\n\nconst TILE_META = {\n  line: { eyebrow: \"Activity\", title: \"Device Discovery Over Time\", Body: LineBody },\n  donut: { eyebrow: \"Composition\", title: \"Windows Distribution\", Body: DonutBody },\n  lifecycle: { eyebrow: \"Pipeline\", title: \"System Lifecycle\", Body: LifecycleBody },\n  discovery: { eyebrow: \"Activity\", title: \"Discovery Log\", Body: DiscoveryBody },\n  adapters: { eyebrow: \"Connections\", title: \"Adapter Connections\", Body: AdaptersBody },\n  stacked: { eyebrow: \"Trend\", title: \"Asset Growth by Category\", Body: StackedBody, footer: 12.4% },\n  severity: { eyebrow: \"Risk\", title: \"Vulnerabilities by Severity\", Body: SeverityBody, footer: 18 resolved },\n  healthOk: { eyebrow: \"Adapters\", title: \"Adapter Health \u00b7 Successful\", Body: HealthOkBody },\n  healthFail: { eyebrow: \"Adapters\", title: \"Adapter Health \u00b7 Attention\", Body: HealthFailBody },\n  costExpAll: { eyebrow: \"Spend\", title: \"Total Expenses (All Time)\", Body: CostExpAllBody },\n  costExp2024: { eyebrow: \"Spend\", title: \"Total Expenses (2024)\", Body: CostExp2024Body },\n  costLicAll: { eyebrow: \"Licenses\", title: \"Total License Cost (All Time)\", Body: CostLicAllBody },\n  costLicTotal: { eyebrow: \"Licenses\", title: \"Total License Cost\", Body: CostLicTotalBody },\n  costRenewals: { eyebrow: \"Renewals\", title: \"Upcoming Renewals (next 90 days)\", Body: CostRenewalsBody, footer: View all results, noPad: true },\n  costCategory: { eyebrow: \"Spend\", title: \"Expenses by Category (2024)\", Body: CostCategoryBody },\n  costApp: { eyebrow: \"Spend\", title: \"Expenses by App (2024)\", Body: CostAppBody },\n  costTopLic: { eyebrow: \"Licenses\", title: \"Most Expensive Licenses (2024)\", Body: CostTopLicBody },\n  costUsage: { eyebrow: \"Usage\", title: \"Azure AD Logons \u00b7 last 30 days\", Body: CostUsageBody },\n};\n\n/* ============ resizable grid (corner drag, neighbors reflow) ============ */\nconst GRID_COLS = 12, ROW_UNIT = 40, GAP = 16;\nconst INITIAL_TILES = [\n  { id: \"line\", w: 8, h: 7 }, { id: \"donut\", w: 4, h: 7 },\n  { id: \"lifecycle\", w: 5, h: 7 }, { id: \"discovery\", w: 7, h: 7 },\n  { id: \"adapters\", w: 7, h: 6 }, { id: \"severity\", w: 5, h: 6 },\n  { id: \"healthOk\", w: 6, h: 5 }, { id: \"healthFail\", w: 6, h: 5 },\n  { id: \"stacked\", w: 12, h: 5 },\n];\nconst COST_TILES = [\n  { id: \"costExpAll\", w: 3, h: 4 }, { id: \"costExp2024\", w: 3, h: 4 }, { id: \"costLicAll\", w: 3, h: 4 }, { id: \"costLicTotal\", w: 3, h: 4 },\n  { id: \"costRenewals\", w: 12, h: 6 },\n  { id: \"costCategory\", w: 6, h: 6 }, { id: \"costApp\", w: 6, h: 6 },\n  { id: \"costTopLic\", w: 6, h: 6 }, { id: \"costUsage\", w: 6, h: 6 },\n];\nconst DASH_TILESETS = { \"Cost Optimization\": COST_TILES };\nfunction DashboardGrid({ tileSet = INITIAL_TILES }) {\n  const [tiles, setTiles] = useState(tileSet);\n  const [hoverId, setHoverId] = useState(null);\n  const [resizingId, setResizingId] = useState(null);\n  const [drag, setDrag] = useState(null);\n  const dragId = drag ? drag.id : null;\n  const ref = useRef(null);\n  const tileEls = useRef({});\n  const justDragged = useRef(false);\n\n  const startDrag = (e, id, immediate) =&gt; {\n    if (e.button || (e.target.closest &amp;&amp; (e.target.closest(\"[data-resize]\") || e.target.closest(\"button\")))) return;\n    const startX = e.clientX, startY = e.clientY;\n    let dragging = false;\n    const begin = (cx, cy) =&gt; {\n      const el = tileEls.current[id]; if (!el) return;\n      const rect = el.getBoundingClientRect();\n      dragging = true;\n      setDrag({ id, x: cx, y: cy, ox: cx - rect.left, oy: cy - rect.top, w: rect.width, h: rect.height });\n      document.body.style.userSelect = \"none\";\n    };\n    const timer = immediate ? null : setTimeout(() =&gt; begin(startX, startY), 300);\n    const onMove = (ev) =&gt; {\n      if (!dragging) {\n        const moved = Math.abs(ev.clientX - startX) &gt; (immediate ? 4 : 8) || Math.abs(ev.clientY - startY) &gt; (immediate ? 4 : 8);\n        if (!moved) return;\n        if (immediate) begin(ev.clientX, ev.clientY); else { end(); return; }\n        if (!dragging) return;\n      }\n      setDrag(d =&gt; d ? { ...d, x: ev.clientX, y: ev.clientY } : d);\n      for (const tid in tileEls.current) {\n        const el = tileEls.current[tid];\n        if (!el || tid === id) continue;\n        const r = el.getBoundingClientRect();\n        if (ev.clientX &gt;= r.left &amp;&amp; ev.clientX &lt;= r.right &amp;&amp; ev.clientY &gt;= r.top &amp;&amp; ev.clientY &lt;= r.bottom) {\n          setTiles(ts =&gt; { const from = ts.findIndex(x =&gt; x.id === id), to = ts.findIndex(x =&gt; x.id === tid); if (from &lt; 0 || to &lt; 0 || from === to) return ts; const n = ts.slice(); const [mv] = n.splice(from, 1); n.splice(to, 0, mv); return n; });\n          break;\n        }\n      }\n    };\n    const end = () =&gt; { clearTimeout(timer); if (dragging) { justDragged.current = true; setTimeout(() =&gt; { justDragged.current = false; }, 60); } dragging = false; setDrag(null); document.body.style.userSelect = \"\"; window.removeEventListener(\"pointermove\", onMove); window.removeEventListener(\"pointerup\", end); };\n    window.addEventListener(\"pointermove\", onMove); window.addEventListener(\"pointerup\", end);\n  };\n\n  const startResize = (e, id) =&gt; {\n    e.preventDefault(); e.stopPropagation();\n    setResizingId(id);\n    const rect = ref.current.getBoundingClientRect();\n    const colW = (rect.width - GAP * (GRID_COLS - 1)) / GRID_COLS;\n    const t = tiles.find(x =&gt; x.id === id);\n    const startWpx = t.w * colW + (t.w - 1) * GAP;\n    const startHpx = t.h * ROW_UNIT + (t.h - 1) * GAP;\n    const sx = e.clientX, sy = e.clientY;\n    const onMove = (ev) =&gt; {\n      let w = Math.round((startWpx + (ev.clientX - sx) + GAP) / (colW + GAP));\n      let h = Math.round((startHpx + (ev.clientY - sy) + GAP) / (ROW_UNIT + GAP));\n      w = Math.max(3, Math.min(GRID_COLS, w));\n      h = Math.max(4, Math.min(14, h));\n      setTiles(ts =&gt; ts.map(x =&gt; x.id === id ? { ...x, w, h } : x));\n    };\n    const onUp = () =&gt; { setResizingId(null); document.body.style.cursor = \"\"; window.removeEventListener(\"mousemove\", onMove); window.removeEventListener(\"mouseup\", onUp); };\n    document.body.style.cursor = \"nwse-resize\";\n    window.addEventListener(\"mousemove\", onMove); window.addEventListener(\"mouseup\", onUp);\n  };\n\n  const isDefault = JSON.stringify(tiles) === JSON.stringify(tileSet);\n  return (\n    &lt;&gt;\n      \n\n         setTiles(tileSet)} /&gt;\n      \n    \n\n      {tiles.map((t, ti) =&gt; {\n        const m = TILE_META[t.id];\n        const isHover = hoverId === t.id, isResizing = resizingId === t.id, isDragging = dragId === t.id, anyDrag = dragId !== null;\n        return (\n          \n (tileEls.current[t.id] = el)}\n            onPointerDown={(e) =&gt; startDrag(e, t.id)}\n            onClickCapture={(e) =&gt; { if (justDragged.current) { e.stopPropagation(); justDragged.current = false; } }}\n            onMouseEnter={() =&gt; setHoverId(t.id)} onMouseLeave={() =&gt; setHoverId(null)}\n            style={{ gridColumn: `span ${t.w}`, gridRow: `span ${t.h}`, position: \"relative\", minWidth: 0, zIndex: 1 }}&gt;\n            {isDragging ? (\n              \n\n            ) : (&lt;&gt;\n            \n\n              \n { e.stopPropagation(); startDrag(e, t.id, true); }} title=\"Drag to move\" style={{ padding: \"13px 20px 12px\", borderBottom: `1px solid ${T.hair}`, display: \"flex\", alignItems: \"center\", gap: 8, cursor: \"grab\" }}&gt;\n                \n\n                  \n{m.eyebrow}\n                  \n{m.title}\n                \n                \n              \n              \n\n              {m.footer &amp;&amp; \n{m.footer}}\n            \n            \n startResize(e, t.id)} data-resize title=\"Drag to resize\"\n              style={{ position: \"absolute\", right: 0, bottom: 0, width: 20, height: 20, cursor: \"nwse-resize\", display: \"flex\", alignItems: \"flex-end\", justifyContent: \"flex-end\", padding: 4, opacity: isHover || isResizing ? 1 : 0, transition: \"opacity .15s\" }}&gt;\n              \n            \n            )}\n          \n        );\n      })}\n    \n    {drag &amp;&amp; (() =&gt; { const m = TILE_META[drag.id]; return createPortal(\n      \n\n        \n\n          \n\n            \n\n              \n{m.eyebrow}\n              \n{m.title}\n            \n            \n          \n          \nDrop to reorder\n        \n      , document.body); })()}\n    \n  );\n}\n\n/* ============ Assets table view (real device schema) ============ */\nconst ADAPTERS = {\n  aws_adapter: { brand: \"aws\" }, active_directory_adapter: { brand: \"active_directory\" },\n  crowd_strike_adapter: { brand: \"crowdstrike\" }, google_mdm_adapter: { brand: \"googlecloud\" },\n  esx_adapter: { brand: \"vmware\" }, sentinelone_adapter: { brand: \"sentinelone\" },\n  service_now_adapter: { brand: \"service_now\" }, epo_adapter: { brand: \"epo\" },\n  paloalto_panorama_adapter: { brand: \"paloalto\" }, cisco_meraki_adapter: { brand: \"cisco_meraki\" },\n  cisco_ise_adapter: { brand: \"cisco_ise\" }, cisco_adapter: { brand: \"cisco\" },\n  tanium_adapter: { brand: \"tanium\" }, tanium_asset_adapter: { brand: \"tanium\" },\n  claroty_adapter: { brand: \"claroty\" }, counter_act_adapter: { brand: \"forescout\" },\n  tenable_security_center_adapter: { brand: \"tenable\" }, zoom_adapter: { brand: \"zoom\" },\n  checkpoint_r80_adapter: { brand: \"checkpoint\" }, cylance_adapter: { brand: \"cylance\" },\n  chef_adapter: { brand: \"chef\" }, axonius_network_inspector_adapter: { short: \"NP\", color: \"#1C1D1F\" },\n};\nfunction AdapterAvatar({ k, size = 32 }) {\n  const a = ADAPTERS[k] || { short: k.slice(0, 2).toUpperCase(), color: \"#939598\" };\n  if (a.brand) return ;\n  return \n 28 ? 11 : 9, fontWeight: 700, color: a.color }}&gt;{a.short};\n}\nfunction AdapterStack({ list }) {\n  const shown = list.slice(0, 3), extra = list.length - shown.length;\n  return (\n    \n      \n      {shown.map((k, i) =&gt; )}\n      {extra &gt; 0 &amp;&amp; +{extra}}\n    \n  );\n}\n/* OS category icons */\nfunction OsIcon({ os }) {\n  const c = { Windows: \"#0078D4\", Linux: \"#1C1D1F\", \"OS X\": \"#555\", iOS: \"#111\", Android: \"#3DDC84\", VMWare: \"#607078\" }[os] || T.faint;\n  const mc = T.isLight ? \"#1B2030\" : \"#D6DAE6\"; // monochrome marks adapt to theme\n  const g = {\n    Windows: ,\n    \"OS X\": ,\n    iOS: ,\n    Android: ,\n    Linux: ,\n    VMWare: vm,\n  }[os];\n  if (!os) return \u2014;\n  return {g || }{os};\n}\nconst DEVICES = [\n  { name: \"PC-CURTIS-WILLIAMS\", host: \"PC-CURTIS-WILLIAMS.demo.local\", os: \"Windows\", ip: \"10.0.49.148\", mac: \"88:53:2E:12:45:C4\", seen: \"2026-06-08 09:34\", tags: [\"Corporate\", \"Managed\"], adapters: [\"active_directory_adapter\", \"crowd_strike_adapter\", \"epo_adapter\", \"google_mdm_adapter\", \"tanium_adapter\", \"sentinelone_adapter\", \"cisco_ise_adapter\"] },\n  { name: \"infranginx-5567897-stg\", host: \"esx-infranginx-5567897-stg.demo.local\", os: \"Linux\", ip: \"10.0.63.107\", mac: \"00:0C:29:12:55:38\", seen: \"2026-06-08 11:43\", tags: [\"Staging\", \"Cloud\"], adapters: [\"cisco_ise_adapter\", \"claroty_adapter\", \"counter_act_adapter\", \"epo_adapter\", \"tanium_adapter\", \"esx_adapter\", \"aws_adapter\"] },\n  { name: \"WIN-RUTHD\", host: \"WIN-RUTHD.demo.local\", os: \"Windows\", ip: \"10.0.48.107\", mac: \"88:53:2E:12:44:9B\", seen: \"2026-06-08 13:18\", tags: [\"Corporate\"], adapters: [\"active_directory_adapter\", \"cisco_adapter\", \"crowd_strike_adapter\", \"cylance_adapter\", \"epo_adapter\", \"tanium_adapter\"] },\n  { name: \"external3026786-prd\", host: \"sepio-external3026786-prd.demo.local\", os: \"Linux\", ip: \"10.0.64.36\", mac: \"88:53:2E:12:56:12\", seen: \"2026-06-08 05:54\", tags: [\"Production\", \"Internet-facing\"], adapters: [\"checkpoint_r80_adapter\", \"chef_adapter\", \"epo_adapter\", \"paloalto_panorama_adapter\", \"axonius_network_inspector_adapter\", \"claroty_adapter\", \"tenable_security_center_adapter\"] },\n  { name: \"macbook-pro-jdoe\", host: \"macbook-pro-jdoe.demo.local\", os: \"OS X\", ip: \"10.0.51.22\", mac: \"A4:83:E7:9C:11:04\", seen: \"2026-06-08 12:02\", tags: [\"Endpoint\", \"BYOD\"], adapters: [\"google_mdm_adapter\", \"crowd_strike_adapter\", \"sentinelone_adapter\"] },\n  { name: \"lablb-2918146-beta\", host: \"esx-lablb-2918146-beta.manufacturing.com\", os: \"Linux\", ip: \"10.0.56.90\", mac: \"00:0C:29:12:4C:BF\", seen: \"2026-06-08 11:30\", tags: [\"Lab\"], adapters: [\"cisco_meraki_adapter\", \"claroty_adapter\", \"epo_adapter\", \"esx_adapter\", \"tanium_asset_adapter\", \"counter_act_adapter\", \"service_now_adapter\"] },\n  { name: \"iphone-asmith\", host: \"\u2014\", os: \"iOS\", ip: \"10.0.77.51\", mac: \"F0:18:98:22:7D:AA\", seen: \"2026-06-08 11:58\", tags: [\"Mobile\", \"BYOD\"], adapters: [\"google_mdm_adapter\", \"zoom_adapter\"] },\n  { name: \"srv-db-fin-04\", host: \"srv-db-fin-04.healthcare-subsidiary.com\", os: \"Linux\", ip: \"10.0.40.12\", mac: \"00:50:56:9A:3C:11\", seen: \"2026-06-08 07:12\", tags: [\"Production\", \"Database\", \"PCI\"], adapters: [\"service_now_adapter\", \"tenable_security_center_adapter\", \"tanium_adapter\", \"epo_adapter\", \"aws_adapter\", \"claroty_adapter\"] },\n  { name: \"azure-infra9274676-prd\", host: \"azure-infra9274676-prd.demo.local\", os: \"Windows\", ip: \"10.0.61.5\", mac: \"00:0D:3A:1F:8E:22\", seen: \"2026-06-08 10:47\", tags: [\"Production\", \"Cloud\"], adapters: [\"active_directory_adapter\", \"aws_adapter\", \"sentinelone_adapter\", \"service_now_adapter\"] },\n  { name: \"android-pixel-7\", host: \"\u2014\", os: \"Android\", ip: \"10.0.78.130\", mac: \"3C:28:6D:55:01:9F\", seen: \"2026-06-08 09:05\", tags: [\"Mobile\"], adapters: [\"google_mdm_adapter\"] },\n  { name: \"Win-MaryChasse\", host: \"WIN-MARYCHASSE.demo.local\", os: \"Windows\", ip: \"10.0.49.201\", mac: \"88:53:2E:12:48:7E\", seen: \"2026-06-07 22:41\", tags: [\"Corporate\"], adapters: [\"active_directory_adapter\", \"epo_adapter\", \"cisco_meraki_adapter\"] },\n  { name: \"esx-mail-2231-prd\", host: \"esx-mail-2231-prd.manufacturing.com\", os: \"VMWare\", ip: \"10.0.62.18\", mac: \"00:0C:29:44:1A:E0\", seen: \"2026-06-08 06:33\", tags: [\"Production\"], adapters: [\"esx_adapter\", \"tenable_security_center_adapter\", \"paloalto_panorama_adapter\"] },\n  { name: \"PC-Doris9920\", host: \"PC-DORIS9920.demo.local\", os: \"Windows\", ip: \"10.0.50.66\", mac: \"88:53:2E:12:51:C9\", seen: \"2026-06-08 08:59\", tags: [\"Corporate\", \"Managed\"], adapters: [\"active_directory_adapter\", \"crowd_strike_adapter\", \"tanium_adapter\", \"google_mdm_adapter\"] },\n  { name: \"kiosk-lobby-03\", host: \"kiosk-lobby-03.demo.local\", os: \"Linux\", ip: \"10.0.44.7\", mac: \"B8:27:EB:0C:5A:31\", seen: \"2026-06-07 19:20\", tags: [\"IoT\", \"Unmanaged\"], adapters: [\"counter_act_adapter\", \"claroty_adapter\"] },\n];\n\nconst INIT_COLS = [\n  { key: \"adapters\", label: \"Adapter Connections\", w: 230, pinned: false },\n  { key: \"name\", label: \"Asset Name\", w: 230, pinned: false },\n  { key: \"host\", label: \"Host Name\", w: 300, pinned: false },\n  { key: \"os\", label: \"OS Type\", w: 140, pinned: false },\n  { key: \"ip\", label: \"IP Address\", w: 140, pinned: false },\n  { key: \"mac\", label: \"MAC Address\", w: 160, pinned: false },\n  { key: \"seen\", label: \"Last Seen (UTC)\", w: 170, pinned: false },\n  { key: \"tags\", label: \"Tags\", w: 220, pinned: false },\n];\nconst CB_W = 46;\nconst mono = { fontFamily: \"ui-monospace, 'SF Mono', monospace\" };\nfunction cellContent(key, r) {\n  switch (key) {\n    case \"adapters\": return ;\n    case \"name\": return {r.name};\n    case \"host\": return {r.host};\n    case \"os\": return ;\n    case \"ip\": return {r.ip};\n    case \"mac\": return {r.mac};\n    case \"seen\": return {r.seen};\n    case \"tags\": return {r.tags.slice(0, 2).map((t, j) =&gt; )}{r.tags.length &gt; 2 &amp;&amp; +{r.tags.length - 2}};\n    default: return null;\n  }\n}\n\nfunction HeaderCell({ col, left, isLastPinned, onResize, onPin, onDragStart, regRef, dragging }) {\n  const [h, setH] = useState(false);\n  const sticky = col.pinned;\n  return (\n    \n regRef(col.key, el)} onPointerDown={(e) =&gt; onDragStart(e, col.key)} onMouseEnter={() =&gt; setH(true)} onMouseLeave={() =&gt; setH(false)}\n      style={{ width: col.w, flexShrink: 0, position: sticky ? \"sticky\" : \"relative\", left: sticky ? left : undefined, zIndex: sticky ? 4 : 1, background: T.canvas, height: 40, display: \"flex\", alignItems: \"center\", gap: 6, padding: \"0 12px\", boxShadow: isLastPinned ? \"2px 0 5px rgba(27,32,70,0.06)\" : \"none\", cursor: \"grab\", opacity: dragging ? 0.4 : 1 }}&gt;\n      {col.label}\n      {(h || col.pinned) &amp;&amp; (\n         onPin(col.key)} title={col.pinned ? \"Unpin column\" : \"Pin column\"}\n          style={{ width: 24, height: 24, borderRadius: 6, border: \"none\", cursor: \"pointer\", background: \"transparent\", color: col.pinned ? T.blue : T.faint, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", transform: col.pinned ? \"none\" : \"rotate(45deg)\", flexShrink: 0 }}&gt;\n          \n        \n      )}\n      \n onResize(e, col.key)} data-colresize title=\"Drag to resize\"\n        style={{ position: \"absolute\", right: -3, top: 8, width: 6, height: 24, cursor: \"col-resize\", display: \"flex\", justifyContent: \"center\", zIndex: 5 }}&gt;\n        \n      \n    \n  );\n}\n\nfunction AssetsTable() {\n  const [columns, setColumns] = useState(INIT_COLS);\n  const [hoverRow, setHoverRow] = useState(null);\n  const [perPage, setPerPage] = useState(20);\n  const [dragCol, setDragCol] = useState(null);\n  const colEls = useRef({});\n  const regRef = (k, el) =&gt; { colEls.current[k] = el; };\n\n  const startColDrag = (e, key) =&gt; {\n    if (e.button || (e.target.closest &amp;&amp; (e.target.closest(\"[data-colresize]\") || e.target.closest(\"button\")))) return;\n    const startX = e.clientX; let dragging = false;\n    const onMove = (ev) =&gt; {\n      if (!dragging) { if (Math.abs(ev.clientX - startX) &gt; 6) { dragging = true; setDragCol(key); document.body.style.userSelect = \"none\"; } else return; }\n      for (const k in colEls.current) {\n        const el = colEls.current[k]; if (!el || k === key) continue;\n        const r = el.getBoundingClientRect();\n        if (ev.clientX &gt;= r.left &amp;&amp; ev.clientX &lt;= r.right) {\n          setColumns(cs =&gt; { const from = cs.findIndex(c =&gt; c.key === key), to = cs.findIndex(c =&gt; c.key === k); if (from &lt; 0 || to &lt; 0 || from === to) return cs; const n = cs.slice(); const [m] = n.splice(from, 1); n.splice(to, 0, m); return n; });\n          break;\n        }\n      }\n    };\n    const onUp = () =&gt; { dragging = false; setDragCol(null); document.body.style.userSelect = \"\"; window.removeEventListener(\"pointermove\", onMove); window.removeEventListener(\"pointerup\", onUp); };\n    window.addEventListener(\"pointermove\", onMove); window.addEventListener(\"pointerup\", onUp);\n  };\n\n  const startColResize = (e, key) =&gt; {\n    e.preventDefault(); e.stopPropagation();\n    const sx = e.clientX, startW = columns.find(c =&gt; c.key === key).w;\n    const onMove = (ev) =&gt; { const w = Math.max(90, startW + (ev.clientX - sx)); setColumns(cs =&gt; cs.map(c =&gt; c.key === key ? { ...c, w } : c)); };\n    const onUp = () =&gt; { document.body.style.cursor = \"\"; window.removeEventListener(\"mousemove\", onMove); window.removeEventListener(\"mouseup\", onUp); };\n    document.body.style.cursor = \"col-resize\";\n    window.addEventListener(\"mousemove\", onMove); window.addEventListener(\"mouseup\", onUp);\n  };\n  const togglePin = (key) =&gt; setColumns(cs =&gt; cs.map(c =&gt; c.key === key ? { ...c, pinned: !c.pinned } : c));\n\n  const pinned = columns.filter(c =&gt; c.pinned), unpinned = columns.filter(c =&gt; !c.pinned);\n  const ordered = [...pinned, ...unpinned];\n  const leftMap = {}; let acc = CB_W;\n  pinned.forEach(c =&gt; { leftMap[c.key] = acc; acc += c.w; });\n  const lastPinned = pinned.length ? pinned[pinned.length - 1].key : null;\n  const totalW = CB_W + columns.reduce((a, c) =&gt; a + c.w, 0);\n\n  return (\n    \n\n      \n\n        \n\n          {/* header */}\n          \n\n            \n\n              \n            \n            {ordered.map(c =&gt; )}\n            \n\n          \n          {/* rows */}\n          {DEVICES.map((r, i) =&gt; {\n            const rowBg = hoverRow === i ? T.surface2 : T.surface;\n            return (\n              \n setHoverRow(i)} onMouseLeave={() =&gt; setHoverRow(null)}\n                style={{ display: \"flex\", height: 52, borderBottom: i &lt; DEVICES.length - 1 ? `1px solid ${T.hair}` : \"none\", background: rowBg }}&gt;\n                \n\n                  {hoverRow === i ?  : {i + 1}}\n                \n                {ordered.map(c =&gt; {\n                  const sticky = c.pinned;\n                  return (\n                    \n\n                      {cellContent(c.key, r)}\n                    \n                  );\n                })}\n                \n\n              \n            );\n          })}\n        \n      \n      {/* footer */}\n      \n\n        \n\n          Results per page:\n          \n\n            {[20, 50, 100].map(n =&gt;  setPerPage(n)} style={{ minWidth: 30, height: 28, borderRadius: 8, border: \"none\", cursor: \"pointer\", fontFamily: T.font, fontSize: 13, fontWeight: 500, ...tnum, background: perPage === n ? T.blue : \"transparent\", color: perPage === n ? T.white : T.body }}&gt;{n})}\n          \n        \n        \n\n          1\u2013{perPage} of 12,409\n          \n\n            \n            1234\n            \u2026621\n            \n          \n        \n      \n    \n  );\n}\n\nfunction QueryPill({ children, primary, active, onClick }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, height: 32, padding: \"0 14px\", borderRadius: 999, cursor: \"pointer\", fontFamily: T.font, fontSize: 13.5, fontWeight: 600, border: primary ? \"none\" : `1px solid ${active || h ? T.lineStrong : T.line}`, background: primary ? T.blue : active || h ? T.surface2 : T.control, color: primary ? T.onAccent : T.body }}&gt;{children};\n}\nfunction ToolLink({ icon: Icon, children }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ display: \"inline-flex\", alignItems: \"center\", gap: 6, height: 30, padding: \"0 10px\", border: \"none\", borderRadius: 999, background: h ? T.surface2 : \"transparent\", color: T.body, fontSize: 13.5, fontWeight: 600, cursor: \"pointer\", fontFamily: T.font }}&gt;{Icon &amp;&amp; }{children};\n}\nfunction SegTool({ icon: Icon, title, first }) {\n  const [h, setH] = useState(false);\n  return (\n     setH(true)} onMouseLeave={() =&gt; setH(false)}&gt;\n      \n      {h &amp;&amp; {title}}\n    \n  );\n}\nfunction IconTool({ icon: Icon, title }) {\n  const [h, setH] = useState(false);\n  return (\n     setH(true)} onMouseLeave={() =&gt; setH(false)}&gt;\n      \n      {h &amp;&amp; {title}}\n    \n  );\n}\nfunction Segmented({ options, value, onChange }) {\n  const idx = Math.max(0, options.indexOf(value));\n  const wpct = 100 / options.length;\n  return (\n    \n\n      \n      {options.map(o =&gt; (\n         onChange(o)} style={{ position: \"relative\", zIndex: 1, minWidth: 62, padding: \"0 12px\", border: \"none\", background: \"transparent\", color: value === o ? T.onAccent : T.body, fontSize: 13.5, fontWeight: value === o ? 600 : 500, cursor: \"pointer\", fontFamily: T.font, transition: \"color .2s\" }}&gt;{o}\n      ))}\n    \n  );\n}\n\nfunction QueriesPanel({ close }) {\n  const recent = [\n    { nm: \"FH: 1.4 Asset Context.Business Importance\", sub: \"Shared Queries\" },\n    { nm: \"low Risk Score_2025-12-24T11-53\", sub: \"Public Queries / Predefined Queries\" },\n    { nm: \"Unmanaged endpoints seen &lt; 7d\", sub: \"Shared Queries\" },\n    { nm: \"Critical CVEs on internet-facing\", sub: \"Public Queries / Predefined Queries\" },\n  ];\n  return createPortal(&lt;&gt;\n    \n\n    \n\n      \n\n        Queries\n        \n        \n        \n      \n      \n\n        \n\n          \n        \n      \n      \n\n        \nFavorites\n        \nRecently Used Saved Queries\n        {recent.map((q, i) =&gt; (\n           (e.currentTarget.style.background = T.surface2)} onMouseLeave={(e) =&gt; (e.currentTarget.style.background = \"transparent\")}\n            style={{ display: \"flex\", alignItems: \"center\", gap: 11, width: \"100%\", border: \"none\", background: \"transparent\", borderRadius: 10, cursor: \"pointer\", padding: \"9px\", textAlign: \"left\", fontFamily: T.font }}&gt;\n            \n            \n              {q.nm}\n              {q.sub}\n            \n          \n        ))}\n      \n    \n  , document.body);\n}\nfunction AssetsView() {\n  const [mode, setMode] = useState(\"Wizard\");\n  const [enrichOpen, setEnrichOpen] = useState(false);\n  const [queriesOpen, setQueriesOpen] = useState(false);\n  return (\n    \n\n      \n\n      {/* breadcrumb + title + module actions */}\n      \n\n        \n\n          \nInventory / Assets\n          \nDevices\n        \n        \n\n          \n\n             setEnrichOpen(o =&gt; !o)}&gt;Enrichment &amp; Investigation \n            {enrichOpen &amp;&amp; (&lt;&gt;\n              \n setEnrichOpen(false)} style={{ position: \"fixed\", inset: 0, zIndex: 40 }} /&gt;\n              \n\n                {[\"Business Data Enrichment\", \"Device Inventory Classification\", \"Asset Investigation\"].map(o =&gt; (\n                   setEnrichOpen(false)} onMouseEnter={(e) =&gt; (e.currentTarget.style.background = T.surface2)} onMouseLeave={(e) =&gt; (e.currentTarget.style.background = \"transparent\")}\n                    style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", gap: 10, width: \"100%\", border: \"none\", background: \"transparent\", borderRadius: 8, cursor: \"pointer\", padding: \"9px 11px\", fontSize: 13, fontWeight: 500, fontFamily: T.font, color: T.body, textAlign: \"left\" }}&gt;\n                    {o} \n                  \n                ))}\n              \n            )}\n          \n          \n        \n      \n      {/* query toolbar */}\n      \n\n        New Query\n        Save As\n        Reset\n        Copy Query\n        \n\n        Display by Date\n        \n\n         setQueriesOpen(o =&gt; !o)}&gt;{queriesOpen ?  : &lt;&gt; Queries}\n        \n      \n      {/* search + wizard / basic filters */}\n      {mode === \"Wizard\" ? (\n        \n\n          \n\n            \n            \n          \n          Query Wizard\n        \n      ) : (\n        \n\n          \n          \n          \nLast Seen\n          \nTags\n           Filter\n        \n      )}\n      {/* stats toolbar */}\n      \n\n        Total 12,409\n         Last updated: 2026-06-08 17:07:59\n        \n\n        \n\n          \n\n            \n            \n            \n          \n           New Asset\n        \n      \n      {/* end flexShrink header */}\n      \n\n        \n      \n      {queriesOpen &amp;&amp;  setQueriesOpen(false)} /&gt;}\n    \n  );\n}\nfunction PageBtn({ children, active, disabled, wide }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ minWidth: wide ? \"auto\" : 30, height: 30, padding: wide ? \"0 10px\" : 0, borderRadius: 999, border: active ? \"none\" : `1px solid ${h &amp;&amp; !disabled ? T.lineStrong : \"transparent\"}`, background: active ? T.blue : h &amp;&amp; !disabled ? T.surface2 : \"transparent\", color: active ? T.onAccent : disabled ? T.faint : T.body, fontSize: 13, fontWeight: 500, cursor: disabled ? \"default\" : \"pointer\", display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\", fontFamily: T.font, ...tnum }}&gt;{children};\n}\n\n/* ============ Inventory tree panel (assets view) ============ */\nconst INV_GROUPS_INIT = [\n  { name: \"Compute\", icon: Server, open: true, items: [\n    { icon: Server, label: \"Corporate Devices\", count: \"8,204\", fav: true },\n    { icon: ShieldAlert, label: \"Internet-facing Assets\", count: \"312\", fav: true },\n    { folder: \"Endpoint\", open: true, items: [\n      { icon: Server, label: \"Devices\", count: \"12,409\", active: true },\n      { icon: Workflow, label: \"Processes\", count: \"0\" },\n    ]},\n    { folder: \"Infrastructure\", open: false, items: [\n      { icon: Boxes, label: \"Compute Services\", count: \"14\" },\n      { icon: Cable, label: \"Databases\", count: \"36\" },\n      { icon: Boxes, label: \"Containers\", count: \"258\" },\n      { icon: Cable, label: \"Serverless Functions\", count: \"103\" },\n      { icon: Boxes, label: \"Compute Images\", count: \"25\" },\n    ]},\n    { icon: Settings, label: \"Configurations\", count: \"0\" },\n    { icon: Clock, label: \"Latest Devices\", count: \"12,409\" },\n  ] },\n  { name: \"Identity\", icon: Users, open: false, items: [\n    { folder: \"Directory\", open: false, items: [\n      { icon: Users, label: \"Users\", count: \"4,821\" },\n      { icon: Users, label: \"Groups\", count: \"312\" },\n    ]},\n    { icon: ShieldAlert, label: \"Service Accounts\", count: \"87\" },\n  ] },\n  { name: \"Applications\", icon: Layers, open: false, items: [\n    { icon: Layers, label: \"SaaS Apps\", count: \"143\" },\n    { icon: Layers, label: \"Installed Software\", count: \"2,904\" },\n  ] },\n  { name: \"Tickets\", icon: Bell, open: false, items: [\n    { icon: Bell, label: \"Open Tickets\", count: \"48\" },\n    { icon: Bell, label: \"Resolved\", count: \"203\" },\n  ] },\n];\nfunction InventoryPanel({ collapsed, setCollapsed }) {\n  const [groups, setGroups] = useState(INV_GROUPS_INIT);\n  const [favOpen, setFavOpen] = useState(true);\n\n  const collectItems = (items) =&gt; items.flatMap(it =&gt; it.folder ? collectItems(it.items) : [it]);\n  const favItems = groups.flatMap(g =&gt; collectItems(g.items)).filter(it =&gt; it.fav);\n\n  const toggleFavInItems = (items, label) =&gt; items.map(it =&gt;\n    it.folder ? { ...it, items: toggleFavInItems(it.items, label) }\n              : it.label === label ? { ...it, fav: !it.fav } : it\n  );\n  const toggleFav = (label) =&gt; setGroups(gs =&gt; gs.map(g =&gt; ({ ...g, items: toggleFavInItems(g.items, label) })));\n  if (collapsed) {\n    const compute = groups.find(g =&gt; g.name === \"Compute\");\n    return (\n      \n\n         setCollapsed(false)} title=\"Expand inventory\" className=\"ax-edge-collapse\"\n          style={{ position: \"absolute\", top: 16, right: -14, zIndex: 6, width: 28, height: 28, borderRadius: \"50%\", border: `1px solid ${T.line}`, background: T.surface, color: T.muted, cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", boxShadow: T.shadow }}&gt;\n          \n        \n        \n\n          \n        \n        \n\n        {(compute ? compute.items : []).map((it, i) =&gt; {\n          const Icon = it.icon;\n          return (\n            \n\n              \n            \n          );\n        })}\n      \n    );\n  }\n  return (\n    \n\n       setCollapsed(true)} title=\"Collapse panel\" className=\"ax-edge-collapse\"\n        style={{ position: \"absolute\", top: 16, right: -14, zIndex: 6, width: 28, height: 28, borderRadius: \"50%\", border: `1px solid ${T.line}`, background: T.surface, color: T.muted, cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", boxShadow: T.shadow }}&gt;\n        \n      \n      \n\n        \n\n          \n        \n      \n      \n\n        \n\n          \n setFavOpen(o =&gt; !o)} style={{ display: \"flex\", alignItems: \"center\", gap: 9, padding: \"9px 8px\", cursor: \"pointer\", borderRadius: 8, color: T.body }}&gt;\n            \n            \n            Favorites\n            {favItems.length}\n          \n          {favOpen &amp;&amp; favItems.map((it, ii) =&gt; )}\n        \n        {groups.map((g, gi) =&gt; { const Icon = g.icon; return (\n          \n\n            \n setGroups(gs =&gt; gs.map((x, i) =&gt; i === gi ? { ...x, open: !x.open } : x))}\n              style={{ display: \"flex\", alignItems: \"center\", gap: 9, padding: \"9px 8px\", cursor: \"pointer\", borderRadius: 8, color: g.open ? T.accentText : T.body }}&gt;\n              \n              \n              {g.name}\n            \n            {g.open &amp;&amp; g.items.map((it, ii) =&gt; it.folder ?  : )}\n          \n        ); })}\n      \n    \n  );\n}\nfunction InvFolder({ folder, toggleFav }) {\n  const [open, setOpen] = useState(folder.open);\n  const [h, setH] = useState(false);\n  const FIcon = open ? FolderOpen : Folder;\n  return (\n    \n\n      \n setH(true)} onMouseLeave={() =&gt; setH(false)} onClick={() =&gt; setOpen(o =&gt; !o)}\n        style={{ display: \"flex\", alignItems: \"center\", gap: 8, height: 34, padding: \"0 8px 0 22px\", borderRadius: 8, cursor: \"pointer\", background: h ? T.surface2 : \"transparent\", transition: \"background .12s\" }}&gt;\n        \n        \n        {folder.folder}\n      \n      {open &amp;&amp; folder.items.map((it, i) =&gt; )}\n    \n  );\n}\nfunction InvRow({ icon: Icon, label, count, active, fav, indent, toggleFav }) {\n  const [h, setH] = useState(false);\n  const left = indent || 28;\n  return (\n    \n setH(true)} onMouseLeave={() =&gt; setH(false)}\n      style={{ position: \"relative\", display: \"flex\", alignItems: \"center\", gap: 11, height: 36, margin: \"1px 0\", padding: `0 8px 0 ${left}px`, borderRadius: 8, cursor: \"pointer\", background: active ? T.accentSoft : h ? T.surface2 : \"transparent\", transition: \"background .14s\" }}&gt;\n      \n      {label}\n      {(h || fav) &amp;&amp; toggleFav &amp;&amp; (\n         { e.stopPropagation(); toggleFav(label); }} title={fav ? \"Remove from favorites\" : \"Add to favorites\"} className=\"ax-star\"\n          style={{ width: 22, height: 22, borderRadius: 6, border: \"none\", background: \"transparent\", cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", padding: 0, flexShrink: 0 }}&gt;\n          \n        \n      )}\n      {!h &amp;&amp; !fav &amp;&amp; {count}}\n    \n  );\n}\n\n/* ============ chrome ============ */\nfunction AppHeader({ theme, setTheme }) {\n  return (\n    \n\n      \n\n        \n      \n      \n\n        \n\n          Search Axonius\n          \u2318K\n        \n      \n      \n\n        New navigation BETA\n         setTheme(theme === \"dark\" ? \"light\" : \"dark\")} title=\"Toggle light / dark\"&gt;{theme === \"dark\" ?  : }\n        \n        \n        \nSB\n      \n    \n  );\n}\nfunction HBtn({ children, onClick, title }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ width: 32, height: 32, borderRadius: 8, border: \"none\", cursor: \"pointer\", background: h ? T.surface2 : \"transparent\", color: h ? T.ink : T.bg80, display: \"flex\", alignItems: \"center\", justifyContent: \"center\" }}&gt;{children};\n}\n\nconst HICONS = {\n  dashboard: '',\n  inventory: '',\n  shield: '',\n  bolt: '',\n  link: '',\n  chart: '',\n  cog: '',\n};\nfunction HIcon({ name, size = 20, color = \"currentColor\", sw = 1.6, className }) {\n  return ${HICONS[name] || \"\"}` }} /&gt;;\n}\nconst RAIL = [\n  { key: \"dashboard\", hi: \"dashboard\", label: \"Dashboard\" }, { key: \"assets\", hi: \"inventory\", label: \"Inventory\" },\n  { key: \"exposures\", hi: \"shield\", label: \"Exposures\" }, { key: \"action\", hi: \"bolt\", label: \"Action Center\" },\n  { key: \"adapters\", hi: \"link\", label: \"Adapters\" }, { key: \"analysis\", hi: \"chart\", label: \"Analysis\" },\n];\nfunction IconRail({ view, setView }) {\n  return (\n    \n\n      {RAIL.map(r =&gt;  setView(r.key)} /&gt;)}\n      \n\n       {}} /&gt;\n      \n\n    \n  );\n}\nfunction RailGlyph({ hi, label, active, onClick }) {\n  const [h, setH] = useState(false);\n  return (\n    \n setH(true)} onMouseLeave={() =&gt; setH(false)} title={label} className=\"ax-rail ax-press\"\n      style={{ position: \"relative\", width: 44, height: 44, borderRadius: 14, cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", background: active ? T.accentSoft : h ? T.surface2 : \"transparent\", transition: \"background .15s\" }}&gt;\n      \n    \n  );\n}\n\nconst DASH_TREE_INIT = [\n  { g: \"Favorites\", open: true, items: [\n    { nm: \"My Dashboard\", fav: true }, { nm: \"Asset Profile - Copy_2024-06-09\", fav: true },\n    { nm: \"Axonius Dashboard\", fav: true, active: true, def: true }, { nm: \"Cost Optimization\", fav: true }, { nm: \"Cloud Compliance\", fav: true },\n  ] },\n  { g: \"Public\", open: false, items: [\n    { folder: \"Security\", open: false, items: [{ nm: \"SecOps Overview\" }, { nm: \"Threat Intelligence\" }] },\n    { folder: \"Executive\", open: false, items: [{ nm: \"Executive Summary\" }, { nm: \"Board Report\" }] },\n    { nm: \"Asset Inventory V 1.0\" }, { nm: \"Active Directory Insights\" },\n  ] },\n  { g: \"Shared\", open: false, items: [\n    { folder: \"Team SOC\", open: false, items: [{ nm: \"Vulnerability Posture\" }, { nm: \"Incident Response\" }] },\n    { nm: \"Cloud Inventory\" }, { nm: \"Compliance Overview\" },\n  ] },\n  { g: \"Private\", open: false, items: [{ nm: \"My Drafts\" }, { nm: \"Work in Progress\" }] },\n  { g: \"Managed by Axonius\", open: false, items: [{ nm: \"Device Overview\" }, { nm: \"User Overview\" }, { nm: \"Adapters Health\" }, { nm: \"Coverage &amp; Gaps\" }] },\n];\nfunction PanelBtn({ primary, filled, icon: Icon, children, onClick }) {\n  const [h, setH] = useState(false);\n  return (\n     setH(true)} onMouseLeave={() =&gt; setH(false)} className={filled ? \"ax-primary\" : \"ax-press\"}\n      style={{ height: 38, borderRadius: 999, border: filled ? \"none\" : primary ? `1.5px solid ${T.accentText}` : `1px solid ${h ? T.lineStrong : T.line}`, cursor: \"pointer\", width: \"100%\", background: filled ? (h ? T.blueDeep : T.blue) : primary ? (h ? T.accentSoft : \"transparent\") : h ? T.surface2 : \"transparent\", color: filled ? T.onAccent : primary ? T.accentText : T.body, fontSize: 13, fontWeight: 600, fontFamily: T.font, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: 7, boxShadow: \"none\", transition: \"background .14s ease, border-color .14s ease\" }}&gt;\n       {children}\n    \n  );\n}\nfunction CreateDashModal({ onCreate, close }) {\n  const [name, setName] = useState(\"\");\n  const [tpl, setTpl] = useState(\"blank\");\n  const tpls = [{ k: \"blank\", t: \"Blank dashboard\", d: \"Start from an empty canvas\" }, { k: \"template\", t: \"From a template\", d: \"Prebuilt charts to tweak\" }];\n  return createPortal(\n    \n\n      \n e.stopPropagation()} style={{ width: 480, maxWidth: \"92vw\", background: T.canvas, border: `1px solid ${T.line}`, borderRadius: 14, boxShadow: \"0 30px 80px rgba(0,0,0,.55)\", overflow: \"hidden\", animation: \"axRise .26s cubic-bezier(.22,1,.36,1) both\" }}&gt;\n        \n\n          Create dashboard\n          \n        \n        \n\n          \n\n            Dashboard name\n             setName(e.target.value)} placeholder=\"e.g. Security Posture\" style={{ width: \"100%\", height: 40, padding: \"0 14px\", border: `1px solid ${T.line}`, borderRadius: 8, background: T.control, color: T.ink, fontFamily: T.font, fontSize: 14, outline: \"none\", boxSizing: \"border-box\" }} /&gt;\n          \n          \n\n            Starting point\n            \n\n              {tpls.map(o =&gt; (\n                 setTpl(o.k)} style={{ flex: 1, textAlign: \"left\", padding: \"12px 13px\", borderRadius: 10, border: `1.5px solid ${tpl === o.k ? T.accentText : T.line}`, background: tpl === o.k ? T.accentSoft : \"transparent\", cursor: \"pointer\", fontFamily: T.font }}&gt;\n                  \n{o.t}\n                  \n{o.d}\n                \n              ))}\n            \n          \n        \n        \n\n          Cancel\n           name.trim() &amp;&amp; onCreate(name.trim())} className=\"ax-press\" style={{ height: 38, padding: \"0 18px\", borderRadius: 999, border: \"none\", background: name.trim() ? T.blue : T.surface2, color: name.trim() ? T.onAccent : T.faint, fontSize: 13.5, fontWeight: 600, cursor: name.trim() ? \"pointer\" : \"default\", fontFamily: T.font }}&gt;Create dashboard\n        \n      \n    , document.body);\n}\nfunction SidePanel({ collapsed, setCollapsed, activeDash, setActiveDash }) {\n  const [tree, setTree] = useState(DASH_TREE_INIT);\n  const [menu, setMenu] = useState(null);\n  const [editing, setEditing] = useState(null);\n  const [createOpen, setCreateOpen] = useState(false);\n\n  const mut = (fn) =&gt; setTree(t =&gt; fn(t.map(g =&gt; ({ ...g, items: g.items.map(it =&gt; ({ ...it })) }))));\n  const toggleGroup = (gi) =&gt; setTree(t =&gt; t.map((g, i) =&gt; i === gi ? { ...g, open: !g.open } : g));\n  const setActive = (gi, ii) =&gt; { mut(t =&gt; { t.forEach(g =&gt; g.items.forEach(it =&gt; it.active = false)); t[gi].items[ii].active = true; return t; }); if (setActiveDash) setActiveDash(tree[gi].items[ii].nm); };\n  const toggleFav = (gi, ii) =&gt; mut(t =&gt; { t[gi].items[ii].fav = !t[gi].items[ii].fav; return t; });\n  const setDefault = (gi, ii) =&gt; mut(t =&gt; { t.forEach(g =&gt; g.items.forEach(it =&gt; it.def = false)); t[gi].items[ii].def = true; return t; });\n  const duplicate = (gi, ii) =&gt; mut(t =&gt; { t[gi].items.splice(ii + 1, 0, { nm: t[gi].items[ii].nm + \" (copy)\" }); return t; });\n  const rename = (gi, ii, nm) =&gt; mut(t =&gt; { t[gi].items[ii].nm = nm || t[gi].items[ii].nm; return t; });\n  const remove = (gi, ii) =&gt; mut(t =&gt; { t[gi].items.splice(ii, 1); return t; });\n  const createDash = (nm) =&gt; mut(t =&gt; { const p = t.find(g =&gt; g.g === \"Private\"); p.open = true; p.items.push({ nm: nm || \"Untitled dashboard\" }); return t; });\n  const openMenu = (gi, ii, e) =&gt; { const r = e.currentTarget.getBoundingClientRect(); setMenu({ gi, ii, top: r.bottom + 6, left: Math.max(8, r.right - 196) }); };\n\n  if (collapsed) {\n    const favItems = tree.flatMap(g =&gt; g.items).filter(it =&gt; it.fav || it.active);\n    return (\n      \n\n         setCollapsed(false)} title=\"Expand dashboards\" className=\"ax-edge-collapse\"\n          style={{ position: \"absolute\", top: 16, right: -14, zIndex: 6, width: 28, height: 28, borderRadius: \"50%\", border: `1px solid ${T.line}`, background: T.surface, color: T.muted, cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", boxShadow: T.shadow }}&gt;\n          \n        \n         setCreateOpen(true)} style={{ width: 38, height: 38, borderRadius: 8, border: `1.5px solid ${T.accentText}`, background: \"transparent\", cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", color: T.accentText }}&gt;\n          \n        \n        \n\n        {favItems.map((s, i) =&gt; (\n          \n\n            \n            {s.fav &amp;&amp; }\n          \n        ))}\n        {createOpen &amp;&amp;  { createDash(nm); setCreateOpen(false); }} close={() =&gt; setCreateOpen(false)} /&gt;}\n      \n    );\n  }\n  return (\n    \n\n       setCollapsed(true)} title=\"Collapse panel\" className=\"ax-edge-collapse\"\n        style={{ position: \"absolute\", top: 16, right: -14, zIndex: 6, width: 28, height: 28, borderRadius: \"50%\", border: `1px solid ${T.line}`, background: T.surface, color: T.muted, cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", boxShadow: T.shadow }}&gt;\n        \n      \n      \n\n         setCreateOpen(true)}&gt;Create Dashboard\n        Manage Dashboards\n      \n      \n\n        \n\n          \n        \n      \n      \n\n        {tree.map((g, gi) =&gt; (\n          \n        ))}\n      \n      {menu &amp;&amp;  setMenu(null)}\n        actions={{ rename: () =&gt; setEditing({ gi: menu.gi, ii: menu.ii }), duplicate, toggleFav, setDefault, remove }} /&gt;}\n      {createOpen &amp;&amp;  { createDash(nm); setCreateOpen(false); }} close={() =&gt; setCreateOpen(false)} /&gt;}\n    \n  );\n}\nconst DASH_CAT_ICON = { Favorites: Star, Public: Globe, Shared: Users, Private: Lock, \"Managed by Axonius\": ShieldCheck };\nfunction DashGroup({ group, gi, toggleGroup, setActive, toggleFav, openMenu, editing, setEditing, rename }) {\n  const Icon = DASH_CAT_ICON[group.g] || LayoutGrid;\n  return (\n    \n\n       toggleGroup(gi)} className=\"ax-catrow\"\n        style={{ display: \"flex\", alignItems: \"center\", gap: 9, width: \"100%\", border: \"none\", background: \"transparent\", cursor: \"pointer\", padding: \"8px 8px\", borderRadius: 8, fontFamily: T.font }}&gt;\n        \n        \n        {group.g}\n        {group.g === \"Favorites\" ? group.items.filter(it =&gt; it.fav !== false).length : group.items.length}\n      \n      {group.open &amp;&amp; group.items\n        .filter(it =&gt; group.g !== \"Favorites\" || it.fav !== false)\n        .map((it, ii) =&gt;\n          it.folder\n            ? \n            : \n        )}\n    \n  );\n}\nfunction DashFolder({ folder, indent, onToggle }) {\n  const [h, setH] = useState(false);\n  const [open, setOpen] = useState(folder.open);\n  const toggle = () =&gt; { setOpen(o =&gt; !o); if (onToggle) onToggle(!open); };\n  const FIcon = open ? FolderOpen : Folder;\n  return (\n    \n\n      \n setH(true)} onMouseLeave={() =&gt; setH(false)} onClick={toggle}\n        style={{ display: \"flex\", alignItems: \"center\", gap: 8, height: 34, padding: `0 8px 0 ${indent || 22}px`, borderRadius: 8, cursor: \"pointer\", background: h ? T.surface2 : \"transparent\", transition: \"background .12s\" }}&gt;\n        \n        \n        {folder.folder}\n      \n      {open &amp;&amp; folder.items.map((it, i) =&gt; (\n        \n e.currentTarget.style.background = T.surface2} onMouseLeave={(e) =&gt; e.currentTarget.style.background = \"transparent\"}&gt;\n          \n          {it.nm}\n        \n      ))}\n    \n  );\n}\nfunction DashItem({ item, gi, ii, setActive, toggleFav, openMenu, editing, setEditing, rename }) {\n  const [h, setH] = useState(false);\n  return (\n    \n setH(true)} onMouseLeave={() =&gt; setH(false)} onClick={() =&gt; !editing &amp;&amp; setActive(gi, ii)} className=\"ax-press\"\n      style={{ position: \"relative\", display: \"flex\", alignItems: \"center\", gap: 9, height: 36, padding: \"0 8px 0 28px\", margin: \"1px 0\", cursor: \"pointer\", borderRadius: 8, border: \"none\", background: item.active ? T.accentSoft : h ? T.surface2 : \"transparent\", transition: \"background .14s\" }}&gt;\n      \n      {editing ? (\n         e.stopPropagation()}\n          onBlur={(e) =&gt; { rename(gi, ii, e.target.value.trim()); setEditing(null); }}\n          onKeyDown={(e) =&gt; { if (e.key === \"Enter\") { rename(gi, ii, e.target.value.trim()); setEditing(null); } if (e.key === \"Escape\") setEditing(null); }}\n          style={{ flex: 1, minWidth: 0, border: `1px solid ${T.blue}`, borderRadius: 6, padding: \"2px 6px\", fontSize: 13.5, fontFamily: T.font, color: T.ink, outline: \"none\", background: T.surface }} /&gt;\n      ) : (\n        {item.nm}\n      )}\n      {(h || item.fav) &amp;&amp; !editing &amp;&amp; (\n         { e.stopPropagation(); toggleFav(gi, ii); }} title={item.fav ? \"Remove from favorites\" : \"Add to favorites\"} className=\"ax-star\"\n          style={{ width: 22, height: 22, borderRadius: 6, border: \"none\", background: \"transparent\", cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", padding: 0, flexShrink: 0 }}&gt;\n          \n        \n      )}\n      {h &amp;&amp; !editing &amp;&amp; (\n         { e.stopPropagation(); openMenu(gi, ii, e); }} title=\"More actions\"\n          style={{ width: 22, height: 22, borderRadius: 6, border: \"none\", background: \"transparent\", cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", padding: 0, flexShrink: 0, color: T.muted }}&gt;\n          \n        \n      )}\n    \n  );\n}\nfunction RowMenu({ menu, tree, close, actions }) {\n  const it = tree[menu.gi]?.items?.[menu.ii] || {};\n  const rows = [\n    { ic: Pencil, l: \"Rename\", fn: () =&gt; actions.rename() },\n    { ic: Copy, l: \"Duplicate\", fn: () =&gt; actions.duplicate(menu.gi, menu.ii) },\n    { ic: Star, l: it.fav ? \"Remove from favorites\" : \"Add to favorites\", fn: () =&gt; actions.toggleFav(menu.gi, menu.ii) },\n    { ic: Check, l: it.def ? \"Default dashboard\" : \"Set as default\", fn: () =&gt; actions.setDefault(menu.gi, menu.ii) },\n    { ic: Download, l: \"Export\", fn: () =&gt; {} },\n    { sep: true },\n    { ic: Trash2, l: \"Delete\", danger: true, fn: () =&gt; actions.remove(menu.gi, menu.ii) },\n  ];\n  return (\n    &lt;&gt;\n      \n\n      \n\n        {rows.map((a, i) =&gt; a.sep\n          ? \n\n          :  { a.fn(); close(); }}&gt;{a.l})}\n      \n    \n  );\n}\nfunction MenuRow({ icon: Icon, danger, children, onClick }) {\n  const [h, setH] = useState(false);\n  return (\n     setH(true)} onMouseLeave={() =&gt; setH(false)}\n      style={{ display: \"flex\", alignItems: \"center\", gap: 9, width: \"100%\", border: \"none\", borderRadius: 8, cursor: \"pointer\", padding: \"7px 9px\", fontSize: 13, fontFamily: T.font, fontWeight: 500, textAlign: \"left\", color: danger ? T.red : T.body, background: h ? (danger ? `${T.red}10` : T.canvas) : \"transparent\" }}&gt;\n       {children}\n    \n  );\n}\n\nfunction ModuleHeader({ view, activeDash }) {\n  const isDash = view === \"dashboard\";\n  return (\n    \n\n      \n{isDash ? \"Dashboards / Favorites\" : \"Assets / All Devices\"}\n      \n\n        \n\n          \n{isDash ? activeDash : \"Assets\"}\n          {isDash &amp;&amp; }\n        \n        \n\n          {isDash ? &lt;&gt;\n             Updated 2m ago\n            Add Chart\n           : &lt;&gt;\n            Export\n            New Query\n          }\n          \n        \n      \n    \n  );\n}\nfunction HdrBtn({ icon: Icon, children, primary, filled }) {\n  const [h, setH] = useState(false);\n  return  setH(true)} onMouseLeave={() =&gt; setH(false)} style={{ display: \"flex\", alignItems: \"center\", gap: 6, height: 34, padding: \"0 14px\", borderRadius: 999, cursor: \"pointer\", fontFamily: T.font, fontSize: 13.5, fontWeight: 600, border: filled ? \"none\" : (primary ? `1.5px solid ${T.accentText}` : `1px solid ${h ? T.lineStrong : T.line}`), background: filled ? (h ? T.blueDeep : T.blue) : (primary ? (h ? T.accentSoft : \"transparent\") : (h ? T.surface2 : T.control)), color: filled ? T.onAccent : (primary ? T.accentText : T.body) }}&gt;{children};\n}\n\nfunction AxoniusApp() {\n  const [view, setView] = useState(\"dashboard\");\n  const [collapsed, setCollapsed] = useState(false);\n  const [invCollapsed, setInvCollapsed] = useState(false);\n  const [theme, setTheme] = useState(\"dark\");\n  const [activeDash, setActiveDash] = useState(\"Axonius Dashboard\");\n  T = THEMES[theme];\n  useEffect(() =&gt; {\n    document.body.style.background = THEMES[theme].shell;\n    const el = document.documentElement;\n    el.classList.add(\"theming\");\n    const id = setTimeout(() =&gt; el.classList.remove(\"theming\"), 450);\n    return () =&gt; clearTimeout(id);\n  }, [theme]);\n  return (\n    \n\n      {`@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700&amp;family=Schibsted+Grotesk:wght@500;600;700&amp;display=swap'); * { box-sizing: border-box; } .theming, .theming * { transition: background-color .4s ease, border-color .4s ease, color .35s ease, fill .3s ease, stroke .3s ease !important; } *::-webkit-scrollbar { width: 9px; height: 9px; } *::-webkit-scrollbar-thumb { background: ${T.bg40}; border-radius: 5px; } *::-webkit-scrollbar-track { background: transparent; }\n        .ax-tile *::-webkit-scrollbar-thumb { background: transparent; transition: background .2s ease; }\n        .ax-tile:hover *::-webkit-scrollbar-thumb { background: ${T.bg40}; }\n        .ax-tbl::-webkit-scrollbar-thumb { background: transparent; } .ax-tbl:hover::-webkit-scrollbar-thumb { background: ${T.bg40}; } .ax-tbl::-webkit-scrollbar-corner { background: transparent; }\n        input::placeholder { color: ${T.muted}; opacity: 1; }\n        .ax-catrow:hover { background: ${T.surface2}; }\n        .ax-catrow:hover .ax-favbtn { opacity: 1 !important; }\n        .ax-edge-collapse { opacity: 0; transform: scale(.8); transition: opacity .15s ease, transform .15s ease; }\n        .ax-sidepanel:hover .ax-edge-collapse { opacity: 1; transform: scale(1); }\n        button, [role=\"button\"] { transition: transform .14s cubic-bezier(.34,1.56,.64,1), background-color .15s ease, box-shadow .18s ease, border-color .15s ease, color .12s ease; }\n        button:not(:disabled):active { transform: scale(.92); }\n        .ax-press { transition: transform .14s cubic-bezier(.34,1.56,.64,1), background-color .14s ease; }\n        .ax-press:active { transform: scale(.95); }\n        .ax-card { transition: transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s ease, border-color .15s ease; }\n        .ax-card:hover { border-color: ${T.lineStrong} !important; box-shadow: ${T.shadow} !important; }\n        .ax-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 16px rgba(37,99,235,.4) !important; }\n        .ax-primary:active { transform: translateY(0) scale(.96); }\n        @keyframes axRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }\n        .ax-rise { animation: axRise .45s cubic-bezier(.22,1,.36,1) both; }\n        @keyframes axJiggle { 0%, 100% { transform: rotate(-0.5deg); } 50% { transform: rotate(0.5deg); } }\n        .ax-jiggle { animation: axJiggle .32s ease-in-out infinite; }\n        @keyframes axPop { 0% { transform: scale(1); } 45% { transform: scale(1.25) rotate(-8deg); } 100% { transform: scale(1); } }\n        .ax-star:hover { animation: axPop .4s ease; }\n        @keyframes axSpin { to { transform: rotate(360deg); } }\n        .ax-spin:hover { animation: axSpin .6s ease; }\n        button:not(:disabled):hover { transform: translateY(-1px); }\n        @keyframes axGrow { from { transform: scaleX(0); } to { transform: scaleX(1); } }\n        @keyframes axGrowY { from { transform: scaleY(0); } to { transform: scaleY(1); } }\n        @keyframes axDraw { to { stroke-dashoffset: 0; } }\n        @keyframes axFade { from { opacity: 0; } to { opacity: 1; } }\n        @keyframes axSlideIn { from { transform: translateX(100%); } to { transform: translateX(0); } }\n        @keyframes axBounce { 0% { transform: translateY(0) scale(1); } 35% { transform: translateY(-4px) scale(1.14); } 70% { transform: translateY(0) scale(1); } 100% { transform: translateY(0) scale(1); } }\n        .ax-rail:hover .ax-railicon { animation: axBounce .5s ease; }\n        .ax-flat:hover { transform: none !important; }`}\n      \n      \n\n        \n        \n\n          {view === \"dashboard\" &amp;&amp; }\n          {view === \"assets\" &amp;&amp; }\n          \n\n            {view === \"dashboard\" &amp;&amp; }\n            {view === \"dashboard\" ? (\n              \n\n                {DASH_TILESETS[activeDash]\n                  ? \n                  : &lt;&gt;}\n              \n            ) : view === \"assets\" ? (\n              \n            ) : (\n              \n\n                {RAIL.find(r =&gt; r.key === view)?.label} view \u2014 coming soon\n              \n            )}\n          \n        \n      \n    \n  );\n}\n\ncreateRoot(document.getElementById(\"root\")).render();\n    \n  \n    fetch('/.inspector/overlay.js')\n      .then(r =&gt; r.text())\n      .then(code =&gt; { const s = document.createElement('script'); s.textContent = code; document.head.appendChild(s); })\n      .catch(() =&gt; {});\n  \n  \n\n", "creation_timestamp": "2026-06-23T09:25:09.000000Z"}