﻿// ════════════════════════════════════════════════════════════════════════════
// app-monitors-view.jsx — v1.4.457
// ────────────────────────────────────────────────────────────────────────────
// Extracted from app.jsx in Session 70 to reduce the main bundle.
// Contains MonitorsView — monitor list, group management, detail panel.
//
// Loading order: AFTER app.jsx and app-monitor-form.jsx.
// Babel standalone with data-presets="react" shares top-level lexical scope
// across <script type="text/babel"> tags — all top-level const/let from
// app.jsx (c, Input, Sel, Btn, Card, SmallToggle, etc.) and MonitorFormPage
// from app-monitor-form.jsx are already in scope here.
// No destructure or window.OP1 namespace needed.
// ════════════════════════════════════════════════════════════════════════════
// ═══ MONITORS ═══
function MonitorsView({ api, canManage, isAdmin, initialNav, onNavConsumed, navInterceptRef, onNavigate, machines, currentMachine, setHelpKey, monitoringControl, onNavigateSynthetic, subViewReset }) {
  const [monitors, setMonitors]               = useState([]);
  const [groups, setGroups]                   = useState([]);
  const [loading, setLoading]                 = useState(true);
  const [err, setErr]                         = useState("");
  const [subView, setSubView]                 = useState("list");
  useEffect(() => {
    if (!subViewReset) return;
    setSubView('list');
    setManageGroupTarget(null);
    setGroupThresholdTarget(null);
    setGroupThresholdRows([]);
  }, [subViewReset]);
  const [editMonitor, setEditMonitor]         = useState(null);
  const [groupThresholdTarget, setGroupThresholdTarget] = useState(null);
  const [groupThresholdRows, setGroupThresholdRows]     = useState([]);
  const [groupThresholdSaving, setGroupThresholdSaving] = useState(false);
  const [groupThresholdErr, setGroupThresholdErr]       = useState("");
  const [groupThresholdOk, setGroupThresholdOk]         = useState("");
  const [search, setSearch]                   = useState("");
  const [typeFilter, setTypeFilter]           = useState("all");
  const [statusFilter, setStatusFilter]       = useState(initialNav?.statusFilter || "all");
  const [groupFilter, setGroupFilter]         = useState(() => initialNav?.groupId === "ungrouped" ? "ungrouped" : initialNav?.groupId ? String(initialNav.groupId) : "all");
  const [hovStat, setHovStat]                 = useState(null);
  const [editMonitorOrigin, setEditMonitorOrigin] = useState(initialNav?.origin || null);
  const [deleteTarget, setDeleteTarget]       = useState(null);
  const [deleting, setDeleting]               = useState(false);
  const [expandedGroups, setExpandedGroups]   = useState(new Set());
  const [sortCol, setSortCol]                 = useState("health");
  const [sortDir, setSortDir]                 = useState("asc");
  const [deleteGroupTarget, setDeleteGroupTarget] = useState(null); // { groupId, groupName, count }
  const [deleteGroupStep, setDeleteGroupStep]     = useState(1);    // 1 = pick action, 2 = confirm all
  const [deletingGroup, setDeletingGroup]         = useState(false);
  const [groupSuccessMsg, setGroupSuccessMsg]     = useState("");
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [hoveredRow, setHoveredRow]         = useState(null);
  const [runningId, setRunningId]           = useState(null);
  const [runningGroupId, setRunningGroupId] = useState(null);
  const [analysisGroup, setAnalysisGroup]   = useState(null);
  const [analysisRange, setAnalysisRange]   = useState("24h");
  const [analysisData, setAnalysisData]     = useState([]);
  const [analysisSyntheticFlows, setAnalysisSyntheticFlows] = useState([]);
  const [analysisSyntheticAlerts, setAnalysisSyntheticAlerts] = useState([]);
  const [analysisLoading, setAnalysisLoading] = useState(false);
  const [analysisErr, setAnalysisErr]       = useState("");
  const [analysisSnapshotPin, setAnalysisSnapshotPin] = useState(null); // { monitorId, timestamp }
  const [analysisSnapshotData, setAnalysisSnapshotData] = useState({}); // { [monitorId]: { Latest, Selected } }
  const [analysisSnapshotLoading, setAnalysisSnapshotLoading] = useState(false);
  const [analysisSnapOpen, setAnalysisSnapOpen] = useState({}); // { [monitorId]: bool }
  const [pinnedTimestamp,  setPinnedTimestamp]  = useState(null); // ISO string | null — shared pin across all charts
  const [groupDiagOpen,    setGroupDiagOpen]    = useState(false);
  const [groupDiagLoading, setGroupDiagLoading] = useState(false);
  const [groupDiagResult,  setGroupDiagResult]  = useState(null);
  const [groupDiagError,   setGroupDiagError]   = useState(null);
  const [groupDiagTs,      setGroupDiagTs]      = useState(null);
  const [groupInvThread,        setGroupInvThread]        = useState([]);
  const [groupFollowUpText,     setGroupFollowUpText]     = useState('');
  const [groupFollowUpLoading,  setGroupFollowUpLoading]  = useState(false);
  const [groupCopiedScriptId,   setGroupCopiedScriptId]   = useState(null);
  const [groupLastDiagCtx,      setGroupLastDiagCtx]      = useState('');
  const [groupRunbookCtx,       setGroupRunbookCtx]       = useState('');
  const [selectedAlertIdx, setSelectedAlertIdx] = useState(null); // index into sortedAlerts for inspector panel
  const [syntheticFlows, setSyntheticFlows]     = useState([]);

  // ── Group notification modal state ──
  const [groupNotifModal, setGroupNotifModal]             = useState(null); // null | { groupId, groupName }
  const [groupAssignments, setGroupAssignments]           = useState([]);
  const [groupAssignmentsLoading, setGroupAssignmentsLoading] = useState(false);
  const [groupMonitorsForModal, setGroupMonitorsForModal] = useState([]);
  const [groupNotifEnabled, setGroupNotifEnabled]         = useState(true);
  const [groupNotifSaving, setGroupNotifSaving]           = useState(false);
  const [groupAssignmentCounts, setGroupAssignmentCounts] = useState({});
  const [monitorAssignmentCounts, setMonitorAssignmentCounts] = useState({});
  const [groupNotifRecipients, setGroupNotifRecipients]   = useState([]);
  const [groupAssignPicker, setGroupAssignPicker]         = useState(false);
  const [groupPickerRecipient, setGroupPickerRecipient]   = useState('');
  const [groupPickerSeverity, setGroupPickerSeverity]     = useState('warning');
  const [groupPickerCooldown, setGroupPickerCooldown]     = useState(30);

  // ── Bulk Add state ──
  const [showBulkAdd, setShowBulkAdd]         = useState(false);
  const [bulkRows, setBulkRows]               = useState([
    { name:'', ip:'', group:'Production', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
    { name:'', ip:'', group:'Production', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
    { name:'', ip:'', group:'Production', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
  ]);
  const [bulkDraftName, setBulkDraftName]     = useState('');
  const [bulkDraftSaved, setBulkDraftSaved]   = useState(null);
  const [bulkCatCollapsed, setBulkCatCollapsed] = useState({ i:false, a:false, d:false, w:false });
  const [bulkCtrlGroup, setBulkCtrlGroup]     = useState('Production');
  const [bulkCtrlProfile, setBulkCtrlProfile] = useState('');
  const [bulkCtrlChecks, setBulkCtrlChecks]   = useState(Array(BULK_TOTAL_COLS).fill(0));
  const [bulkAddStep, setBulkAddStep]               = useState(1);
  const [bulkThresholds, setBulkThresholds]         = useState({});
  const [bulkStep2Collapsed, setBulkStep2Collapsed] = useState({});
  const [bulkTestResults, setBulkTestResults]       = useState({});
  const [bulkTestRunning, setBulkTestRunning]       = useState(false);
  const [bulkEnabled, setBulkEnabled]               = useState({});
  const [bulkAlertsEnabled, setBulkAlertsEnabled]   = useState({});
  const [bulkStep3Collapsed, setBulkStep3Collapsed] = useState({});
  const [bulkCreateDone, setBulkCreateDone]         = useState(false);
  const [bulkSkipTest, setBulkSkipTest]             = useState(false);
  const [bulkSuccessMsg, setBulkSuccessMsg]         = useState('');
  const [bulkWizardMode, setBulkWizardMode]         = useState('create'); // 'create' | 'mass-edit'
  const [massEditSet, setMassEditSet]               = useState(null);
  const [massEditMonitors, setMassEditMonitors]     = useState([]);
  const [massEditThresholds, setMassEditThresholds] = useState({});
  const [massEditEnabled, setMassEditEnabled]       = useState({});
  const [showManageGroups, setShowManageGroups]         = useState(false);
  const [groupFormOpen, setGroupFormOpen]               = useState(false);
  const [groupFormMode, setGroupFormMode]               = useState("add");
  const [groupFormId, setGroupFormId]                   = useState(null);
  const [groupFormName, setGroupFormName]               = useState("");
  const [groupFormParent, setGroupFormParent]           = useState("");
  const [groupFormSaving, setGroupFormSaving]           = useState(false);
  const [groupFormErr, setGroupFormErr]                 = useState("");
  const [confirmModal, setConfirmModal]                 = useState(null);
  const showConfirm = (msg, onOk) => setConfirmModal({ msg, onOk });
  const [bulkAddGroupId, setBulkAddGroupId]             = useState(null);
  const [bulkAddGroupName, setBulkAddGroupName]         = useState('');
  const [manageGroupTarget, setManageGroupTarget]       = useState(null);

  const handleBulkPaste = useCallback((e, rowIdx, field) => {
    const text = e.clipboardData.getData('text');
    const lines = text.split('\n').map(l => l.trim()).filter(l => l.length > 0);
    if (lines.length <= 1) return;
    e.preventDefault();
    setBulkRows(prev => {
      const next = [...prev];
      lines.forEach((val, i) => {
        const ri = rowIdx + i;
        if (ri < next.length) {
          next[ri] = { ...next[ri], [field]: val };
        } else {
          next.push({ name:'', ip:'', group: bulkCtrlGroup, profile:'', checks: Array(BULK_TOTAL_COLS).fill(0), [field]: val });
        }
      });
      return next;
    });
  }, [bulkCtrlGroup]);

  const load = useCallback(() => {
    setLoading(true);
    api("GET","/monitors").then(r=>{setMonitors(Array.isArray(r)?r:[]); setErr("");}).catch(e=>setErr(e.message)).finally(()=>setLoading(false));
    api("GET","/synthetic/flows").then(r=>setSyntheticFlows(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const loadGroupAssignmentCounts = useCallback(async (groupList) => {
    if (!groupList || groupList.length === 0) return;
    try {
      const counts = {};
      await Promise.all(groupList.map(async (g) => {
        const a = await api("GET", `/notifications/groups/${g.GroupID}/assignments`);
        counts[g.GroupID] = Array.isArray(a) ? a.length : 0;
      }));
      setGroupAssignmentCounts(counts);
    } catch { /* silent */ }
  }, [api]);

  const loadGroupNotifModal = useCallback(async (groupId, groupName) => {
    setGroupAssignmentsLoading(true);
    try {
      const [assignments, recipients, allMonitors] = await Promise.all([
        api("GET", `/notifications/groups/${groupId}/assignments`),
        api("GET", "/notifications/recipients"),
        api("GET", "/monitors"),
      ]);
      const assignArr = Array.isArray(assignments) ? assignments : [];
      const recsArr   = Array.isArray(recipients)  ? recipients  : [];
      const monsArr   = Array.isArray(allMonitors)  ? allMonitors : [];
      const inGroup   = monsArr.filter(m => m.GroupID === groupId || m.GroupName === groupName);
      setGroupAssignments(assignArr);
      setGroupNotifRecipients(recsArr);
      setGroupMonitorsForModal(inGroup);
      // Load per-monitor assignment counts for coverage section
      const mCounts = {};
      await Promise.all(inGroup.map(async (m) => {
        try {
          const a = await api("GET", `/notifications/monitors/${m.MonitorID}/assignments`);
          mCounts[m.MonitorID] = Array.isArray(a) ? a.length : 0;
        } catch { mCounts[m.MonitorID] = 0; }
      }));
      setMonitorAssignmentCounts(mCounts);
    } catch {
      setGroupAssignments([]); setGroupMonitorsForModal([]);
    } finally { setGroupAssignmentsLoading(false); }
  }, [api]);

  useEffect(()=>{
    if (!analysisGroup) { setAnalysisData([]); setAnalysisSnapshotData({}); return; }
    setAnalysisLoading(true); setAnalysisErr("");
    api("GET",`/monitors/group/${encodeURIComponent(analysisGroup)}/analysis?range=${analysisRange}`)
      .then(r=>{
        const arr = Array.isArray(r)?r:(Array.isArray(r?.monitors)?r.monitors:[]);
        setAnalysisData(arr);
        setAnalysisSyntheticFlows(Array.isArray(r?.syntheticFlows)?r.syntheticFlows:[]);
        setAnalysisSyntheticAlerts(Array.isArray(r?.syntheticAlerts)?r.syntheticAlerts:[]);
        // Extract process snapshots from response (infra-mem monitors)
        const snapMap = {};
        arr.forEach(mon=>{
          if (mon.ProcessSnapshot) snapMap[mon.MonitorId] = mon.ProcessSnapshot;
        });
        setAnalysisSnapshotData(snapMap);
      })
      .catch(e=>setAnalysisErr(e?.message||"Failed to load analysis data"))
      .finally(()=>setAnalysisLoading(false));
  },[analysisGroup, analysisRange, api]);

  const fetchSnapshot = useCallback((monitorId, snapshotAt)=>{
    if (!analysisGroup) return;
    setAnalysisSnapshotLoading(true);
    const q = snapshotAt ? `&snapshotAt=${encodeURIComponent(snapshotAt)}` : "";
    api("GET",`/monitors/group/${encodeURIComponent(analysisGroup)}/analysis?range=${analysisRange}${q}`)
      .then(r=>{
        const arr = Array.isArray(r)?r:(Array.isArray(r?.monitors)?r.monitors:[]);
        const mon = arr.find(x=>x.MonitorId===monitorId);
        if (mon?.ProcessSnapshot) {
          setAnalysisSnapshotData(prev=>({...prev,[monitorId]:mon.ProcessSnapshot}));
          if (snapshotAt) setAnalysisSnapshotPin({monitorId, timestamp:mon.ProcessSnapshot.Selected?.Timestamp||snapshotAt});
        }
      })
      .catch(()=>{})
      .finally(()=>setAnalysisSnapshotLoading(false));
  },[analysisGroup, analysisRange, api]);

  const runGroupDiagnosis = async () => {
    setGroupDiagOpen(true); setGroupDiagLoading(true); setGroupDiagResult(null); setGroupDiagError(null); setGroupDiagTs(null);
    setGroupInvThread([]); setGroupFollowUpText(''); setGroupLastDiagCtx(''); setGroupRunbookCtx('');
    try {
      let context, userMessage, systemPrompt;
      const rangeLabel = analysisRange==="7d"?"7 days":"24 hours";
      if (pinnedTimestamp) {
        // ── Point-in-time mode ──
        const arr = await api("GET",`/monitors/group/${encodeURIComponent(analysisGroup)}/analysis?range=${analysisRange}&pinnedAt=${encodeURIComponent(pinnedTimestamp)}&snapshotAt=${encodeURIComponent(pinnedTimestamp)}`);
        const mons = Array.isArray(arr)?arr:(Array.isArray(arr?.monitors)?arr.monitors:[]);
        const fmtPin = new Date(pinnedTimestamp).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});
        const lines=[];
        lines.push(`POINT-IN-TIME GROUP DIAGNOSIS — ${analysisGroup}`);
        lines.push(`Pinned moment: ${fmtPin}`);
        lines.push(`Analysis window: ${rangeLabel}`);
        lines.push(`MONITOR VALUES AT ${fmtPin}:`);
        lines.push("");
        mons.forEach(mon=>{
          const unit=mon.Unit||"";
          const pinnedVal=mon.PinnedValue!=null?`${mon.PinnedValue}${unit}`:"N/A";
          const avg=mon.AvgValue!=null?`${mon.AvgValue}${unit}`:"N/A";
          const sev=mon.PinnedSeverity||mon.CurrentStatus||"unknown";
          lines.push(`${mon.MonitorName} — ${pinnedVal} — ${sev} (${rangeLabel} avg: ${avg})`);
          const snap=mon.ProcessSnapshot?.Selected||mon.ProcessSnapshot?.Latest;
          if (snap?.Processes?.length) {
            lines.push(`  MEMORY PROCESS SNAPSHOT AT ${fmtPin}:`);
            snap.Processes.forEach(p=>lines.push(`  ${p.Name} — ${p.MemoryPct}% (${p.MemoryMB} MB)`));
          }
          lines.push("");
        });
        const totalPinnedAlerts=mons.reduce((s,m)=>s+(m.PinnedAlertCount||0),0);
        lines.push(`ALERTS FIRING AT THIS MOMENT: ${totalPinnedAlerts}`);
        const pinMs=new Date(pinnedTimestamp).getTime();
        [
          ...(analysisData||[]).flatMap(m=>(m.Alerts||[]).map(a=>({...a,monitorName:m.MonitorName}))),
          ...(analysisSyntheticAlerts||[]).map(a=>({...a,monitorName:a.FlowName||'Synthetic'}))
        ]
          .filter(a=>Math.abs(new Date(a.Timestamp).getTime()-pinMs)<5*60*1000)
          .forEach(a=>lines.push(`  [${a.Severity}] ${a.monitorName}`));
        lines.push("");
        lines.push("CROSS-MONITOR CORRELATION:");
        const memMon=mons.find(m=>m.MonitorSubType==="infra-mem"||m.MonitorSubType==="infra-memory");
        const cpuMon=mons.find(m=>m.MonitorSubType==="infra-cpu");
        if (memMon?.PinnedValue!=null) lines.push(`Memory at ${memMon.PinnedValue}% at this moment.`);
        if (cpuMon?.PinnedValue!=null) lines.push(`CPU at ${cpuMon.PinnedValue}% at this moment.`);
        mons.filter(m=>m.MonitorType==="http"&&m.PinnedValue!=null).forEach(m=>lines.push(`${m.MonitorName} response: ${m.PinnedValue}ms at this moment.`));
        lines.push("CONTEXT: This is a specific moment in time, not an average.");
        lines.push(`The user selected this timestamp ${totalPinnedAlerts>0?"because it corresponds to alerts":"manually"}.`);
        context=lines.join("\n");
        const grpPinRunbookParts=mons.filter(mon=>mon.RunbookUrl||mon.RunbookNotes).flatMap(mon=>[mon.RunbookNotes?`[${mon.MonitorName}] Runbook notes: ${mon.RunbookNotes}`:"",mon.RunbookUrl?`[${mon.MonitorName}] Runbook URL: ${mon.RunbookUrl}`:""].filter(Boolean));
        const grpPinRunbookCtx=grpPinRunbookParts.join("\n");
        setGroupRunbookCtx(grpPinRunbookCtx);
        systemPrompt="You are an expert infrastructure monitoring assistant helping users analyze point-in-time monitoring data in OP1 Operations Portal. Be concise, practical, and specific. Format your response with these sections: ## What Was Tested, ## What Went Wrong, ## Likely Causes (bullet list, ranked by probability), ## Suggested Fixes (numbered, actionable). Always end your response with a ## Next Steps section containing 2-4 specific investigation actions tailored to this exact incident. For any step involving data collection, provide a ready-to-run PowerShell script pre-populated with the specific monitor name, machine name, and time window from the context. Format each script as a fenced powershell code block with a comment on line 1 identifying the monitor and time window. Keep total response under 500 words."+(grpPinRunbookCtx?"\n\n## Operator runbook notes\n"+grpPinRunbookCtx+"\n\nThese notes are written by the operators who manage these monitors. Treat them as authoritative — factor them directly into your diagnosis and reference them where relevant.":"");
        userMessage=`${context}\n\nAnalyse what was happening across this infrastructure group at this specific moment. Focus on cross-monitor correlations — did multiple metrics spike simultaneously? What likely triggered the alerts at this moment? What was the causal chain?`;
      } else {
        // ── Window average mode ──
        const lines=[];
        lines.push(`GROUP CORRELATION ANALYSIS — ${analysisGroup}`);
        lines.push(`Time range: ${rangeLabel}`);
        lines.push(`Generated: ${new Date().toLocaleString()}`);
        lines.push(`MONITORS (${analysisData.length} total):`);
        lines.push("");
        const warnList=[], critList=[], healthyList=[];
        (analysisData||[]).forEach(mon=>{
          const unit=mon.Unit||"";
          const avg=mon.AvgValue!=null?`${mon.AvgValue}${unit}`:"N/A";
          const peak=mon.PeakValue!=null?`${mon.PeakValue}${unit}`:"N/A";
          lines.push(`${mon.MonitorName} (${mon.MonitorSubType||mon.MonitorType}) — Status: ${mon.CurrentStatus||"unknown"} — Avg: ${avg} — Peak: ${peak} — Alerts: ${mon.AlertCount||0}`);
          if (mon.WarnThreshold!=null||mon.CritThreshold!=null) lines.push(`  Threshold: warn=${mon.WarnThreshold??'N/A'} crit=${mon.CritThreshold??'N/A'}`);
          const snap=analysisSnapshotData[mon.MonitorId];
          const snapEntry=snap?.Selected||snap?.Latest;
          if (snapEntry?.Processes?.length) {
            lines.push(`  MEMORY PROCESS SNAPSHOT:`);
            lines.push(`  Snapshot at: ${snapEntry.Timestamp||"N/A"}`);
            snapEntry.Processes.forEach(p=>lines.push(`  ${p.Name} — ${p.MemoryPct}% (${p.MemoryMB} MB)`));
          }
          lines.push("");
          const st=(mon.CurrentStatus||"").toLowerCase();
          if (st==="warning") warnList.push(mon.MonitorName);
          else if (st==="critical") critList.push(mon.MonitorName);
          else if (st==="healthy") healthyList.push(mon.MonitorName);
        });
        const allAlerts=[
          ...(analysisData||[]).flatMap(m=>(m.Alerts||[]).map(a=>({...a,monitorName:m.MonitorName}))),
          ...(analysisSyntheticAlerts||[]).map(a=>({...a,monitorName:a.FlowName||'Synthetic'}))
        ];
        const monWithAlerts=(analysisData||[]).filter(m=>(m.AlertCount||0)>0).map(m=>`${m.MonitorName} (${m.AlertCount})`);
        lines.push("ALERT SUMMARY:");
        lines.push(`Total alerts in period: ${allAlerts.length}`);
        if (monWithAlerts.length) lines.push(`Monitors with active alerts: ${monWithAlerts.join(", ")}`);
        lines.push("");
        lines.push("INFRASTRUCTURE CONTEXT:");
        lines.push(`Group: ${analysisGroup}`);
        if (warnList.length)    lines.push(`Monitors in warning: ${warnList.join(", ")}`);
        if (critList.length)    lines.push(`Monitors in critical: ${critList.join(", ")}`);
        if (healthyList.length) lines.push(`Monitors healthy: ${healthyList.join(", ")}`);
        context=lines.join("\n");
        const grpWinRunbookParts=(analysisData||[]).filter(mon=>mon.RunbookUrl||mon.RunbookNotes).flatMap(mon=>[mon.RunbookNotes?`[${mon.MonitorName}] Runbook notes: ${mon.RunbookNotes}`:"",mon.RunbookUrl?`[${mon.MonitorName}] Runbook URL: ${mon.RunbookUrl}`:""].filter(Boolean));
        const grpWinRunbookCtx=grpWinRunbookParts.join("\n");
        setGroupRunbookCtx(grpWinRunbookCtx);
        systemPrompt="You are an expert infrastructure monitoring assistant helping users analyze group-level monitoring data in OP1 Operations Portal. Be concise, practical, and specific. Format your response with these sections: ## What Was Tested, ## What Went Wrong, ## Likely Causes (bullet list, ranked by probability), ## Suggested Fixes (numbered, actionable). Always end your response with a ## Next Steps section containing 2-4 specific investigation actions tailored to this exact incident. For any step involving data collection, provide a ready-to-run PowerShell script pre-populated with the specific monitor name, machine name, and time window from the context. Format each script as a fenced powershell code block with a comment on line 1 identifying the monitor and time window. Keep total response under 500 words."+(grpWinRunbookCtx?"\n\n## Operator runbook notes\n"+grpWinRunbookCtx+"\n\nThese notes are written by the operators who manage these monitors. Treat them as authoritative — factor them directly into your diagnosis and reference them where relevant.":"");
        userMessage=`${context}\n\nAnalyze this infrastructure group. Identify the root cause of any issues, explain correlations between monitors, and provide specific recommended actions. If memory process data is available, explain what is driving memory consumption and whether it poses a risk.`;
      }
      setGroupLastDiagCtx(userMessage);
      const d=await api("POST","/diagnose",{systemPrompt,userMessage});
      const txt=d?.text||"No response received.";
      setGroupDiagResult(txt);
      setGroupDiagTs(new Date().toLocaleTimeString());
      setGroupInvThread([{ role:'assistant', content:txt, timestamp:new Date().toISOString(), id:Date.now() }]);
    } catch(e) {
      setGroupDiagError((e?.message&&!e.message.startsWith("HTTP "))?e.message:"Diagnosis unavailable — please try again.");
    } finally {
      setGroupDiagLoading(false);
    }
  };

  const renderGroupDiagMd = (text) => {
    if (!text) return null;
    const parseBold = (s) => s.split(/(\*\*[^*]+\*\*)/g).map((p,j) =>
      p.startsWith("**")&&p.endsWith("**") ? <strong key={j}>{p.slice(2,-2)}</strong> : p
    );
    return text.split("\n").map((line,i) => {
      const t=line.trim();
      if (t.startsWith("## "))
        return <div key={i} style={{ fontSize:11, fontWeight:700, textTransform:"uppercase", color:"#006D8C", letterSpacing:"0.5px", borderBottom:"1px solid #C0D8E8", paddingBottom:3, marginBottom:6, marginTop:i>0?12:0 }}>{t.slice(3)}</div>;
      if (t==="---") return <hr key={i} style={{ border:"none", borderTop:"1px solid #C0D8E8", margin:"10px 0" }} />;
      if (t.startsWith("- ")||t.startsWith("• "))
        return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", marginTop:2 }}>•</span><span>{parseBold(t.slice(2))}</span></div>;
      if (/^\d+\./.test(t)) {
        const num=t.match(/^\d+/)[0];
        return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", minWidth:14 }}>{num}.</span><span>{parseBold(t.slice(num.length+1).trim())}</span></div>;
      }
      if (t==="") return <div key={i} style={{ height:6 }} />;
      return <div key={i} style={{ marginBottom:3, lineHeight:1.6 }}>{parseBold(line)}</div>;
    });
  };
  const handleGroupFollowUp = () => {
    const q = groupFollowUpText.trim();
    if (!q || groupFollowUpLoading) return;
    const newUserMsg = { role: 'user', content: q, timestamp: new Date().toISOString(), id: Date.now() };
    const updatedThread = [...groupInvThread, newUserMsg];
    setGroupInvThread(updatedThread);
    setGroupFollowUpText('');
    setGroupFollowUpLoading(true);
    const systemPrompt = "You are an expert infrastructure monitoring assistant helping users investigate group-level monitoring incidents in OP1 Operations Portal. You are in an ongoing investigation conversation. Be concise and specific. When providing scripts, format them as fenced powershell code blocks. If suggesting next steps, keep them actionable and targeted."+(groupRunbookCtx?"\n\n## Operator runbook notes\n"+groupRunbookCtx+"\n\nThese operator notes are authoritative context for these monitors. Reference them when relevant to the user's question.":"");
    const threadHistory = updatedThread.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n\n');
    const userMessage = `Initial incident context:\n${groupLastDiagCtx}\n\n---\nConversation so far:\n${threadHistory}\n\n---\nPlease respond to the latest user message.`;
    api("POST", "/diagnose", { systemPrompt, userMessage })
      .then(d => {
        const txt = d?.text || "No response received.";
        setGroupInvThread(prev => [...prev, { role: 'assistant', content: txt, timestamp: new Date().toISOString(), id: Date.now() + 1 }]);
      })
      .catch(() => {
        setGroupInvThread(prev => [...prev, { role: 'assistant', content: 'Follow-up unavailable — please try again.', timestamp: new Date().toISOString(), id: Date.now() + 1 }]);
      })
      .finally(() => setGroupFollowUpLoading(false));
  };

  useEffect(()=>{
    load();
    api("GET","/monitors/groups").then(r=>{ const g=Array.isArray(r)?r:[]; setGroups(g); loadGroupAssignmentCounts(g); }).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[load]);

  useEffect(()=>{
    if (initialNav?.groupId != null && initialNav.groupId !== "") {
      setGroupFilter(initialNav.groupId === "ungrouped" ? "ungrouped" : String(initialNav.groupId));
    } else if (initialNav && Object.keys(initialNav).length > 0 && !initialNav.groupId) {
      setGroupFilter("all");
    }
    if (initialNav?.openAdd) {
      setEditMonitor(null); setEditMonitorOrigin(null); setSubView("form"); if (onNavConsumed) onNavConsumed();
    } else if (initialNav?.editMonitor) {
      setEditMonitor(initialNav.editMonitor); setEditMonitorOrigin(initialNav.origin || null); setSubView("form"); if (onNavConsumed) onNavConsumed();
    } else if (initialNav?.editId && monitors.length > 0) {
      const m = monitors.find(x => x.MonitorID === initialNav.editId);
      if (m) { setEditMonitor(m); setEditMonitorOrigin(initialNav.origin || null); setSubView("form"); if (onNavConsumed) onNavConsumed(); }
    }
    if (initialNav?.expandAll && monitors.length > 0) {
      const groupIds = [...new Set(monitors.filter(m => m.GroupID).map(m => m.GroupID))];
      setExpandedGroups(new Set(groupIds));
    }
    if (initialNav?.groupId && initialNav.groupId !== 'ungrouped' && monitors.length > 0) {
      const parentId = Number(initialNav.groupId);
      const subIds = groups.filter(g => g.ParentGroupID === parentId).map(g => g.GroupID);
      setExpandedGroups(s => { const n = new Set(s); n.add(parentId); subIds.forEach(id => n.add(id)); return n; });
    }
    if (initialNav?.groupId === 'ungrouped') {
      setExpandedGroups(s => { const n = new Set(s); n.add('ungrouped'); return n; });
    }
  },[monitors, initialNav]);

  const openAdd  = () => { setEditMonitor(null); setEditMonitorOrigin(null); setSubView("form"); };
  const openEdit = m  => { setEditMonitor(m);    setEditMonitorOrigin(null); setSubView("form"); };
  const openAddForGroup = (groupId, groupName) => { setEditMonitor({ _addWithGroup:true, GroupID:groupId, GroupName:groupName }); setEditMonitorOrigin(null); setSubView("form"); };
  const onSaved  = () => { setSubView("list"); setEditMonitor(null); load(); };

  const openAddGroupForm = () => {
    setGroupFormMode("add"); setGroupFormId(null);
    setGroupFormName(""); setGroupFormParent(""); setGroupFormErr(""); setGroupFormOpen(true);
  };
  const openEditGroupForm = (g) => {
    setGroupFormMode("edit"); setGroupFormId(g.GroupID);
    setGroupFormName(g.GroupName || ""); setGroupFormParent(g.ParentGroupID ? String(g.ParentGroupID) : "");
    setGroupFormErr(""); setGroupFormOpen(true);
  };
  const saveGroupForm = async () => {
    if (!groupFormName.trim()) { setGroupFormErr("Group name is required."); return; }
    setGroupFormSaving(true); setGroupFormErr("");
    try {
      const body = { GroupName: groupFormName.trim(), ParentGroupID: groupFormParent ? parseInt(groupFormParent) : null };
      if (groupFormMode === "add") {
        await api("POST", "/monitors/groups", body);
      } else {
        await api("PUT", `/monitors/groups/${groupFormId}`, body);
      }
      setGroupFormOpen(false);
      const updated = await api("GET", "/monitors/groups");
      if (Array.isArray(updated)) setGroups(updated);
    } catch(e) { setGroupFormErr(e.message || "Save failed."); }
    finally { setGroupFormSaving(false); }
  };

  const saveGroupThresholds = async () => {
    setGroupThresholdSaving(true);
    setGroupThresholdErr('');
    setGroupThresholdOk('');
    let updated = 0;
    const failures = [];
    for (const row of groupThresholdRows) {
      try {
        await api('PATCH', '/monitors/' + row.monitorId + '/thresholds', {
          ResponseTimeThresholdMs: row.warn !== '' ? parseInt(row.warn) || null : null,
          CriticalThresholdMs:     row.crit !== '' ? parseInt(row.crit) || null : null,
          CheckFrequencySeconds:   row.freq !== '' && parseInt(row.freq) >= 10 ? parseInt(row.freq) : null,
        });
        await api('PATCH', '/monitors/' + row.monitorId + '/enabled', { enabled: row.enabled });
        updated++;
      } catch(err) {
        failures.push({ name: row.monitorName || ('monitor ' + row.monitorId), error: err?.message || String(err) });
      }
    }
    setGroupThresholdSaving(false);
    if (failures.length > 0) {
      const summary = updated + ' updated, ' + failures.length + ' failed: ' +
        failures.slice(0, 5).map(f => f.name).join(', ') +
        (failures.length > 5 ? ' and ' + (failures.length - 5) + ' more' : '');
      setGroupThresholdErr(summary);
    }
    setGroupThresholdOk('\u2713 ' + updated + ' monitor' + (updated === 1 ? '' : 's') + ' updated in ' + (groupThresholdTarget?.groupName || 'group'));
    load();
    setTimeout(() => {
      setSubView('list');
      setGroupThresholdTarget(null);
      setGroupThresholdRows([]);
      setManageGroupTarget(null);
      setGroupThresholdOk('');
    }, 1200);
  };

  const confirmDelete = async () => {
    if (!deleteTarget) return;
    setDeleting(true);
    try {
      await api("DELETE",`/monitors/${deleteTarget.MonitorID}`);
      setDeleteTarget(null);
      load();
    } catch(e) { setErr(e.message); }
    finally { setDeleting(false); }
  };

  const toggleEnabled = async (m, e) => {
    e.stopPropagation();
    const newVal = m.IsEnabled === false ? true : false;
    setMonitors(prev => prev.map(x => x.MonitorID === m.MonitorID ? {...x, IsEnabled: newVal} : x));
    try { await api("PATCH", `/monitors/${m.MonitorID}/enabled`, { enabled: newVal }); }
    catch(ex) { setMonitors(prev => prev.map(x => x.MonitorID === m.MonitorID ? {...x, IsEnabled: !newVal} : x)); setErr(ex.message); }
  };

  const toggleAlertsEnabled = async (m, e) => {
    e.stopPropagation();
    const newVal = m.AlertsEnabled === false ? true : false;
    setMonitors(prev => prev.map(x => x.MonitorID === m.MonitorID ? {...x, AlertsEnabled: newVal} : x));
    try { await api("PATCH", `/monitors/${m.MonitorID}/alerts-enabled`, { enabled: newVal }); }
    catch(ex) { setMonitors(prev => prev.map(x => x.MonitorID === m.MonitorID ? {...x, AlertsEnabled: !newVal} : x)); setErr(ex.message); }
  };

  const toggleGroupEnabled = async (groupId, allCurrentlyDisabled, e) => {
    e.stopPropagation();
    const newVal = allCurrentlyDisabled;  // if all disabled → enable; otherwise → disable
    setMonitors(prev => prev.map(x => x.GroupID === groupId ? {...x, IsEnabled: newVal} : x));
    try { await api("PATCH", `/monitors/groups/${groupId}/enabled`, { enabled: newVal }); }
    catch(ex) { setMonitors(prev => prev.map(x => x.GroupID === groupId ? {...x, IsEnabled: !newVal} : x)); setErr(ex.message); }
  };

  const handleDeleteGroupClick = (e, groupId, groupName, count) => {
    e.stopPropagation();
    setDeleteGroupTarget({ groupId, groupName, count });
    setDeleteGroupStep(1);
  };

  const doDeleteGroup = async (withMonitors) => {
    if (!deleteGroupTarget) return;
    setDeletingGroup(true);
    let succeeded = false;
    let successMsg = "";
    try {
      await api("DELETE", `/monitors/groups/${deleteGroupTarget.groupId}?deleteMonitors=${withMonitors}`);
      succeeded = true;
      successMsg = withMonitors
        ? `Group and ${deleteGroupTarget.count} monitor${deleteGroupTarget.count !== 1 ? "s" : ""} deleted`
        : "Group deleted — monitors kept as unassigned";
    } catch(ex) { setErr(ex.message); }
    finally {
      setDeletingGroup(false);
      setDeleteGroupTarget(null);
      setDeleteGroupStep(1);
      if (succeeded) {
        setGroupSuccessMsg(successMsg);
        await load();
        setTimeout(() => setGroupSuccessMsg(""), 4000);
      }
    }
  };

  const cycleSort = (col) => {
    if (sortCol !== col) { setSortCol(col); setSortDir("asc"); }
    else setSortDir(d => d === "asc" ? "desc" : "asc");
  };

  const filtered = monitors.filter(m => {
    if (typeFilter !== "all" && m.MonitorType !== typeFilter) return false;
    if (statusFilter !== "all" && (statusFilter === "disabled" ? m.IsEnabled !== false : (m.IsEnabled === false || (m.EffectiveStatus||m.CurrentStatus) !== statusFilter))) return false;
    if (groupFilter === "ungrouped" && (m.GroupID && m.GroupID !== 0)) return false;
    if (groupFilter !== "all" && groupFilter !== "ungrouped") {
      const groupFilterId = Number(groupFilter);
      const subGroupIds = groups.filter(g => g.ParentGroupID === groupFilterId).map(g => g.GroupID);
      const allowedIds = new Set([groupFilterId, ...subGroupIds]);
      if (!allowedIds.has(m.GroupID)) return false;
    }
    if (search && !m.MonitorName?.toLowerCase().includes(search.toLowerCase()) && !m.IPAddressOrUrl?.includes(search)) return false;
    return true;
  });

  // Build grouped structure
  const groupMap = {};
  const ungrouped = [];
  for (const m of filtered) {
    if (m.GroupID) {
      if (!groupMap[m.GroupID]) groupMap[m.GroupID] = { id: m.GroupID, name: m.GroupName || "Group", monitors: [] };
      groupMap[m.GroupID].monitors.push(m);
    } else {
      ungrouped.push(m);
    }
  }
  // Seed top-level groups that have no monitors; sub-groups with no monitors are rendered via subGroupsByParent only
  for (const g of groups) {
    if (!groupMap[g.GroupID] && !g.ParentGroupID) {
      groupMap[g.GroupID] = { id: g.GroupID, name: g.GroupName || "Group", monitors: [] };
    }
  }
  // Separate top-level groups from sub-groups using ParentGroupID from groups state
  const groupParentMap = {};
  for (const g of groups) { groupParentMap[g.GroupID] = g.ParentGroupID || null; }
  const namedGroups = Object.values(groupMap)
    .filter(g => !groupParentMap[String(g.id)] && !groupParentMap[Number(g.id)])
    .sort((a,b) => a.name.localeCompare(b.name));
  const subGroupsByParent = {};
  for (const g of Object.values(groupMap)) {
    const parentId = groupParentMap[String(g.id)] ?? groupParentMap[Number(g.id)];
    if (parentId) {
      if (!subGroupsByParent[parentId]) subGroupsByParent[parentId] = [];
      subGroupsByParent[parentId].push(g);
    }
  }
  // Sort sub-groups by name within each parent
  for (const key of Object.keys(subGroupsByParent)) {
    subGroupsByParent[key].sort((a,b) => a.name.localeCompare(b.name));
  }

  const HEALTH_RANK = { critical:0, warning:1, unknown:2, healthy:3 };
  const sortMonitors = (arr) => {
    const dir = sortDir === "asc" ? 1 : -1;
    return [...arr].sort((a, b) => {
      if (sortCol === "health") {
        const ra = HEALTH_RANK[a.EffectiveStatus||a.CurrentStatus||"unknown"] ?? 2;
        const rb = HEALTH_RANK[b.EffectiveStatus||b.CurrentStatus||"unknown"] ?? 2;
        return dir * (ra - rb);
      }
      if (sortCol === "type")     return dir * (a.MonitorType||"").localeCompare(b.MonitorType||"");
      if (sortCol === "name")     return dir * (a.MonitorName||"").localeCompare(b.MonitorName||"");
      if (sortCol === "source")   return dir * (a.SourceMachine||"").localeCompare(b.SourceMachine||"");
      if (sortCol === "target")   return dir * (a.IPAddressOrUrl||"").localeCompare(b.IPAddressOrUrl||"");
      if (sortCol === "freq")     return dir * ((a.CheckFrequencySeconds||0) - (b.CheckFrequencySeconds||0));
      if (sortCol === "response") return dir * ((a.LastResponseTimeMs||0) - (b.LastResponseTimeMs||0));
      if (sortCol === "uptime")   return dir * ((a.Uptime30Day||0) - (b.Uptime30Day||0));
      return (a.MonitorType||"").localeCompare(b.MonitorType||"") || (a.MonitorName||"").localeCompare(b.MonitorName||"");
    });
  };

  const buildRows = (arr) => {
    const parents = arr.filter(m => !m.ParentMonitorID);
    const children = arr.filter(m => !!m.ParentMonitorID);
    const byParent = {};
    for (const ch of children) {
      if (!byParent[ch.ParentMonitorID]) byParent[ch.ParentMonitorID] = [];
      byParent[ch.ParentMonitorID].push(ch);
    }
    const rows = [];
    const parentIds = new Set(parents.map(p => p.MonitorID));
    for (const p of sortMonitors(parents)) {
      rows.push({ m: p, isChild: false });
      for (const ch of sortMonitors(byParent[p.MonitorID] || [])) rows.push({ m: ch, isChild: true });
    }
    // Children whose parent wasn't in the filtered set
    for (const ch of children.filter(c => !parentIds.has(c.ParentMonitorID))) rows.push({ m: ch, isChild: true });
    return rows;
  };

  const groupWorst = (ms) => {
    const enabled = ms.filter(m => m.IsEnabled !== false);
    if (enabled.length === 0) return "unknown";
    if (enabled.some(m => (m.EffectiveStatus||m.CurrentStatus) === "critical")) return "critical";
    if (enabled.some(m => (m.EffectiveStatus||m.CurrentStatus) === "warning"))  return "warning";
    if (enabled.every(m => (m.EffectiveStatus||m.CurrentStatus) === "healthy"))  return "healthy";
    return "unknown";
  };

  const COLS = 13;
  const MONITOR_COL_GRID = "70px 58px 1fr 130px 160px 50px 80px 80px 40px 52px 50px 62px 40px";

  const StatusCircle = ({ m }) => {
    const isDisabled = m.IsEnabled === false;
    const isSuppressed = m.IsSuppressed && !isDisabled;
    const status = m.EffectiveStatus || m.CurrentStatus || "unknown";
    const col = isDisabled ? "#C0D8E8" : (STATUS_COLOR[status] || "#9CA3AF");
    return (
      <span style={{ display:"inline-block", width:10, height:10, borderRadius:"50%", flexShrink:0,
        background: isSuppressed ? "transparent" : col,
        border: isSuppressed ? `1.5px dashed ${STATUS_COLOR[status]||"#9CA3AF"}` : "none" }} />
    );
  };

  const SmallToggle = ({ checked, onClick }) => (
    <div onClick={onClick}
      style={{ width:28, height:15, borderRadius:8, background:checked?c.blue:c.border, position:"relative", transition:"background 0.2s", cursor:"pointer", flexShrink:0 }}>
      <div style={{ position:"absolute", top:2, left:checked?13:2, width:11, height:11, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
    </div>
  );

  const SortTh = ({ col, label }) => {
    const active = sortCol === col;
    const arrow = active ? (sortDir === "asc" ? " \u2191" : " \u2193") : " \u2195";
    return (
      <span onClick={() => cycleSort(col)}
        style={{ cursor:"pointer", userSelect:"none", whiteSpace:"nowrap",
          color: active ? "#006D8C" : "inherit" }}>
        {label}<span style={{ fontSize:8, opacity: active ? 1 : 0.35, marginLeft:1 }}>{arrow}</span>
      </span>
    );
  };

  const renderGroupHeader = (groupId, groupName, groupMonitors, color, isUngrouped, depth = 0) => {
    const isExpanded = expandedGroups.has(groupId);
    const monWorst = groupWorst(groupMonitors);
    const sfWorst = (() => {
      const gSynths = syntheticFlows.filter(f => f.GroupName === groupName && f.IsEnabled !== false && f.LastRunPassed != null);
      if (gSynths.length === 0) return "unknown";
      if (gSynths.some(f => !f.LastRunPassed || (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs))) return "critical";
      if (gSynths.some(f => f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs)) return "warning";
      if (gSynths.every(f => f.LastRunPassed)) return "healthy";
      return "unknown";
    })();
    const RANK = { critical:0, warning:1, healthy:2, unknown:3 };
    const worst = (RANK[monWorst] ?? 3) <= (RANK[sfWorst] ?? 3) ? monWorst : sfWorst;
    const dotColor = worst === "healthy" ? "#22C55E" : worst === "warning" ? "#F59E0B" : worst === "critical" ? "#EF4444" : "#D1D5DB";
    const healthBorderColor = worst === "healthy" ? "#008C6F" : worst === "warning" ? "#E89A2E" : worst === "critical" ? "#D95C5C" : "#4A4A4A";
    const allDisabled = groupMonitors.length > 0 && groupMonitors.every(m => m.IsEnabled === false);
    const isRunningThis = runningGroupId === groupId;
    const groupRowTint = (status) => {
      if (status === 'critical') return '#FBECEC';
      if (status === 'warning')  return '#FDF6E8';
      if (status === 'healthy')  return '#EAF5F0';
      return '#F3F4F6';
    };
    return (
      <div key={`gh_${groupId}`}
        onClick={() => setExpandedGroups(s => { const n=new Set(s); if(n.has(groupId)) n.delete(groupId); else n.add(groupId); return n; })}
        style={{
          display:"flex", alignItems:"center", gap:10,
          background: depth === 1 ? "#F5F1EA" : groupRowTint(worst),
          borderTop:"1px solid #D8CCBA", borderBottom: depth === 1 ? "0.5px solid #E5DDD0" : "1px solid #D8CCBA",
          borderLeft:`3px solid ${depth === 1 ? healthBorderColor + "99" : healthBorderColor}`,
          paddingLeft: depth === 1 ? 28 : 12,
          padding:"5px 12px", cursor:"pointer", userSelect:"none"
        }}>
        <span style={{ fontSize:10, color:"#4A4A4A", display:"inline-block", transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)", transition:"transform 150ms ease", flexShrink:0 }}>▶</span>
        <div style={{ width:8, height:8, borderRadius:"50%", background:dotColor, flexShrink:0 }} />
        <div style={{ display:"flex", alignItems:"center", gap:8, minWidth:600, flexShrink:0 }}>
          <span style={{ fontWeight:600, fontSize:13, color: isUngrouped ? "#9CA3AF" : "#1A1A1A" }}>{groupName}</span>
          {(isUngrouped || depth === 1) && <span style={{ fontSize:10, background:"#E0F2F7", color:"#006D8C", borderRadius:10, padding:"1px 8px", fontWeight:500 }}>{groupMonitors.length + syntheticFlows.filter(f => f.GroupName === groupName).length}</span>}
        </div>
        {!isUngrouped && canManage && (
          <div style={{ display:"flex", alignItems:"center", gap:6 }} onClick={e=>e.stopPropagation()}>
            <button
              onClick={(e) => { e.stopPropagation(); openAddForGroup(groupId, groupName); }}
              style={{ fontSize:11, padding:"4px 12px", lineHeight:1, border:"1px solid #006D8C", borderRadius:6, background:"#ffffff", color:"#006D8C", cursor:"pointer", fontWeight:500, whiteSpace:"nowrap" }}
              title={`Add monitor to ${groupName}`}
            >+ Add Monitor</button>
            {depth === 0 && (
              <button
                onClick={(e) => { e.stopPropagation(); handleManageGroup(groupId, groupName, 'thresholds'); }}
                style={{ display:"inline-flex", alignItems:"center", fontSize:11, padding:"4px 12px", lineHeight:1, border:"1px solid #006D8C", borderRadius:6, background:"#ffffff", color:"#006D8C", cursor:"pointer", fontWeight:500, whiteSpace:"nowrap" }}
                title={`Manage ${groupName}`}
              >⚙ Manage<span style={{ fontSize:10, background:'#EEEAE0', color:'#4A4A4A', padding:'0 6px', borderRadius:8, lineHeight:'14px', marginLeft:6 }}>{groupMonitors.length + syntheticFlows.filter(f => f.GroupName === groupName).length}</span></button>
            )}
            {depth === 0 && (() => {
              try {
                const d = localStorage.getItem('op1_bulk_draft_' + groupId);
                if (!d) return null;
                const saved = JSON.parse(d);
                if (!saved || !saved.rows || saved.rows.length === 0) return null;
                const rowCount = saved.rows.filter(r => r.ip && r.ip.trim()).length;
                if (rowCount === 0) return null;
                return (
                  <span
                    onClick={(e) => { e.stopPropagation(); handleManageGroup(groupId, groupName, 'bulk-add'); }}
                    title={`Resume draft: ${saved.name || 'Untitled'} — ${rowCount} server${rowCount!==1?'s':''}`}
                    style={{ fontSize:10, padding:'2px 8px', borderRadius:10, background:'#FEF3C7', color:'#92400E', border:'1px solid #FCD34D', cursor:'pointer', fontWeight:500, whiteSpace:'nowrap' }}
                  >📋 Draft ({rowCount})</span>
                );
              } catch(_) { return null; }
            })()}
          </div>
        )}
        <div style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:0 }} onClick={e=>e.stopPropagation()}>
          {/* Zone: Operations */}
          <div style={{ display:"flex", alignItems:"center", gap:8 }}>
            {!isUngrouped && canManage && (
              <button
                disabled={runningGroupId !== null}
                onClick={async (e) => {
                  e.stopPropagation();
                  setRunningGroupId(groupId);
                  try {
                    await api("POST", `/monitors/groups/${groupId}/run`);
                    load();
                  } catch(_) {}
                  finally { setRunningGroupId(null); }
                }}
                style={{
                  fontSize:11, padding:"4px 12px", lineHeight:1, border:"1px solid #006D8C", borderRadius:6,
                  background:"#ffffff", color:"#006D8C", cursor: runningGroupId !== null ? "not-allowed" : "pointer",
                  opacity: runningGroupId !== null ? 0.5 : 1, fontWeight:500, whiteSpace:"nowrap",
                }}
                title="Run all checks in this group now"
              >{isRunningThis ? "Running…" : "▶ Run All"}</button>
            )}
            {!isUngrouped && (
              <button
                onClick={(e)=>{ e.stopPropagation(); setAnalysisGroup(groupName); setAnalysisRange("24h"); setSubView("analysis"); }}
                style={{ fontSize:11, padding:"4px 12px", lineHeight:1, border:"1px solid #006D8C", background:"#E0F2F7", color:"#006D8C", borderRadius:6, fontWeight:500, cursor:"pointer", whiteSpace:"nowrap" }}
                title={`Correlation analysis for ${groupName}`}
              >⬡ Analyze</button>
            )}
            {!isUngrouped && (()=>{
              const cnt = groupAssignmentCounts[groupId] || 0;
              const active = cnt > 0;
              return (
                <button onClick={(e)=>{ e.stopPropagation(); setGroupNotifModal({groupId,groupName}); setGroupNotifEnabled(true); loadGroupNotifModal(groupId, groupName); }}
                  title={active ? `${cnt} group recipient${cnt!==1?'s':''} — click to manage` : 'No group notification recipients — click to add'}
                  style={{ display:"flex", alignItems:"center", gap:5, fontSize:11, padding:"4px 12px", lineHeight:1, border:`1px solid ${active?c.blue:c.border}`, borderRadius:6, background:active?c.blueLight:"#fff", color:active?c.blue:c.textMuted, cursor:"pointer", fontWeight:500, whiteSpace:"nowrap" }}>
                  <svg width="12" height="12" viewBox="0 0 16 16" fill="none"><path d="M8 1a5 5 0 0 0-5 5v3l-1.5 2h13L13 9V6a5 5 0 0 0-5-5zm0 14a2 2 0 0 0 2-2H6a2 2 0 0 0 2 2z" fill="currentColor"/></svg>
                  {active ? `${cnt} recipient${cnt!==1?'s':''}` : 'Notify'}
                  <span style={{ width:6, height:6, borderRadius:"50%", background:active?c.green:c.border, flexShrink:0 }} />
                </button>
              );
            })()}
            {!isUngrouped && canManage && (
              <SmallToggle checked={!allDisabled} onClick={(e)=>toggleGroupEnabled(groupId, allDisabled, e)} />
            )}
            {!isUngrouped && isAdmin && (
              <GroupDeleteBtn onClick={(e) => handleDeleteGroupClick(e, groupId, groupName, groupMonitors.length)} />
            )}
          </div>
        </div>
      </div>
    );
  };

  const renderMonitorRow = ({ m, isChild }, i) => {
    const isEnabled = m.IsEnabled !== false;
    const alertsEnabled = m.AlertsEnabled !== false;
    const status = m.EffectiveStatus || m.CurrentStatus || "unknown";
    const dotColor = !isEnabled ? "#D1D5DB" : status === "healthy" ? "#22C55E" : status === "warning" ? "#F59E0B" : status === "critical" ? "#EF4444" : "#D1D5DB";
    const isSuppressed = m.IsSuppressed && isEnabled;
    const rowKey = `monitor-${m.MonitorID}`;
    const isHovered = hoveredRow === rowKey;
    const rowBg = isHovered ? "#F0F7FB" : "#FFFFFF";
    const rowStyle = { display:"grid", gridTemplateColumns:MONITOR_COL_GRID, borderBottom:"1px solid #F1F5F9", animation:`fadeIn 0.2s ease ${i*0.015}s both`, opacity: isEnabled ? 1 : 0.38, background: rowBg, transition:"background 100ms ease", cursor:"default", userSelect:"text" };
    const cell = { fontSize:12, fontWeight:400, color:"#374151", padding:"5px 10px" };
    return (
      <div key={m.MonitorID} className="mon-row"
        onMouseEnter={() => setHoveredRow(rowKey)}
        onMouseLeave={() => setHoveredRow(null)}
        style={rowStyle}>
        <div style={{ ...cell, textAlign:"right" }}>
          <span style={{ fontSize:11, color:"#6B7280", background:"#f8fafc", borderRadius:3, padding:"1px 6px", whiteSpace:"nowrap" }}>{m.MonitorType}</span>
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }}>
          <span style={{ display:"inline-block", width:10, height:10, borderRadius:"50%", flexShrink:0,
            background: isSuppressed ? "transparent" : dotColor,
            border: isSuppressed ? `1.5px dashed ${dotColor}` : "none" }} />
        </div>
        <div style={{ ...cell, color:"#111827", paddingLeft: isChild ? 32 : 10, fontWeight:400 }}>
          <span onClick={()=>openEdit(m)} style={{ color:"#006D8C", cursor:"pointer" }}
            onMouseEnter={e=>e.currentTarget.style.textDecoration="underline"}
            onMouseLeave={e=>e.currentTarget.style.textDecoration="none"}>
            {m.MonitorName}
          </span>
        </div>
        <div style={cell}>{m.SourceMachine ? (machines?.find(mc=>mc.MachineName===m.SourceMachine)?.MachineAlias || m.SourceMachine) : <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{(()=>{
          if (m.MonitorType==="Database") {
            try {
              const tc=JSON.parse(m.TypeConfigJson||"{}");
              if (tc.DbHost) return tc.DbHost+(tc.DbName?" / "+tc.DbName:"");
            } catch(e) {}
          }
          return (m.IPAddressOrUrl||"")+(m.Port?`:${m.Port}`:"");
        })()}</div>
        <div style={{ ...cell, textAlign:"center" }}>{m.CheckFrequencySeconds}s</div>
        <div style={{ ...cell, textAlign:"center" }}>{(()=>{
          const st = (m.MonitorSubType||'').toLowerCase();
          const lv = m.LastValue;
          const rt = m.LastResponseTimeMs;
          // WMI/agent resource monitors: use LastValue (actual measured value)
          if (st==='infra-cpu'||st==='infra-mem'||st==='infra-disk'||st==='infra-diskio')
            return lv != null ? `${Number(lv).toFixed(1)}%` : <span style={{ color:"#D1D5DB" }}>—</span>;
          if (st==='infra-net')
            return lv != null ? `${Number(lv).toFixed(1)} Mbps` : <span style={{ color:"#D1D5DB" }}>—</span>;
          // SSL certificate monitors: use LastValue (days until expiry)
          if (st === 'web-ssl')
            return lv != null ? `${Math.round(lv)}d` : <span style={{ color:"#D1D5DB" }}>—</span>;
          // Latency-based monitors: use LastResponseTimeMs
          if (['infra-ping','web-http','web-content','web-cm','web-load','web-headers','web-apiresp','web-dns','web-redir','tcp-port','app-port','db-conn','db-query'].includes(st))
            return rt != null ? `${rt}ms` : <span style={{ color:"#D1D5DB" }}>—</span>;
          return <span style={{ color:"#D1D5DB" }}>—</span>;
        })()}</div>
        <div style={{ ...cell, textAlign:"center" }}>{m.Uptime30Day != null ? `${Number(m.Uptime30Day).toFixed(1)}%` : <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e=>e.stopPropagation()}>
          {canManage && <Btn variant="secondary"
            disabled={runningId === m.MonitorID}
            onClick={async (e) => {
              e.stopPropagation();
              setRunningId(m.MonitorID);
              try {
                const updated = await api("POST", `/monitors/${m.MonitorID}/check`);
                setMonitors(prev => prev.map(x => x.MonitorID === m.MonitorID ? { ...x, ...updated } : x));
              } catch(_) {}
              finally { setRunningId(null); }
            }}
            style={{ padding:"3px 6px", fontSize:11, minWidth:24, textAlign:"center" }}
            title="Run check now"
          >{runningId === m.MonitorID ? "…" : "▶"}</Btn>}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e=>e.stopPropagation()}>
          {canManage && <span onClick={(e)=>toggleAlertsEnabled(m,e)} title={alertsEnabled?"Alerts on":"Alerts off"}
            style={{ fontSize:13, cursor:"pointer", color:alertsEnabled?"#006D8C":"#C0D8E8", userSelect:"none", lineHeight:1 }}>
            {alertsEnabled ? "🔔" : "🔕"}
          </span>}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e=>e.stopPropagation()}>
          {canManage && <Btn variant="secondary" onClick={()=>openEdit(m)} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e=>e.stopPropagation()}>
          {canManage && <SmallToggle checked={isEnabled} onClick={(e)=>toggleEnabled(m,e)} />}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e=>e.stopPropagation()}>
          {canManage && <Btn variant="danger" onClick={()=>setDeleteTarget(m)} style={{ padding:"3px 6px", fontSize:11 }}>Del</Btn>}
        </div>
      </div>
    );
  };

  const renderSyntheticFlowRow = (f, i) => {
    const isEnabled = f.IsEnabled !== false;
    const rowKey = `synth-${f.FlowId}`;
    const isHovered = hoveredRow === rowKey;
    const rowBg = isHovered ? "#F0F7FB" : "#FFFFFF";
    const cell = { fontSize:12, fontWeight:400, color:"#374151", padding:"5px 10px" };
    let dotColor = "#9CA3AF";
    if (isEnabled && f.LastRunPassed != null) {
      if (!f.LastRunPassed) { dotColor = c.red; }
      else if (f.LastDurationMs != null && f.DurationCritMs != null && f.LastDurationMs >= f.DurationCritMs) { dotColor = c.red; }
      else if (f.LastDurationMs != null && f.DurationWarnMs != null && f.LastDurationMs >= f.DurationWarnMs) { dotColor = c.amber; }
      else { dotColor = c.green; }
    }
    const sfUptimeColor = pct => pct == null ? "#9CA3AF" : pct >= 99 ? c.green : pct >= 95 ? c.amber : c.red;
    const sfFmtDur = ms => ms == null ? null : ms < 1000 ? `${ms}ms` : `${(ms/1000).toFixed(1)}s`;
    const sfRunningKey = `sf-${f.FlowId}`;
    return (
      <div key={`synth-${f.FlowId}`} className="mon-row"
        onClick={() => onNavigateSynthetic && onNavigateSynthetic(f.FlowId)}
        onMouseEnter={() => setHoveredRow(rowKey)}
        onMouseLeave={() => setHoveredRow(null)}
        style={{ display:"grid", gridTemplateColumns:MONITOR_COL_GRID, borderBottom:"1px solid #F1F5F9", animation:`fadeIn 0.2s ease ${i*0.015}s both`, opacity: isEnabled ? 1 : 0.38, background: rowBg, transition:"background 100ms ease", cursor:"pointer" }}>
        <div style={{ ...cell, textAlign:"right" }}>
          <span style={{ fontSize:11, color:"#6B7280", background:"#f8fafc", borderRadius:3, padding:"1px 6px", whiteSpace:"nowrap" }}>Synthetic</span>
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }}>
          <span style={{ display:"inline-block", width:10, height:10, borderRadius:"50%", background:dotColor }} />
        </div>
        <div style={{ ...cell, color:c.blue, cursor:"pointer" }}
          onClick={e => { e.stopPropagation(); onNavigateSynthetic && onNavigateSynthetic(f.FlowId); }}>
          {f.FlowName}
        </div>
        <div style={cell}>{f.ExecutionTarget ? f.ExecutionTarget.charAt(0).toUpperCase() + f.ExecutionTarget.slice(1) : <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }} title={f.FirstNavigateUrl||""}>{f.FirstNavigateUrl ? (f.FirstNavigateUrl.length > 40 ? f.FirstNavigateUrl.slice(0,40)+"…" : f.FirstNavigateUrl) : <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, textAlign:"center" }}>{f.IntervalMinutes}m</div>
        <div style={{ ...cell, textAlign:"center", fontFamily:"monospace" }}>{sfFmtDur(f.LastDurationMs) ?? <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, textAlign:"center", fontFamily:"monospace", color:sfUptimeColor(f.UptimePct30d) }}>{f.UptimePct30d != null ? `${Number(f.UptimePct30d).toFixed(1)}%` : <span style={{ color:"#D1D5DB" }}>—</span>}</div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
          {canManage && <Btn variant="secondary"
            disabled={runningId === sfRunningKey}
            onClick={async e => { e.stopPropagation(); setRunningId(sfRunningKey); try { await api("POST", `/synthetic/flows/${f.FlowId}/run`); } catch(_) {} finally { setRunningId(null); } }}
            style={{ padding:"3px 6px", fontSize:11, minWidth:24, textAlign:"center" }}
            title="Run now"
          >{runningId === sfRunningKey ? "…" : "▶"}</Btn>}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
          <span style={{ fontSize:13, color:"#006D8C", lineHeight:1 }}>🔔</span>
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
          {canManage && <Btn variant="secondary" onClick={e => { e.stopPropagation(); onNavigateSynthetic && onNavigateSynthetic(f.FlowId); }} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
          {canManage && <SmallToggle checked={isEnabled} onClick={async () => {
            try {
              const detail = await api("GET", `/synthetic/flows/${f.FlowId}`);
              if (!detail) return;
              await api("PUT", `/synthetic/flows/${f.FlowId}`, { ...detail, IsEnabled: !f.IsEnabled, Steps: detail.Steps||[] });
              setSyntheticFlows(prev => prev.map(x => x.FlowId === f.FlowId ? { ...x, IsEnabled: !f.IsEnabled } : x));
            } catch(_) {}
          }} />}
        </div>
        <div style={{ ...cell, display:"flex", alignItems:"center", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
          {canManage && <Btn variant="danger" onClick={e => { e.stopPropagation(); showConfirm("Delete this flow?", async () => { try { await api("DELETE", `/synthetic/flows/${f.FlowId}`); setSyntheticFlows(prev => prev.filter(x => x.FlowId !== f.FlowId)); } catch(_) {} }); }} style={{ padding:"3px 6px", fontSize:11 }}>Del</Btn>}
        </div>
      </div>
    );
  };

  if (subView === "analysis") {
    const sinceMs   = analysisRange === "7d" ? 7*24*3600*1000 : 24*3600*1000;
    const sinceDate = new Date(Date.now() - sinceMs);
    const nowDate   = new Date();
    const allAlerts = [
      ...(analysisData||[]).flatMap(mon=>(mon.Alerts||[]).map(a=>({...a,monitorName:mon.MonitorName,monitorId:mon.MonitorId}))),
      ...(analysisSyntheticAlerts||[]).map(a=>({...a,monitorName:a.FlowName||'Synthetic',monitorId:null}))
    ];
    const sortedAlerts = [...allAlerts].sort((a,b)=>new Date(a.Timestamp)-new Date(b.Timestamp));
    const totalAlerts = allAlerts.length;
    const tsToXPct = ts => { const t=new Date(ts).getTime(); return Math.max(0,Math.min(100,(t-sinceDate.getTime())/sinceMs*100)); };
    const statusLineCol = s => s==="healthy"?"#006D8C":s==="warning"?"#E89A2E":s==="critical"?"#D95C5C":"#9CA3AF";
    const statusDotCol  = s => ({healthy:"#22C55E",warning:"#F59E0B",critical:"#EF4444"})[s]||"#D1D5DB";
    const fmtTs = d => d.toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"});
    const sfPassing = analysisSyntheticFlows.filter(f => f.CurrentStatus === "passing").length;
    const sfFailing = analysisSyntheticFlows.filter(f => f.CurrentStatus === "failing").length;
    return (
      <div style={{ display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#fff" }}>
        {/* Header */}
        <div style={{ background:"#1E2B3C", padding:"10px 16px", display:"flex", alignItems:"center", gap:12, flexShrink:0 }}>
          <button onClick={()=>{ setSubView("list"); setAnalysisGroup(null); }}
            style={{ background:"rgba(255,255,255,0.12)", border:"1px solid rgba(255,255,255,0.2)", borderRadius:6, color:"#fff", fontSize:12, padding:"4px 10px", cursor:"pointer", fontWeight:500 }}>
            ← Back
          </button>
          <span style={{ fontSize:14, fontWeight:700, color:"#fff" }}>{analysisGroup}</span>
          <span style={{ fontSize:12, color:"rgba(255,255,255,0.45)" }}>· Correlation Analysis</span>
          <div style={{ marginLeft:"auto", display:"flex", gap:6 }}>
            {["24h","7d"].map(r=>(
              <button key={r} onClick={()=>setAnalysisRange(r)}
                style={{ fontSize:11, padding:"3px 10px", borderRadius:5, border:"none", cursor:"pointer", fontWeight:600,
                  background:analysisRange===r?"#5DD4F4":"rgba(255,255,255,0.12)",
                  color:analysisRange===r?"#1E2B3C":"#fff" }}>
                {r}
              </button>
            ))}
          </div>
        </div>
        {/* Body row: charts + inspector + diagnosis */}
        <div style={{ flex:1, display:"flex", flexDirection:"row", overflow:"hidden" }}>
        {/* Column 1 — Charts */}
        <div style={{ flex:1, minWidth:0, overflowY:"auto", overflowX:"auto", padding:"12px 16px" }}>
          {analysisLoading ? (
            <div style={{ textAlign:"center", padding:"48px 0", color:"#6B7280", fontSize:13 }}>Loading analysis data…</div>
          ) : analysisErr ? (
            <div style={{ padding:16, color:"#D95C5C", fontSize:12 }}>{analysisErr}</div>
          ) : analysisData.length === 0 ? (
            <div style={{ textAlign:"center", padding:"48px 0", color:"#6B7280", fontSize:13 }}>No check history found for this group in the selected time range.</div>
          ) : (
            <div style={{ minWidth:"100%" }}>
              {/* Alert timeline strip */}
              <div style={{ background:"#fff", border:"1px solid #e2e5ea", borderRadius:6, padding:"8px 12px", marginBottom:10 }}>
                <div style={{ fontSize:10, fontWeight:700, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:5 }}>
                  Alert Timeline · {totalAlerts} alert{totalAlerts!==1?"s":""} total
                </div>
                <div style={{ position:"relative", height:16, background:"#f1f5f9", borderRadius:3 }}>
                  {allAlerts.map((al,i)=>{
                    const alMs=new Date(al.Timestamp).getTime();
                    const isSel=pinnedTimestamp&&Math.abs(alMs-new Date(pinnedTimestamp).getTime())<2*60*1000;
                    return (
                      <div key={i}
                        onClick={()=>{ setPinnedTimestamp(al.Timestamp); const idx=sortedAlerts.findIndex(a=>a.Timestamp===al.Timestamp&&a.monitorId===al.monitorId); setSelectedAlertIdx(idx>=0?idx:null); }}
                        title={`${al.Severity} · ${new Date(al.Timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`}
                        style={{ position:"absolute", left:tsToXPct(al.Timestamp)+"%", top:isSel?0:2, bottom:isSel?0:2, width:isSel?3:2,
                          background:al.Severity==="critical"?"#D95C5C":"#E89A2E",
                          opacity:isSel?1:0.8, cursor:"pointer", zIndex:2 }} />
                    );
                  })}
                  {pinnedTimestamp && (()=>{
                    const pPct=tsToXPct(pinnedTimestamp);
                    return pPct>=0&&pPct<=100 ? <div style={{ position:"absolute", left:pPct+"%", top:0, bottom:0, width:1.5, background:"#E89A2E", opacity:0.9, pointerEvents:"none" }} /> : null;
                  })()}
                </div>
                <div style={{ display:"flex", justifyContent:"space-between", fontSize:9, color:"#94a3b8", marginTop:3 }}>
                  <span>{fmtTs(sinceDate)}</span><span>{fmtTs(nowDate)}</span>
                </div>
              </div>
              {/* Pin indicator bar */}
              {pinnedTimestamp && (
                <div style={{ marginBottom:8, padding:"5px 10px", background:"#FDF3E0", borderLeft:"3px solid #E89A2E", borderRadius:4, fontSize:11, color:"#92400E", display:"flex", alignItems:"center", gap:8, flexWrap:"wrap" }}>
                  <span>📍 Point-in-time: <strong>{new Date(pinnedTimestamp).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}</strong></span>
                  <span style={{ color:"#B45309", fontSize:10 }}>· click any chart or alert tick to move pin</span>
                  <button onClick={()=>setPinnedTimestamp(null)} style={{ marginLeft:"auto", fontSize:10, padding:"1px 7px", border:"1px solid #E89A2E", borderRadius:3, background:"transparent", color:"#92400E", cursor:"pointer" }}>✕ Clear</button>
                </div>
              )}
              {/* Synthetic Flows section */}
              {analysisSyntheticFlows.length > 0 && (
                <div style={{ marginBottom:10 }}>
                  <div style={{ borderLeft:"2px solid #006D8C", paddingLeft:8, marginBottom:8, display:"flex", alignItems:"center", gap:8, flexWrap:"wrap" }}>
                    <span style={{ fontSize:10, fontWeight:700, color:"#006D8C", textTransform:"uppercase", letterSpacing:"0.08em" }}>Synthetic Flows</span>
                    <span style={{ fontSize:10, background:"#f1f5f9", color:"#64748b", borderRadius:10, padding:"1px 7px" }}>{analysisSyntheticFlows.length} flow{analysisSyntheticFlows.length!==1?"s":""}</span>
                    {sfPassing>0 && <span style={{ fontSize:10, background:"#EAF3DE", color:"#3B6D11", borderRadius:10, padding:"1px 7px", fontWeight:500 }}>{sfPassing} passing</span>}
                    {sfFailing>0 && <span style={{ fontSize:10, background:"#FCEBEB", color:"#A32D2D", borderRadius:10, padding:"1px 7px", fontWeight:500 }}>{sfFailing} failing</span>}
                  </div>
                  {analysisSyntheticFlows.map(flow => {
                    const sfDpts = flow.DataPoints || [];
                    const sfGapMs = (flow.ScheduleIntervalMinutes || 30) * 2 * 60 * 1000;
                    const sfDurs = sfDpts.filter(d => d.DurationMs != null).map(d => d.DurationMs);
                    const sfMaxDur = sfDurs.length ? Math.max(...sfDurs) : 0;
                    const sfWarnMs = flow.WarnThresholdMs;
                    const sfCritMs = flow.CritThresholdMs;
                    const sfYMax = Math.max(sfCritMs ? sfCritMs * 1.2 : 0, sfMaxDur * 1.1, 100);
                    const sfW = 700, sfH = 60, sfP = 2;
                    const sfTimes = sfDpts.filter(d => d.Timestamp).map(d => new Date(d.Timestamp).getTime());
                    const sfAnalysisStart = sinceDate.getTime();
                    const sfXMinRaw = sfTimes.length ? Math.min(...sfTimes) : sfAnalysisStart;
                    const sfXMax = sfTimes.length ? Math.max(...sfTimes) : sfAnalysisStart + 1;
                    const sfDataRange = (sfXMax - sfXMinRaw) || 1;
                    const sfHasLeadingGap = sfTimes.length > 0 && (sfXMinRaw - sfAnalysisStart) > 0.1 * sfDataRange;
                    const sfXMin = sfHasLeadingGap ? sfAnalysisStart : sfXMinRaw;
                    const sfXRange = (sfXMax - sfXMin) || 1;
                    const sfX = ts => (new Date(ts).getTime() - sfXMin) / sfXRange * sfW;
                    const sfY = v => sfH - sfP - (v / sfYMax) * (sfH - 2*sfP);
                    const sfWarnY = sfWarnMs != null ? sfY(sfWarnMs) : null;
                    const sfCritY = sfCritMs != null ? sfY(sfCritMs) : null;
                    const sfHasAxis = sfWarnMs != null || sfCritMs != null;
                    const sfDot = flow.CurrentStatus === "passing" ? "#639922" : flow.CurrentStatus === "failing" ? "#E24B4A" : "#9CA3AF";
                    const sfSegs = [], sfGaps = [];
                    for (let si = 0; si < sfDpts.length - 1; si++) {
                      const sa = sfDpts[si], sb = sfDpts[si+1];
                      const saT = new Date(sa.Timestamp).getTime(), sbT = new Date(sb.Timestamp).getTime();
                      if (sa.DurationMs != null && sb.DurationMs != null && (sbT - saT) <= sfGapMs) {
                        sfSegs.push({ x1:sfX(sa.Timestamp), y1:sfY(sa.DurationMs), x2:sfX(sb.Timestamp), y2:sfY(sb.DurationMs), passed:sa.Passed });
                      } else if (sa.DurationMs != null && (sbT - saT) > sfGapMs) {
                        const sgy = sfY(sa.DurationMs);
                        sfGaps.push({ x1:sfX(sa.Timestamp), x2:sfX(sb.Timestamp), y:sgy, midX:(sfX(sa.Timestamp)+sfX(sb.Timestamp))/2 });
                      }
                    }
                    if (sfHasLeadingGap && sfDpts.length > 0) {
                      const leadX = sfX(sfDpts[0].Timestamp);
                      const leadY = sfDpts[0].DurationMs != null ? sfY(sfDpts[0].DurationMs) : sfH / 2;
                      sfGaps.unshift({ x1: 0, x2: leadX, y: leadY, midX: leadX / 2 });
                    }
                    return (
                      <div key={flow.FlowId} style={{ background:"#fff", border:"1px solid #e2e5ea", borderRadius:6, marginBottom:8, overflow:"hidden" }}>
                        <div style={{ display:"flex", alignItems:"center", gap:8, padding:"7px 12px", borderBottom:"1px solid #f1f5f9", flexWrap:"wrap" }}>
                          <span style={{ width:7, height:7, borderRadius:"50%", background:sfDot, flexShrink:0, display:"inline-block" }} />
                          <span style={{ fontSize:12, fontWeight:600, color:"#006D8C", flex:1, minWidth:120 }}>{flow.FlowName}</span>
                          <span style={{ fontSize:10, background:"#E1F5EE", color:"#0F6E56", borderRadius:10, padding:"1px 7px", fontWeight:500 }}>synthetic</span>
                          {sfDurs.length>0 && <span style={{ fontSize:11, color:"#64748b" }}>avg {Math.round(sfDurs.reduce((s,v)=>s+v,0)/sfDurs.length).toLocaleString()}ms</span>}
                          {sfDurs.length>0 && <span style={{ fontSize:11, color:"#94a3b8" }}>peak {Math.max(...sfDurs).toLocaleString()}ms</span>}
                        </div>
                        <div style={{ padding:"4px 12px 6px" }}>
                          <div style={{ position:"relative", display:"flex" }}>
                            {sfHasAxis && (
                              <div style={{ width:44, flexShrink:0, height:sfH, display:"flex", flexDirection:"column", justifyContent:"space-between", paddingTop:2, paddingBottom:2, pointerEvents:"none" }}>
                                {sfCritMs != null && <span style={{ fontSize:9, color:"#A32D2D", lineHeight:1 }}>{sfCritMs.toLocaleString()}ms</span>}
                                {sfWarnMs != null && <span style={{ fontSize:9, color:"#BA7517", lineHeight:1, marginTop:"auto" }}>{sfWarnMs.toLocaleString()}ms</span>}
                              </div>
                            )}
                            <div style={{ flex:1, minWidth:0 }}>
                              <svg viewBox={`0 0 ${sfW} ${sfH}`} preserveAspectRatio="none" style={{ width:"100%", height:sfH, display:"block" }}>
                                <rect width={sfW} height={sfH} fill="#f8fafc" rx={2} />
                                {sfWarnY!=null && sfWarnY>=0 && sfWarnY<=sfH && <line x1={0} y1={sfWarnY} x2={sfW} y2={sfWarnY} stroke="#BA7517" strokeDasharray="5,4" strokeWidth={1} opacity={0.6} />}
                                {sfCritY!=null && sfCritY>=0 && sfCritY<=sfH && <line x1={0} y1={sfCritY} x2={sfW} y2={sfCritY} stroke="#E24B4A" strokeDasharray="2,3" strokeWidth={1} opacity={0.5} />}
                                {sfSegs.map((seg,si2) => <line key={si2} x1={seg.x1} y1={seg.y1} x2={seg.x2} y2={seg.y2} stroke={seg.passed?"#639922":"#E24B4A"} strokeWidth={2} strokeLinecap="round" />)}
                                {sfGaps.map((gap,gi) => (
                                  <g key={gi}>
                                    <line x1={gap.x1} y1={gap.y} x2={gap.x2} y2={gap.y} stroke="#B4B2A9" strokeDasharray="4,4" strokeWidth={1.5} />
                                    <rect x={gap.midX-20} y={gap.y-8} width={40} height={13} rx={3} fill="#F1EFE8" />
                                    <text x={gap.midX} y={gap.y+1} textAnchor="middle" fontSize={8} fill="#888780">no data</text>
                                  </g>
                                ))}
                              </svg>
                            </div>
                          </div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              )}
              {analysisSyntheticFlows.length > 0 && (
                <div style={{ fontSize:10, fontWeight:700, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:6, borderLeft:"2px solid #e2e5ea", paddingLeft:8, marginTop:2 }}>Infrastructure & Website Monitors</div>
              )}
              {/* Per-monitor cards */}
              {analysisData.map(mon=>{
                const dpts    = mon.DataPoints||[];
                const isMonSsl = mon.MonitorSubType === "web-ssl";
                const indexed = dpts.map((d,i) => {
                  const v = d.Value != null ? d.Value : (isMonSsl && d.StatusCode != null ? d.StatusCode / 10 : null);
                  return {...d, Value: v, i};
                }).filter(d => d.Value != null);
                const totalPts = Math.max(dpts.length,2);
                const vals    = indexed.map(d=>d.Value);
                const minV    = vals.length ? Math.min(...vals) : 0;
                const maxV    = vals.length ? Math.max(...vals) : 1;
                const vRange  = (maxV-minV)||1;
                const svgW=1000, svgH=60, pad=3;
                const xOf = idx => pad+(idx/(totalPts-1))*(svgW-2*pad);
                const yOf = v => svgH-pad-((v-minV)/vRange)*(svgH-2*pad-4);
                const pathD = indexed.length>=2
                  ? indexed.map((d,ii)=>`${ii===0?"M":"L"}${xOf(d.i).toFixed(1)},${yOf(d.Value).toFixed(1)}`).join(" ")
                  : "";
                // Trend: compare first-half avg vs second-half avg
                const half = Math.floor(indexed.length/2);
                const avgOf = arr => arr.length ? arr.reduce((s,d)=>s+d.Value,0)/arr.length : null;
                const fAvg = avgOf(indexed.slice(0,half)), sAvg = avgOf(indexed.slice(half));
                let trendArrow="", trendCol="#4A4A4A";
                if (fAvg!=null && sAvg!=null && fAvg>0) {
                  const pct=(sAvg-fAvg)/fAvg*100;
                  if      (pct>5)   { trendArrow="↑"; trendCol="#D95C5C"; }
                  else if (pct>2)   { trendArrow="↗"; trendCol="#E89A2E"; }
                  else if (pct>=-2) { trendArrow="→"; trendCol="#4A4A4A"; }
                  else if (pct>=-5) { trendArrow="↘"; trendCol="#008C6F"; }
                  else              { trendArrow="↓"; trendCol="#008C6F"; }
                }
                // Threshold Y positions
                const warnY = mon.WarnThreshold!=null ? yOf(mon.WarnThreshold) : null;
                const critY = mon.CritThreshold!=null ? yOf(mon.CritThreshold) : null;
                const inSvg = y => y!=null && y>=0 && y<=svgH;
                const isMem = mon.MonitorSubType==="infra-mem"||mon.MonitorSubType==="infra-memory";
                const snapData = analysisSnapshotData[mon.MonitorId]; // { Latest, Selected }
                const snapOpen = !!analysisSnapOpen[mon.MonitorId];
                const isPinned = analysisSnapshotPin?.monitorId===mon.MonitorId;
                return (
                  <div key={mon.MonitorId} style={{ background:"#fff", border:"1px solid #e2e5ea", borderRadius:6, marginBottom:8, overflow:"hidden" }}>
                    <div style={{ display:"flex", alignItems:"center", gap:8, padding:"7px 12px", borderBottom:"1px solid #f1f5f9", flexWrap:"wrap" }}>
                      <span style={{ width:8, height:8, borderRadius:"50%", background:statusDotCol(mon.CurrentStatus), flexShrink:0, display:"inline-block" }} />
                      <span
                        style={{ fontSize:12, fontWeight:600, color:"#1A1A1A", flex:1, minWidth:120, cursor:"pointer" }}
                        title="Edit monitor"
                        onClick={()=>{ const m=monitors.find(x=>x.MonitorID===mon.MonitorId); if(m) { setEditMonitor(m); setEditMonitorOrigin("Correlation Analysis"); setSubView("form"); } }}
                        onMouseEnter={e=>{e.currentTarget.style.textDecoration="underline";}}
                        onMouseLeave={e=>{e.currentTarget.style.textDecoration="none";}}
                      >{mon.MonitorName}<span style={{ marginLeft:5, fontSize:10, color:"#94a3b8", fontWeight:400 }}>✎</span></span>
                      <span style={{ fontSize:10, background:"#e0f2f7", color:"#006D8C", borderRadius:10, padding:"1px 7px", fontWeight:500 }}>{mon.MonitorSubType||mon.MonitorType}</span>
                      {mon.AvgValue>0 && <span style={{ fontSize:11, color:"#64748b" }}>avg {mon.AvgValue}{mon.Unit}</span>}
                      {mon.PeakValue>0 && <span style={{ fontSize:11, color:"#94a3b8" }}>peak {mon.PeakValue}{mon.Unit}
                        {trendArrow && <span style={{ marginLeft:4, color:trendCol, fontWeight:700 }}>{trendArrow}</span>}
                      </span>}
                      {mon.AlertCount>0 && (()=>{
                        const hasCrit = (mon.Alerts||[]).some(a=>a.Severity==="critical");
                        return <span style={{ fontFamily:"DM Sans,sans-serif", fontSize:11, fontWeight:600,
                          background:hasCrit?"#FAEAEA":"#FDF3E0", color:hasCrit?"#D95C5C":"#E89A2E",
                          borderRadius:4, padding:"1px 7px" }}>{mon.AlertCount} alert{mon.AlertCount!==1?"s":""}</span>;
                      })()}
                      {isMem && <button onClick={()=>setAnalysisSnapOpen(prev=>({...prev,[mon.MonitorId]:!snapOpen}))}
                        style={{ marginLeft:"auto", fontSize:10, padding:"2px 8px", border:"1px solid #cbd5e1",
                          borderRadius:4, background:snapOpen?"#e0f2f7":"#f8fafc", color:"#006D8C",
                          cursor:"pointer", fontWeight:500 }}>
                        {snapOpen?"Hide Processes":"Processes"}
                      </button>}
                    </div>
                    <div style={{ padding:"4px 12px 0" }}>
                      <svg viewBox={`0 0 ${svgW} ${svgH}`} preserveAspectRatio="none" style={{ width:"100%", height:60, display:"block", cursor:"crosshair" }}
                        onClick={e=>{
                          const rect=e.currentTarget.getBoundingClientRect();
                          const f=Math.max(0,Math.min(1,(e.clientX-rect.left)/rect.width));
                          const ts=new Date(sinceDate.getTime()+f*(nowDate.getTime()-sinceDate.getTime())).toISOString();
                          setPinnedTimestamp(ts);
                          if (isMem) { setAnalysisSnapOpen(prev=>({...prev,[mon.MonitorId]:true})); fetchSnapshot(mon.MonitorId,ts); }
                        }}>
                        <rect width={svgW} height={svgH} fill="#f8fafc" rx={2} />
                        {/* Danger zone fills — above crit (red), warn-to-crit band (amber) */}
                        {critY!=null && <rect x={0} y={0} width={svgW} height={Math.max(0,critY)} fill="rgba(217,92,92,0.07)" />}
                        {warnY!=null && <rect x={0} y={critY!=null?Math.max(0,critY):0} width={svgW}
                          height={Math.max(0,warnY-(critY!=null?Math.max(0,critY):0))} fill="rgba(232,154,46,0.07)" />}
                        {/* Alert markers */}
                        {(()=>{
                          const dedupedAlerts = [];
                          (mon.Alerts||[]).forEach(al=>{
                            const ax = pad+tsToXPct(al.Timestamp)/100*(svgW-2*pad);
                            const existing = dedupedAlerts.find(d=>Math.abs(d.ax-ax)<3);
                            if (existing) { if (al.Severity==="critical") { existing.sev="critical"; existing.al=al; } }
                            else dedupedAlerts.push({ax, sev:al.Severity, al});
                          });
                          const selAl2 = selectedAlertIdx!==null ? sortedAlerts[selectedAlertIdx] : null;
                          return dedupedAlerts.map((d,i)=>{
                            const isSel = selAl2 && d.al && selAl2.monitorId===mon.MonitorId &&
                              Math.abs(new Date(selAl2.Timestamp).getTime()-new Date(d.al.Timestamp).getTime())<3*60*1000;
                            return (
                              <g key={i} style={{ cursor:"pointer" }} onClick={()=>{
                                if(d.al){ const idx=sortedAlerts.findIndex(a=>a.Timestamp===d.al.Timestamp&&a.monitorId===mon.MonitorId); setSelectedAlertIdx(idx>=0?idx:null); setPinnedTimestamp(d.al.Timestamp); }
                              }}>
                                {isSel && <line x1={d.ax.toFixed(1)} y1={0} x2={d.ax.toFixed(1)} y2={svgH} stroke="#fff" strokeWidth={5} opacity={0.6} />}
                                <line x1={d.ax.toFixed(1)} y1={0} x2={d.ax.toFixed(1)} y2={svgH}
                                  stroke={d.sev==="critical"?"#D95C5C":"#E89A2E"}
                                  strokeWidth={isSel?2.5:1.5} strokeDasharray={isSel?"none":"3 2"} opacity={isSel?1:0.7} />
                                {isSel && <circle cx={d.ax.toFixed(1)} cy={(svgH/2).toFixed(1)} r={4} fill={d.sev==="critical"?"#D95C5C":"#E89A2E"} stroke="#fff" strokeWidth={1.5} />}
                                <rect x={(d.ax-5).toFixed(1)} y={0} width={10} height={svgH} fill="transparent" />
                              </g>
                            );
                          });
                        })()}
                        {/* Warn threshold line + label */}
                        {warnY!=null && inSvg(warnY) && <g>
                          <line x1={0} y1={warnY.toFixed(1)} x2={svgW} y2={warnY.toFixed(1)}
                            stroke="#E89A2E" strokeWidth={1} strokeDasharray="4,3" opacity={0.7} />
                          <text x={svgW-4} y={(warnY-2).toFixed(1)} textAnchor="end" fontSize={9} fill="#E89A2E" fontFamily="DM Sans,sans-serif">W:{mon.WarnThreshold}{mon.Unit}</text>
                        </g>}
                        {/* Crit threshold line + label */}
                        {critY!=null && inSvg(critY) && <g>
                          <line x1={0} y1={critY.toFixed(1)} x2={svgW} y2={critY.toFixed(1)}
                            stroke="#D95C5C" strokeWidth={1} strokeDasharray="4,3" opacity={0.7} />
                          <text x={svgW-4} y={(critY-2).toFixed(1)} textAnchor="end" fontSize={9} fill="#D95C5C" fontFamily="DM Sans,sans-serif">C:{mon.CritThreshold}{mon.Unit}</text>
                        </g>}
                        {/* Data line */}
                        {pathD && <path d={pathD} fill="none" stroke={statusLineCol(mon.CurrentStatus)} strokeWidth={1.5} strokeLinecap="round" strokeLinejoin="round" />}
                        {/* Pin line + dot at pinned timestamp */}
                        {pinnedTimestamp && (()=>{
                          const pinMs2=new Date(pinnedTimestamp).getTime();
                          const totalMs=nowDate.getTime()-sinceDate.getTime();
                          const pinFrac=Math.max(0,Math.min(1,(pinMs2-sinceDate.getTime())/totalMs));
                          const xPin=pad+pinFrac*(svgW-2*pad);
                          const nearest=indexed.length>0?indexed.reduce((a,b)=>{
                            const aT=sinceDate.getTime()+(a.i/(Math.max(totalPts-1,1)))*totalMs;
                            const bT=sinceDate.getTime()+(b.i/(Math.max(totalPts-1,1)))*totalMs;
                            return Math.abs(aT-pinMs2)<Math.abs(bT-pinMs2)?a:b;
                          }):null;
                          const yPin=nearest?yOf(nearest.Value):svgH/2;
                          return (<g>
                            <line x1={xPin.toFixed(1)} y1={0} x2={xPin.toFixed(1)} y2={svgH} stroke="#E89A2E" strokeWidth={1.5} strokeDasharray="4 3" opacity={0.85} />
                            {nearest && <circle cx={xPin.toFixed(1)} cy={yPin.toFixed(1)} r={4} fill="#E89A2E" stroke="#fff" strokeWidth={1.5} />}
                            {isMem && nearest && (
                              <g>
                                <rect x={Math.max(2,Math.min(svgW-50,xPin-22))} y={Math.max(2,yPin-17)} width={44} height={13} rx={2} fill="#E89A2E" opacity={0.9} />
                                <text x={(Math.max(2,Math.min(svgW-50,xPin-22))+22).toFixed(1)} y={(Math.max(2,yPin-17)+9).toFixed(1)} textAnchor="middle" fontSize={8} fill="#fff" fontFamily="DM Sans,sans-serif">
                                  {new Date(pinnedTimestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}
                                </text>
                              </g>
                            )}
                          </g>);
                        })()}
                        {/* Y axis labels: min (bottom-left) and max+unit (top-left) */}
                        {vals.length>0 && <g>
                          <text x={pad+2} y={svgH-pad-2} textAnchor="start" fontSize={9} fill="#7A9AB8" fontFamily="DM Sans,sans-serif">{Math.round(minV)}</text>
                          <text x={pad+2} y={pad+10} textAnchor="start" fontSize={9} fill="#7A9AB8" fontFamily="DM Sans,sans-serif">{Math.round(maxV)}{mon.Unit}</text>
                        </g>}
                        {indexed.length===0 && <text x={svgW/2} y={svgH/2} textAnchor="middle" dominantBaseline="central" fontSize={14} fill="#d1d5db">No data</text>}
                      </svg>
                    </div>
                    <div style={{ display:"flex", justifyContent:"space-between", padding:"1px 12px 6px", fontSize:9, color:"#94a3b8" }}>
                      {[0,1,2,3].map(i=>{
                        const t=new Date(sinceDate.getTime()+(sinceMs/4)*i);
                        return <span key={i}>{fmtTs(t)}</span>;
                      })}
                      <span>{fmtTs(nowDate)}</span>
                    </div>
                    {/* Process snapshot panel */}
                    {isMem && snapOpen && (()=>{
                      const showSnap = (isPinned && snapData?.Selected) ? snapData.Selected : snapData?.Latest;
                      const isSelected = isPinned && snapData?.Selected;
                      return (
                        <div style={{ borderTop:"1px solid #f1f5f9", padding:"8px 12px 10px" }}>
                          <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:6 }}>
                            <span style={{ fontSize:10, fontWeight:600, color:"#475569", textTransform:"uppercase", letterSpacing:"0.05em" }}>
                              Top Processes — Memory
                            </span>
                            {showSnap?.Timestamp && <span style={{ fontSize:9, color:"#94a3b8" }}>
                              {isSelected?"Selected: ":"Latest: "}{new Date(showSnap.Timestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})}
                            </span>}
                            {isSelected && <button onClick={()=>{setAnalysisSnapshotPin(null);}}
                              style={{ fontSize:9, padding:"1px 6px", border:"1px solid #e2e5ea", borderRadius:3,
                                background:"#f8fafc", color:"#64748b", cursor:"pointer" }}>
                              ✕ Clear
                            </button>}
                            {isMem && <span style={{ fontSize:9, color:"#94a3b8", marginLeft:"auto" }}>Click chart to inspect a point</span>}
                            {analysisSnapshotLoading && <span style={{ fontSize:9, color:"#006D8C" }}>Loading…</span>}
                          </div>
                          {!showSnap && <div style={{ fontSize:11, color:"#94a3b8", fontStyle:"italic" }}>No snapshots recorded yet. Data will appear after the next agent check-in.</div>}
                          {showSnap?.Processes?.map((proc,pi)=>{
                            const barPct = Math.min(100, Number(proc.MemoryPct)||0);
                            return (
                              <div key={pi} style={{ display:"flex", alignItems:"center", gap:6, marginBottom:3 }}>
                                <span style={{ fontSize:9, color:"#64748b", width:14, textAlign:"right", flexShrink:0 }}>{proc.Rank}</span>
                                <span style={{ fontSize:10, color:"#1A1A1A", minWidth:0, flex:"0 0 180px", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{proc.Name}</span>
                                <div style={{ flex:1, height:8, background:"#f1f5f9", borderRadius:2, overflow:"hidden", minWidth:40 }}>
                                  <div style={{ width:barPct+"%", height:"100%", background:"#006D8C", borderRadius:2, opacity:0.7 }} />
                                </div>
                                <span style={{ fontSize:10, color:"#475569", width:46, textAlign:"right", flexShrink:0 }}>{Number(proc.MemoryPct).toFixed(1)}%</span>
                                <span style={{ fontSize:9, color:"#94a3b8", width:52, textAlign:"right", flexShrink:0 }}>{Number(proc.MemoryMB).toFixed(0)} MB</span>
                              </div>
                            );
                          })}
                        </div>
                      );
                    })()}
                  </div>
                );
              })}
              {/* Legend */}
              <div style={{ display:"flex", gap:16, padding:"4px 0 8px", fontSize:11, color:"#64748b", alignItems:"center" }}>
                <span style={{ display:"flex", alignItems:"center", gap:5 }}>
                  <svg width={22} height={12}><line x1={0} y1={6} x2={22} y2={6} stroke="#D95C5C" strokeWidth={1.5} strokeDasharray="3 2"/></svg>
                  Critical alert
                </span>
                <span style={{ display:"flex", alignItems:"center", gap:5 }}>
                  <svg width={22} height={12}><line x1={0} y1={6} x2={22} y2={6} stroke="#E89A2E" strokeWidth={1.5} strokeDasharray="3 2"/></svg>
                  Warning alert
                </span>
              </div>
            </div>
          )}
        </div>
        {/* ── Column 2 — Alert Inspector (always visible, 350px) ── */}
        {(()=>{
          const selAl = selectedAlertIdx !== null ? sortedAlerts[selectedAlertIdx] : null;
          const monData = selAl ? analysisData.find(m=>m.MonitorId===selAl.monitorId) : null;
          const sevCol = selAl ? (selAl.Severity==="critical"?"#D95C5C":"#E89A2E") : "#94a3b8";
          const sevBg  = selAl ? (selAl.Severity==="critical"?"#FAEAEA":"#FDF3E0") : "#f8fafc";
          const dpts = monData?.DataPoints||[];
          const alMs = selAl ? new Date(selAl.Timestamp).getTime() : 0;
          const totalMs = nowDate.getTime()-sinceDate.getTime();
          const nearestDp = dpts.length>0 ? dpts.reduce((best,dp,dpIdx)=>{
            const dpT = sinceDate.getTime()+(dpIdx/Math.max(dpts.length-1,1))*totalMs;
            const dist = Math.abs(dpT-alMs);
            return dist < best.dist ? {dp, dist} : best;
          },{dp:null,dist:Infinity}).dp : null;
          const navBtn = (lbl, disabled, onClick) => (
            <button onClick={onClick} disabled={disabled}
              style={{ width:28, height:28, display:"flex", alignItems:"center", justifyContent:"center",
                border:"0.5px solid #e2e5ea", borderRadius:4, background: disabled?"#f8fafc":"#fff",
                color: disabled?"#cbd5e1":"#374151", cursor: disabled?"default":"pointer", fontSize:14, fontWeight:600, flexShrink:0 }}>
              {lbl}
            </button>
          );
          return (
            <div style={{ width:350, flexShrink:0, borderLeft:"1px solid #e2e5ea", overflowY:"auto", background:"#fff", display:"flex", flexDirection:"column" }}>
              <div style={{ padding:"8px 12px", borderBottom:"0.5px solid #e2e5ea", display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0 }}>
                <span style={{ fontSize:10, fontWeight:700, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.07em" }}>Alert Inspector</span>
                {sortedAlerts.length>0 && <span style={{ fontSize:10, color:"#94a3b8" }}>{sortedAlerts.length} alert{sortedAlerts.length!==1?"s":""}</span>}
              </div>
              {!selAl ? (
                <div style={{ flex:1, display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center", padding:16, textAlign:"center", gap:8 }}>
                  <div style={{ fontSize:28, opacity:0.25 }}>&#128276;</div>
                  <div style={{ fontSize:12, color:"#94a3b8", lineHeight:1.5 }}>Click an alert marker<br/>or timeline tick to inspect</div>
                  {sortedAlerts.length===0 && <div style={{ fontSize:11, color:"#cbd5e1", marginTop:4 }}>No alerts in this range</div>}
                  {analysisData.length>0 && (
                    <div style={{ marginTop:16, width:"100%", padding:"0 4px" }}>
                      <div style={{ fontSize:9, color:"#cbd5e1", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:6 }}>Group Analysis</div>
                      <button onClick={runGroupDiagnosis} disabled={groupDiagLoading}
                        style={{ width:"100%", fontSize:11, padding:"6px 0", borderRadius:5,
                          border:`1px solid ${pinnedTimestamp?"rgba(0,109,140,0.8)":"rgba(0,109,140,0.5)"}`,
                          cursor:groupDiagLoading?"not-allowed":"pointer", fontWeight:600,
                          background:pinnedTimestamp?"rgba(0,109,140,0.15)":"rgba(0,109,140,0.08)", color:"#006D8C" }}>
                        {groupDiagLoading?"Analysing…":pinnedTimestamp?`⚡ Diagnose at ${new Date(pinnedTimestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:"⚡ Diagnose Group"}
                      </button>
                    </div>
                  )}
                </div>
              ) : (
                <div style={{ flex:1, overflowY:"auto", display:"flex", flexDirection:"column" }}>
                  <div style={{ display:"flex", alignItems:"center", gap:6, padding:"8px 12px", borderBottom:"0.5px solid #f1f5f9", flexShrink:0 }}>
                    {navBtn("←", selectedAlertIdx===0, ()=>setSelectedAlertIdx(i=>Math.max(0,i-1)))}
                    <span style={{ flex:1, textAlign:"center", fontSize:11, color:"#64748b", fontWeight:500 }}>
                      {selectedAlertIdx+1} / {sortedAlerts.length}
                    </span>
                    {navBtn("→", selectedAlertIdx===sortedAlerts.length-1, ()=>setSelectedAlertIdx(i=>Math.min(sortedAlerts.length-1,i+1)))}
                  </div>
                  <div style={{ margin:"10px 12px 0", background:sevBg, border:`1px solid ${sevCol}40`, borderRadius:6, padding:"9px 11px" }}>
                    <div style={{ fontSize:10, fontWeight:700, color:sevCol, textTransform:"uppercase", letterSpacing:"0.08em", marginBottom:4 }}>{selAl.Severity||"alert"}</div>
                    <div style={{ fontSize:12, fontWeight:600, color:"#1e293b", marginBottom:3, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{selAl.monitorName}</div>
                    <div style={{ fontSize:10, color:"#64748b" }}>{new Date(selAl.Timestamp).toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}</div>
                  </div>
                  {nearestDp?.Value!=null && (
                    <div style={{ margin:"8px 12px 0", background:"#f8fafc", border:"0.5px solid #e2e5ea", borderRadius:6, padding:"8px 11px" }}>
                      <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Value at alert</div>
                      <div style={{ fontSize:20, fontWeight:700, color:sevCol, lineHeight:1 }}>{typeof nearestDp.Value==="number"?nearestDp.Value.toFixed(1):nearestDp.Value}{monData?.Unit||""}</div>
                      {(monData?.WarnThreshold!=null||monData?.CritThreshold!=null) && (
                        <div style={{ fontSize:10, color:"#94a3b8", marginTop:4 }}>
                          {monData.WarnThreshold!=null&&<span>Warn: {monData.WarnThreshold}{monData.Unit} </span>}
                          {monData.CritThreshold!=null&&<span>Crit: {monData.CritThreshold}{monData.Unit}</span>}
                        </div>
                      )}
                    </div>
                  )}
                  {selAl.Message && (
                    <div style={{ margin:"8px 12px 0", fontSize:11, color:"#475569", lineHeight:1.5,
                      background:"#f8fafc", borderRadius:6, padding:"8px 11px", border:"0.5px solid #e2e5ea" }}>
                      {selAl.Message}
                    </div>
                  )}
                  <div style={{ margin:"8px 12px 0" }}>
                    <button onClick={()=>onNavigate("alerts")}
                      style={{ width:"100%", fontSize:11, padding:"5px 0", borderRadius:5,
                        border:"0.5px solid #e2e5ea", background:"#f8fafc", color:"#374151",
                        cursor:"pointer", fontWeight:500 }}>
                      View in Alerts ↗
                    </button>
                  </div>
                  <div style={{ margin:"10px 12px", padding:"10px 0 0", borderTop:"0.5px solid #e2e5ea" }}>
                    <div style={{ fontSize:9, color:"#cbd5e1", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:6 }}>Group Analysis</div>
                    <button onClick={runGroupDiagnosis} disabled={groupDiagLoading}
                      style={{ width:"100%", fontSize:11, padding:"6px 0", borderRadius:5,
                        border:`1px solid ${pinnedTimestamp?"rgba(0,109,140,0.8)":"rgba(0,109,140,0.5)"}`,
                        cursor:groupDiagLoading?"not-allowed":"pointer", fontWeight:600,
                        background:pinnedTimestamp?"rgba(0,109,140,0.15)":"rgba(0,109,140,0.08)", color:"#006D8C" }}>
                      {groupDiagLoading?"Analysing…":pinnedTimestamp?`⚡ Diagnose at ${new Date(pinnedTimestamp).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}`:"⚡ Diagnose Group"}
                    </button>
                  </div>
                </div>
              )}
            </div>
          );
        })()}
        {/* ── Column 3 — AI Group Diagnosis (only when groupDiagOpen, 420px) ── */}
        {groupDiagOpen && (
          <div style={{ width:420, flexShrink:0, borderLeft:"1px solid #e2e5ea", overflowY:"auto", background:"#fff", display:"flex", flexDirection:"column" }}>
            <div style={{ padding:"10px 12px", borderBottom:"1px solid #e2e5ea", display:"flex", alignItems:"flex-start", justifyContent:"space-between", flexShrink:0 }}>
              <div>
                <div style={{ fontSize:13, fontWeight:600, color:"#1e293b" }}>⚡ AI Group Diagnosis</div>
                <div style={{ fontSize:11, color:"#64748b", marginTop:2, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", maxWidth:320 }}>{analysisGroup}</div>
              </div>
              <button onClick={()=>{ setGroupDiagOpen(false); setGroupDiagResult(null); setGroupDiagError(null); }} style={{ background:"none", border:"none", color:"#94a3b8", cursor:"pointer", fontSize:18, padding:0, lineHeight:1, flexShrink:0 }}>✕</button>
            </div>
            <div style={{ flex:1, overflowY:"auto", padding:14, fontSize:13, lineHeight:1.6, color:"#1A1A1A" }}>
              {groupDiagLoading ? (
                <div style={{ display:"flex", alignItems:"center", gap:10, color:"#6B7280", padding:"24px 0" }}>
                  <Spinner /><span>Analysing {analysisGroup}…</span>
                </div>
              ) : groupDiagError ? (
                <div style={{ background:"#FAEAEA", border:"1px solid #D95C5C40", borderRadius:8, padding:"12px 14px", color:"#D95C5C", fontSize:12 }}>{groupDiagError}</div>
              ) : groupDiagResult ? (
                <div style={{ fontSize:12 }}>
                  {(()=>{
                    const segs=[];
                    const re=/```powershell\n([\s\S]*?)```/g;
                    let last=0,mm;
                    while((mm=re.exec(groupDiagResult))!==null){
                      if(mm.index>last) segs.push({type:'text',content:groupDiagResult.slice(last,mm.index)});
                      segs.push({type:'script',content:mm[1].trim(),id:`gps-${mm.index}`});
                      last=mm.index+mm[0].length;
                    }
                    if(last<groupDiagResult.length) segs.push({type:'text',content:groupDiagResult.slice(last)});
                    return segs.map((seg,si)=>seg.type==='text'?(
                      <div key={si}>{renderGroupDiagMd(seg.content)}</div>
                    ):(
                      <div key={si} style={{ position:"relative", background:"#1E2B3C", borderRadius:6, padding:"8px 10px", marginTop:4, marginBottom:4 }}>
                        <pre style={{ margin:0, fontSize:11, color:"#C9D1D9", fontFamily:"'IBM Plex Mono',monospace", whiteSpace:"pre-wrap", wordBreak:"break-word" }}>{seg.content}</pre>
                        <button onClick={()=>{ navigator.clipboard.writeText(seg.content); setGroupCopiedScriptId(seg.id); setTimeout(()=>setGroupCopiedScriptId(null),2000); }}
                          style={{ position:"absolute", top:6, right:6, background:groupCopiedScriptId===seg.id?"#008C6F":"#2D3F52", color:"#fff", border:"none", borderRadius:4, fontSize:10, padding:"2px 7px", cursor:"pointer" }}>
                          {groupCopiedScriptId===seg.id?"Copied!":"Copy"}
                        </button>
                      </div>
                    ));
                  })()}
                </div>
              ) : null}
              {groupInvThread.length > 1 && (
                <div style={{ marginTop:12, borderTop:"1px solid #C0D8E8", paddingTop:12 }}>
                  <div style={{ fontSize:10, fontWeight:700, textTransform:"uppercase", color:"#6B6B6B", letterSpacing:"0.5px", marginBottom:8 }}>Investigation Thread</div>
                  {groupInvThread.slice(1).map((msg)=>(
                    <div key={msg.id} style={{ marginBottom:10 }}>
                      <div style={{ display:"flex", alignItems:"center", gap:6, marginBottom:4 }}>
                        <span style={{ fontSize:10, fontWeight:700, color:msg.role==='user'?"#006D8C":"#4A4A4A", background:msg.role==='user'?"#E8F4F8":"#F5F5F5", borderRadius:4, padding:"1px 6px" }}>{msg.role==='user'?'You':'AI'}</span>
                        <span style={{ fontSize:10, color:"#9CA3AF" }}>{new Date(msg.timestamp).toLocaleTimeString()}</span>
                      </div>
                      {msg.role==='user'?(
                        <div style={{ fontSize:12, color:"#1A1A1A", paddingLeft:8, lineHeight:1.5 }}>{msg.content}</div>
                      ):(
                        <div style={{ fontSize:12, paddingLeft:8 }}>
                          {(()=>{
                            const segs=[];
                            const re=/```powershell\n([\s\S]*?)```/g;
                            let last=0,mm;
                            while((mm=re.exec(msg.content))!==null){
                              if(mm.index>last) segs.push({type:'text',content:msg.content.slice(last,mm.index)});
                              segs.push({type:'script',content:mm[1].trim(),id:`gth-${msg.id}-${mm.index}`});
                              last=mm.index+mm[0].length;
                            }
                            if(last<msg.content.length) segs.push({type:'text',content:msg.content.slice(last)});
                            return segs.map((seg,si)=>seg.type==='text'?(
                              <div key={si}>{renderGroupDiagMd(seg.content)}</div>
                            ):(
                              <div key={si} style={{ position:"relative", background:"#1E2B3C", borderRadius:6, padding:"8px 10px", marginTop:4, marginBottom:4 }}>
                                <pre style={{ margin:0, fontSize:11, color:"#C9D1D9", fontFamily:"'IBM Plex Mono',monospace", whiteSpace:"pre-wrap", wordBreak:"break-word" }}>{seg.content}</pre>
                                <button onClick={()=>{ navigator.clipboard.writeText(seg.content); setGroupCopiedScriptId(seg.id); setTimeout(()=>setGroupCopiedScriptId(null),2000); }}
                                  style={{ position:"absolute", top:6, right:6, background:groupCopiedScriptId===seg.id?"#008C6F":"#2D3F52", color:"#fff", border:"none", borderRadius:4, fontSize:10, padding:"2px 7px", cursor:"pointer" }}>
                                  {groupCopiedScriptId===seg.id?"Copied!":"Copy"}
                                </button>
                              </div>
                            ));
                          })()}
                        </div>
                      )}
                    </div>
                  ))}
                </div>
              )}
              {(groupDiagResult||groupInvThread.length>0) && (
                <div style={{ marginTop:12, borderTop:"1px solid #C0D8E8", paddingTop:12 }}>
                  <div style={{ display:"flex", gap:6 }}>
                    <textarea value={groupFollowUpText} onChange={e=>setGroupFollowUpText(e.target.value)}
                      onKeyDown={e=>{ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();handleGroupFollowUp();} }}
                      placeholder="Ask a follow-up question… (Enter to send, Shift+Enter for newline)"
                      rows={2} style={{ flex:1, fontSize:12, padding:"6px 8px", border:"1px solid #C0D8E8", borderRadius:6, resize:"none", fontFamily:"inherit", color:"#1A1A1A" }} />
                    <button onClick={handleGroupFollowUp} disabled={!groupFollowUpText.trim()||groupFollowUpLoading}
                      style={{ alignSelf:"flex-end", background:"#006D8C", color:"#fff", border:"none", borderRadius:6, padding:"6px 12px", fontSize:12, fontWeight:600, cursor:(!groupFollowUpText.trim()||groupFollowUpLoading)?"not-allowed":"pointer", opacity:(!groupFollowUpText.trim()||groupFollowUpLoading)?0.5:1 }}>
                      {groupFollowUpLoading?"…":"Send"}
                    </button>
                  </div>
                </div>
              )}
            </div>
            {(groupDiagResult||groupDiagError) && (
              <div style={{ padding:"8px 12px", borderTop:"1px solid #C0D8E8", fontSize:11, color:"#9CA3AF", flexShrink:0 }}>
                Powered by Claude · {groupDiagTs||"—"}
              </div>
            )}
          </div>
        )}
        </div>
      {confirmModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.45)', zIndex:3000, display:'flex', alignItems:'center', justifyContent:'center' }}>
          <div style={{ background:'#fff', borderRadius:8, padding:24, width:380, boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
            <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{confirmModal.msg}</div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
              <button onClick={()=>setConfirmModal(null)}
                style={{ fontSize:12, padding:'6px 16px', border:'0.5px solid #C0D8E8', borderRadius:6, background:'#fff', color:'#4A4A4A', cursor:'pointer' }}>Cancel</button>
              <button onClick={()=>{ confirmModal.onOk(); setConfirmModal(null); }}
                style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{confirmModal.okLabel || 'Confirm'}</button>
            </div>
          </div>
        </div>
      )}
      </div>
    );
  }

  if (subView === "form") {
    return (
      <MonitorFormPage
        key={editMonitor?.MonitorID || "new"}
        api={api}
        monitor={editMonitor}
        origin={editMonitorOrigin}
        onClose={() => { setSubView(editMonitorOrigin === "Correlation Analysis" ? "analysis" : "list"); setEditMonitor(null); setEditMonitorOrigin(null); load(); }}
        onSaved={onSaved}
        navInterceptRef={navInterceptRef}
        onNavigate={onNavigate}
        onCopy={m => openEdit(m)}
      />
    );
  }


  // ── Bulk Add computed values ──
  const bulkMonitorCount      = bulkRows.reduce((sum, row) => sum + (row.checks || []).filter(Boolean).length, 0);
  const bulkHasAnyIP          = bulkRows.some(r => r.ip && r.ip.trim().length > 0);
  const bulkServerCount       = bulkRows.filter(r => r.ip && r.ip.trim().length > 0).length;
  const bulkThresholdGroups   = buildThresholdGroups(bulkRows);

  // ── Bulk Add style constants ──
  const bkThFix    = { fontSize:11, fontWeight:600, textAlign:'left', whiteSpace:'nowrap', padding:'4px 8px' };
  const bkTdCtrl   = { padding:'3px 6px', verticalAlign:'middle', height:24 };
  const bkTdData   = { padding:'1px 4px', verticalAlign:'middle', height:24 };
  const bkInp      = { height:19, fontSize:11, border:'none', outline:'none', background:'transparent', fontFamily:'inherit', padding:'0 3px', borderRadius:2, boxSizing:'border-box' };
  const bkInp2     = { height:19, fontSize:11, fontFamily:'monospace', textAlign:'right', padding:'2px 5px', border:'0.5px solid #c8d0da', borderRadius:3, boxSizing:'border-box', width:70, background:'#fff', color:'#1A1A1A', outline:'none' };
  const bkInp2Ctrl = { ...bkInp2, background:'#FFF8F0', borderColor:'#C8B89A' };
  const bulkPassCount    = Object.values(bulkTestResults).filter(v => v.startsWith('pass:')).length;
  const bulkFailCount    = Object.values(bulkTestResults).filter(v => v.startsWith('fail:')).length;
  const bulkTestedCount  = Object.values(bulkTestResults).filter(v => v !== 'pending' && v !== 'testing').length;
  const bulkEnabledCount = bulkThresholdGroups.reduce((sum, g) => sum + g.servers.filter((_, si) => bulkEnabled[g.typeKey+'_'+si] !== false).length, 0);
  const bulkCanCreate    = (bulkSkipTest || bulkTestedCount > 0) && bulkThresholdGroups.some(g => g.servers.length > 0);
  const bulkTotalCount   = bulkThresholdGroups.reduce((sum, g) => sum + g.servers.length, 0);

  const buildBulkThresholdGroups = () => {
    const gs = buildThresholdGroups(bulkRows);
    setBulkThresholds(initThresholds(gs));
    setBulkAddStep(2);
  };
  const buildStep3Preview = () => {
    const gs = bulkThresholdGroups;
    const init={}; const enInit={}; const alInit={};
    gs.forEach(g=>{ g.servers.forEach((_,si)=>{ const k=g.typeKey+'_'+si; init[k]='pending'; enInit[k]=true; alInit[k]=true; }); });
    setBulkTestResults(init); setBulkEnabled(enInit); setBulkAlertsEnabled(alInit); setBulkStep3Collapsed({});
    setBulkAddStep(3);
  };

  // ── Bulk Add async test/create helpers ──
  const bulkRunTest = async (typeKey, serverIdx, server) => {
    const key = typeKey + '_' + serverIdx;
    setBulkTestResults(prev => ({ ...prev, [key]: 'testing' }));
    const th = bulkThresholds[typeKey];
    const thRow = (th && th[serverIdx]) || {};
    try {
      const r = await api('POST', '/monitors/test', {
        MonitorName: server.serverName || server.ip,
        MonitorSubType: BULK_SUBTYPE_MAP[typeKey] || '',
        IPAddressOrUrl: server.ip,
        ResponseTimeThresholdMs: parseInt(thRow.warn) || 2000,
        CriticalThresholdMs:     parseInt(thRow.crit) || 5000,
        CheckFrequencySeconds:   parseInt(thRow.freq) || 60,
        IsEnabled: true,
      });
      if (r && r.IsSuccess) {
        const val = r.ResponseTimeMs != null ? r.ResponseTimeMs+'ms' : (r.StatusCode ? 'HTTP '+r.StatusCode : 'OK');
        setBulkTestResults(prev => ({ ...prev, [key]: 'pass:'+val }));
      } else {
        setBulkTestResults(prev => ({ ...prev, [key]: 'fail:'+(r?.ErrorMessage || 'check failed') }));
      }
    } catch(e) {
      setBulkTestResults(prev => ({ ...prev, [key]: 'fail:'+(e.message || 'connection failed') }));
    }
  };
  const bulkRunGroup = async (g) => {
    for (let si = 0; si < g.servers.length; si++) {
      await bulkRunTest(g.typeKey, si, g.servers[si]);
      if (si < g.servers.length - 1) await new Promise(res => setTimeout(res, 300));
    }
  };
  const bulkRunAll = async () => {
    setBulkTestRunning(true);
    for (let gi = 0; gi < bulkThresholdGroups.length; gi++) {
      await bulkRunGroup(bulkThresholdGroups[gi]);
      if (gi < bulkThresholdGroups.length - 1) await new Promise(res => setTimeout(res, 200));
    }
    setBulkTestRunning(false);
  };
  const bulkCreate = async () => {
    let created = 0;
    const failures = [];
    const serverCount = bulkRows.filter(r => r.ip && r.ip.trim()).length;
    for (const g of bulkThresholdGroups) {
      for (let si = 0; si < g.servers.length; si++) {
        const key = g.typeKey + '_' + si;
        const s = g.servers[si];
        const th = bulkThresholds[g.typeKey];
        const thRow = (th && th[si]) || { warn:g.defaults.warn, crit:g.defaults.crit, freq:g.defaults.freq };
        try {
          await api('POST', '/monitors', {
            MonitorName: s.serverName || (s.ip + ' \u2014 ' + g.label),
            MonitorType: BULK_MONITOR_TYPE_MAP[g.cat] || 'Server',
            MonitorSubType: BULK_SUBTYPE_MAP[g.typeKey] || '',
            GroupName: bulkAddGroupName || bulkRows[s.rowIndex]?.group || 'Production',
            IPAddressOrUrl: s.ip,
            Port: 0,
            ResponseTimeThresholdMs: parseInt(thRow.warn) || 2000,
            CriticalThresholdMs:     parseInt(thRow.crit) || 5000,
            CheckFrequencySeconds:   parseInt(thRow.freq) || 60,
            IsEnabled: bulkEnabled[key] !== false,
            AlertsEnabled: bulkAlertsEnabled[key] !== false,
          });
          created++;
        } catch(err) {
          failures.push({ name: s.serverName || s.ip || ('row ' + (si+1)), error: err?.message || String(err) });
        }
      }
    }
    try { localStorage.removeItem('op1_bulk_draft_' + bulkAddGroupId); } catch(_) {}
    setShowBulkAdd(false); setBulkAddStep(1);
    setBulkRows([
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
    ]);
    setBulkThresholds({}); setBulkTestResults({}); setBulkEnabled({}); setBulkAlertsEnabled({});
    setBulkSkipTest(false); setBulkCreateDone(false);
    setSubView('list'); setManageGroupTarget(null); setGroupThresholdTarget(null); setGroupThresholdRows([]);
    load();
    if (failures.length > 0) {
      const summary = created + ' created, ' + failures.length + ' failed: ' +
        failures.slice(0, 5).map(f => f.name).join(', ') +
        (failures.length > 5 ? ' and ' + (failures.length - 5) + ' more' : '');
      setErr(summary);
    }
    const msg = created+' monitor'+(created!==1?'s':'')+' created in '+bulkAddGroupName;
    setBulkSuccessMsg(msg);
    setTimeout(() => setBulkSuccessMsg(''), 3000);
  };

  // ── Bulk Add step JSX consts (for manage-group subView) ──
  const bulkTableJsx = (
    <div style={{ overflowX:'auto' }}>
      <table style={{ borderCollapse:'collapse', width:'100%', fontSize:11, tableLayout:'auto', borderTop:'0.5px solid #e2e8f0' }}>
        <thead>
          <tr>
            <th style={{ ...bkThFix, background:'#1E2B3C', color:'#fff', minWidth:120, borderRight:'0.5px solid rgba(255,255,255,0.1)' }}>Server name</th>
            <th style={{ ...bkThFix, background:'#1E2B3C', color:'#fff', minWidth:110, borderRight:'0.5px solid rgba(255,255,255,0.1)' }}>IP address <span style={{ color:'#F87171', fontSize:9 }}>required</span></th>
            <th style={{ ...bkThFix, background:'#1E2B3C', color:'#fff', minWidth:120, borderRight:'2px solid #3A4F6A' }}>Profile</th>
            {BULK_CATS.map(cat => {
              const collapsed = bulkCatCollapsed[cat.id];
              const checkedCount = bulkRows.reduce((s,r) => s + cat.indices.filter(ci => (r.checks||[])[ci]).length, 0);
              return (
                <th key={cat.id}
                  colSpan={collapsed ? 1 : cat.cols.length}
                  onClick={()=>setBulkCatCollapsed(prev=>({...prev,[cat.id]:!prev[cat.id]}))}
                  style={{ ...bkThFix, background:cat.color, color:'#fff', textAlign:'center', cursor:'pointer', borderLeft:'2px solid '+cat.borderColor, userSelect:'none', fontSize:11 }}
                >
                  {collapsed ? `${cat.short} \u25b8${checkedCount > 0 ? ' '+checkedCount+'\u2713' : ''}` : `${cat.label} \u25be`}
                </th>
              );
            })}
          </tr>
          <tr style={{ background:'#F5F0E8' }}>
            <th style={{ ...bkThFix, fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Name</th>
            <th style={{ ...bkThFix, fontSize:9, color:'#6B6B6B', fontWeight:500 }}>IP</th>
            <th style={{ ...bkThFix, fontSize:9, color:'#6B6B6B', fontWeight:500, borderRight:'2px solid #C8B89A' }}>Profile</th>
            {BULK_CATS.map(cat => bulkCatCollapsed[cat.id] ? null : cat.cols.map((col, ii) => (
              <th key={cat.id+ii} style={{ ...bkThFix, fontSize:9, color:cat.color, fontWeight:500, textAlign:'center', padding:'3px 4px', borderLeft: ii===0 ? '2px solid '+cat.borderColor : '0.5px solid #e2e8f0' }}>{col}</th>
            )))}
          </tr>
          <tr style={{ background:'#F0EBE0', borderBottom:'2px solid #C8B89A' }}>
            <td style={{ ...bkTdCtrl }}><span style={{ fontSize:9, color:'#8A7A60', textTransform:'uppercase', letterSpacing:'0.06em' }}>apply to all</span></td>
            <td style={{ ...bkTdCtrl }}></td>
            <td style={{ ...bkTdCtrl, borderRight:'2px solid #C8B89A' }}>
              <select value={bulkCtrlProfile} onChange={e => {
                const pk=e.target.value; setBulkCtrlProfile(pk);
                const nc=[...(BULK_PROFILES[pk]||BULK_PROFILES['']).checks];
                setBulkCtrlChecks(nc);
                setBulkRows(prev=>prev.map(r=>({...r,profile:pk,checks:[...nc]})));
              }} style={{ ...bkInp, width:'100%', border:'0.5px solid #C8B89A', background:'#fff', borderRadius:3 }}>
                {Object.entries(BULK_PROFILES).map(([k,v])=><option key={k} value={k}>{v.label}</option>)}
              </select>
            </td>
            {BULK_CATS.map(cat => bulkCatCollapsed[cat.id] ? null : cat.indices.map((ci, ii) => (
              <td key={cat.id+ii} style={{ ...bkTdCtrl, textAlign:'center', borderLeft: ii===0 ? '2px solid '+cat.borderColor : '0.5px solid #e2e8f0' }}>
                <label style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:1, cursor:'pointer' }}>
                  <input type="checkbox" checked={!!bulkCtrlChecks[ci]} onChange={e => {
                    const v=e.target.checked?1:0;
                    setBulkCtrlChecks(prev=>{ const n=[...prev]; n[ci]=v; return n; });
                    setBulkRows(prev=>prev.map(r=>{ const nc=[...(r.checks||Array(BULK_TOTAL_COLS).fill(0))]; nc[ci]=v; return {...r,checks:nc}; }));
                  }} style={{ accentColor:'#006D8C', width:12, height:12, cursor:'pointer', margin:0 }} />
                  <span style={{ fontSize:8, color:'#8A7A60' }}>all</span>
                </label>
              </td>
            )))}
          </tr>
        </thead>
        <tbody>
          {bulkRows.map((row, ri) => {
            const rowChecks = row.checks || Array(BULK_TOTAL_COLS).fill(0);
            const rowBg = ri % 2 === 0 ? '#FFFFFF' : 'rgba(245,240,232,0.4)';
            return (
              <tr key={ri} style={{ background:rowBg, height:24 }}>
                <td style={{ ...bkTdData, borderRight:'0.5px solid #e2e8f0' }}>
                  <input value={row.name} placeholder="auto-generated if blank"
                    onChange={e=>setBulkRows(prev=>{const n=[...prev];n[ri]={...n[ri],name:e.target.value};return n;})}
                    onPaste={e=>handleBulkPaste(e,ri,'name')}
                    style={{ ...bkInp, width:'100%', color:'#374151' }} />
                </td>
                <td style={{ ...bkTdData, borderRight:'0.5px solid #e2e8f0' }}>
                  <input value={row.ip} placeholder="required"
                    onChange={e=>setBulkRows(prev=>{const n=[...prev];n[ri]={...n[ri],ip:e.target.value};return n;})}
                    onPaste={e=>handleBulkPaste(e,ri,'ip')}
                    style={{ ...bkInp, width:'100%', color: row.ip ? '#374151' : '#B05050' }} />
                </td>
                <td style={{ ...bkTdData, borderRight:'2px solid #C8B89A' }}>
                  <select value={row.profile} onChange={e=>{
                    const pk=e.target.value;
                    const nc=[...(BULK_PROFILES[pk]||BULK_PROFILES['']).checks];
                    setBulkRows(prev=>{const n=[...prev];n[ri]={...n[ri],profile:pk,checks:nc};return n;});
                  }} style={{ ...bkInp, width:'100%' }}>
                    {Object.entries(BULK_PROFILES).map(([k,v])=><option key={k} value={k}>{v.label}</option>)}
                  </select>
                </td>
                {BULK_CATS.map(cat => bulkCatCollapsed[cat.id] ? null : cat.indices.map((ci, ii) => (
                  <td key={cat.id+ii} style={{ ...bkTdData, textAlign:'center', borderLeft: ii===0 ? '2px solid '+cat.borderColor : '0.5px solid #e2e8f0' }}>
                    <input type="checkbox" checked={!!rowChecks[ci]} onChange={e=>{
                      const v=e.target.checked?1:0;
                      setBulkRows(prev=>{const n=[...prev];const nc=[...(n[ri].checks||Array(BULK_TOTAL_COLS).fill(0))];nc[ci]=v;n[ri]={...n[ri],checks:nc};return n;});
                    }} style={{ accentColor:'#006D8C', width:12, height:12, cursor:'pointer', margin:0 }} />
                  </td>
                )))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
  const step2Jsx = (
    <div style={{ padding:'12px 16px', display:'flex', flexDirection:'column', gap:10 }}>
      {bulkThresholdGroups.map(g => {
        const isCollapsed = !!bulkStep2Collapsed[g.typeKey];
        const th = bulkThresholds[g.typeKey] || {};
        const ctrl = th.ctrl || { warn:g.defaults.warn, crit:g.defaults.crit, freq:g.defaults.freq, extra:g.defaults.extra };
        return (
          <div key={g.typeKey} style={{ border:'0.5px solid #e2e5ea', borderRadius:6, overflow:'hidden', borderLeft:'3px solid '+g.catBorder }}>
            <div onClick={()=>setBulkStep2Collapsed(prev=>({...prev,[g.typeKey]:!prev[g.typeKey]}))}
              style={{ padding:'6px 12px', background:'#f8fafc', cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'space-between', userSelect:'none' }}>
              <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                <span style={{ width:8, height:8, borderRadius:'50%', background:g.catColor, display:'inline-block', flexShrink:0 }} />
                <span style={{ fontSize:12, fontWeight:600, color:'#1E2B3C' }}>{g.label}</span>
                <span style={{ fontSize:11, color:'#6B6B6B' }}>{g.servers.length} server{g.servers.length!==1?'s':''}</span>
                <span style={{ fontSize:10, padding:'1px 7px', borderRadius:10, background:g.catBadgeBg, color:g.catBadgeColor, fontWeight:500 }}>{g.catLabel}</span>
              </div>
              <span style={{ fontSize:12, color:'#6B6B6B' }}>{isCollapsed?'\u25b8':'\u25be'}</span>
            </div>
            {!isCollapsed && (
            <table style={{ width:'100%', borderCollapse:'collapse', fontSize:11 }}>
              <thead>
                <tr style={{ background:'#F0EBE0', borderBottom:'2px solid #C8B89A' }}>
                  <td style={{ padding:'3px 10px', fontSize:9, color:'#8A7A60', textTransform:'uppercase', letterSpacing:'0.06em', whiteSpace:'nowrap' }}>Apply to all</td>
                  <td style={{ padding:'3px 6px' }}><input value={ctrl.warn} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>{const g2={...prev[g.typeKey]};g2.ctrl={...g2.ctrl,warn:v};g.servers.forEach((_,si)=>{g2[si]={...(g2[si]||{}),warn:v};});return{...prev,[g.typeKey]:g2};});}} style={{...bkInp2Ctrl}} /></td>
                  <td style={{ padding:'3px 6px' }}><input value={ctrl.crit} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>{const g2={...prev[g.typeKey]};g2.ctrl={...g2.ctrl,crit:v};g.servers.forEach((_,si)=>{g2[si]={...(g2[si]||{}),crit:v};});return{...prev,[g.typeKey]:g2};});}} style={{...bkInp2Ctrl}} /></td>
                  <td style={{ padding:'3px 6px' }}><input value={ctrl.freq} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>{const g2={...prev[g.typeKey]};g2.ctrl={...g2.ctrl,freq:v};g.servers.forEach((_,si)=>{g2[si]={...(g2[si]||{}),freq:v};});return{...prev,[g.typeKey]:g2};});}} style={{...bkInp2Ctrl}} /></td>
                  {g.extra && (<td style={{ padding:'3px 6px' }}><input value={ctrl.extra} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>{const g2={...prev[g.typeKey]};g2.ctrl={...g2.ctrl,extra:v};g.servers.forEach((_,si)=>{g2[si]={...(g2[si]||{}),extra:v};});return{...prev,[g.typeKey]:g2};});}} style={{...bkInp2Ctrl}} /></td>)}
                </tr>
                <tr style={{ background:'#f0f4f8' }}>
                  <th style={{ padding:'3px 10px', textAlign:'left', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Server</th>
                  <th style={{ padding:'3px 6px', textAlign:'right', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Warn{g.unit?' ('+g.unit+')':''}</th>
                  <th style={{ padding:'3px 6px', textAlign:'right', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Critical{g.unit?' ('+g.unit+')':''}</th>
                  <th style={{ padding:'3px 6px', textAlign:'right', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Frequency (sec)</th>
                  {g.extra && <th style={{ padding:'3px 6px', textAlign:'right', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>{g.extra.label}</th>}
                </tr>
              </thead>
              <tbody>
                {g.servers.map((s, si) => {
                  const row = th[si] || { warn:g.defaults.warn, crit:g.defaults.crit, freq:g.defaults.freq, extra:g.defaults.extra };
                  return (
                    <tr key={si} style={{ background:si%2===0?'#fff':'#fafafa' }}>
                      <td style={{ padding:'2px 10px', color:'#4A4A4A', fontSize:11, whiteSpace:'nowrap' }}>{s.serverName||s.ip||'Row '+(s.rowIndex+1)}</td>
                      <td style={{ padding:'1px 6px' }}><input value={row.warn} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>({...prev,[g.typeKey]:{...(prev[g.typeKey]||{}),[si]:{...(prev[g.typeKey]?.[si]||{}),warn:v}}}));}} style={{...bkInp2}} /></td>
                      <td style={{ padding:'1px 6px' }}><input value={row.crit} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>({...prev,[g.typeKey]:{...(prev[g.typeKey]||{}),[si]:{...(prev[g.typeKey]?.[si]||{}),crit:v}}}));}} style={{...bkInp2}} /></td>
                      <td style={{ padding:'1px 6px' }}><input value={row.freq} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>({...prev,[g.typeKey]:{...(prev[g.typeKey]||{}),[si]:{...(prev[g.typeKey]?.[si]||{}),freq:v}}}));}} style={{...bkInp2}} /></td>
                      {g.extra && (<td style={{ padding:'1px 6px' }}><input value={row.extra} onChange={e=>{const v=e.target.value;setBulkThresholds(prev=>({...prev,[g.typeKey]:{...(prev[g.typeKey]||{}),[si]:{...(prev[g.typeKey]?.[si]||{}),extra:v}}}));}} style={{...bkInp2}} /></td>)}
                    </tr>
                  );
                })}
              </tbody>
            </table>
            )}
          </div>
        );
      })}
    </div>
  );
  const step3Jsx = (
    <div style={{ padding:'12px 16px', display:'flex', flexDirection:'column', gap:12 }}>
      <div style={{ display:'flex', gap:10 }}>
        {[
          { label:'Monitors to create', value:bulkTotalCount,   color:'#1E2B3C' },
          { label:'Servers',            value:bulkServerCount,  color:'#1E2B3C' },
          { label:'Tests passed',       value:bulkPassCount,    color:'#0F6E56' },
          { label:'Tests failed',       value:bulkFailCount,    color: bulkFailCount>0?'#A32D2D':'#1E2B3C' },
        ].map((card, i) => (
          <div key={i} style={{ flex:1, background:'#f8fafc', borderRadius:6, padding:'8px 14px', textAlign:'center', border:'0.5px solid #e2e5ea' }}>
            <div style={{ fontSize:20, fontWeight:500, color:card.color }}>{card.value}</div>
            <div style={{ fontSize:10, color:'#6B6B6B', marginTop:2 }}>{card.label}</div>
          </div>
        ))}
      </div>
      <div style={{ background:'#f8fafc', border:'0.5px solid #e2e5ea', borderRadius:6, padding:'8px 12px', display:'flex', alignItems:'center', gap:10 }}>
        <button disabled={bulkTestRunning} onClick={()=>bulkRunAll()}
          style={{ height:28, padding:'0 14px', fontSize:12, background:'#1E2B3C', color:'#F5F0E8', border:'none', borderRadius:6, cursor:bulkTestRunning?'not-allowed':'pointer', fontWeight:600, flexShrink:0, opacity:bulkTestRunning?0.7:1 }}>
          {bulkTestRunning ? 'Testing...' : (Object.values(bulkTestResults).some(v=>v!=='pending') ? '▶ Re-test all' : '▶ Test all monitors')}
        </button>
        <span style={{ flex:1, fontSize:11, color:'#6B6B6B' }}>
          {bulkTestRunning ? 'Running tests...' : Object.values(bulkTestResults).some(v=>v!=='pending') ? bulkPassCount+' passed \u00b7 '+bulkFailCount+' failed' : 'Run tests to validate connectivity before creating'}
        </span>
        <button onClick={()=>setBulkSkipTest(true)} style={{ fontSize:10, color:'#6B6B6B', background:'none', border:'none', cursor:'pointer', textDecoration:'underline', padding:0, flexShrink:0 }}>Skip testing</button>
      </div>
      {bulkThresholdGroups.map(g => {
        const isCollapsed3 = !!bulkStep3Collapsed[g.typeKey];
        const groupEnabledAll = g.servers.every((_,si) => bulkEnabled[g.typeKey+'_'+si] !== false);
        const groupAlertsAll  = g.servers.every((_,si) => bulkAlertsEnabled[g.typeKey+'_'+si] !== false);
        return (
          <div key={g.typeKey} style={{ border:'0.5px solid #e2e5ea', borderRadius:6, overflow:'hidden', borderLeft:'3px solid '+g.catBorder }}>
            <div style={{ padding:'6px 12px', background:'#f8fafc', display:'flex', alignItems:'center', justifyContent:'space-between', userSelect:'none' }}>
              <div onClick={()=>setBulkStep3Collapsed(prev=>({...prev,[g.typeKey]:!prev[g.typeKey]}))} style={{ display:'flex', alignItems:'center', gap:8, flex:1, cursor:'pointer' }}>
                <span style={{ width:8, height:8, borderRadius:'50%', background:g.catColor, display:'inline-block', flexShrink:0 }} />
                <span style={{ fontSize:12, fontWeight:600, color:'#1E2B3C' }}>{g.label}</span>
                <span style={{ fontSize:11, color:'#6B6B6B' }}>{g.servers.length} server{g.servers.length!==1?'s':''}</span>
              </div>
              <div style={{ display:'flex', alignItems:'center', gap:8, flexShrink:0 }}>
                <button disabled={bulkTestRunning} onClick={()=>bulkRunGroup(g)} style={{ height:22, padding:'0 10px', fontSize:10, background:'#1E2B3C', color:'#F5F0E8', border:'none', borderRadius:4, cursor:bulkTestRunning?'not-allowed':'pointer', opacity:bulkTestRunning?0.6:1, fontWeight:600 }}>▶ Test</button>
                <span style={{ fontSize:10, color:'#6B6B6B' }}>Enable all</span>
                <div style={{ position:'relative', width:30, height:16, flexShrink:0, cursor:'pointer' }} onClick={()=>{const n=!groupEnabledAll;setBulkEnabled(prev=>{const r={...prev};g.servers.forEach((_,si)=>{r[g.typeKey+'_'+si]=n;});return r;});}}>
                  <div style={{ position:'absolute', inset:0, borderRadius:8, background:groupEnabledAll?'#006D8C':'#cbd5e0' }} />
                  <div style={{ position:'absolute', top:2, left:groupEnabledAll?16:2, width:12, height:12, borderRadius:'50%', background:'#fff', transition:'left 0.15s' }} />
                </div>
                <span style={{ fontSize:10, color:'#6B6B6B' }}>Alerts</span>
                <div style={{ position:'relative', width:30, height:16, flexShrink:0, cursor:'pointer' }} onClick={()=>{const n=!groupAlertsAll;setBulkAlertsEnabled(prev=>{const r={...prev};g.servers.forEach((_,si)=>{r[g.typeKey+'_'+si]=n;});return r;});}}>
                  <div style={{ position:'absolute', inset:0, borderRadius:8, background:groupAlertsAll?'#E89A2E':'#cbd5e0' }} />
                  <div style={{ position:'absolute', top:2, left:groupAlertsAll?16:2, width:12, height:12, borderRadius:'50%', background:'#fff', transition:'left 0.15s' }} />
                </div>
              </div>
            </div>
            {!isCollapsed3 && (
            <table style={{ width:'100%', borderCollapse:'collapse', fontSize:11 }}>
              <thead>
                <tr style={{ background:'#f0f4f8' }}>
                  <th style={{ padding:'3px 10px', textAlign:'left', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Server</th>
                  <th style={{ padding:'3px 6px', textAlign:'left', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>IP</th>
                  <th style={{ padding:'3px 6px', textAlign:'left', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Thresholds</th>
                  <th style={{ padding:'3px 6px', textAlign:'left', fontSize:9, color:'#6B6B6B', fontWeight:500 }}>Test result</th>
                  <th style={{ padding:'3px 6px', textAlign:'center', fontSize:9, color:'#6B6B6B', fontWeight:500, width:60 }}>Enabled</th>
                  <th style={{ padding:'3px 6px', textAlign:'center', fontSize:9, color:'#6B6B6B', fontWeight:500, width:60 }}>Alerts</th>
                </tr>
              </thead>
              <tbody>
                {g.servers.map((s, si) => {
                  const key3 = g.typeKey+'_'+si;
                  const th3 = bulkThresholds[g.typeKey];
                  const thRow3 = (th3&&th3[si])||{warn:g.defaults.warn,crit:g.defaults.crit,freq:g.defaults.freq};
                  const result3 = bulkTestResults[key3] || 'pending';
                  const isEnabled3 = bulkEnabled[key3] !== false;
                  const isAlerts3  = bulkAlertsEnabled[key3] !== false;
                  return (
                    <tr key={si} style={{ background:si%2===0?'#fff':'#fafafa', opacity:isEnabled3?1:0.45 }}>
                      <td style={{ padding:'3px 10px', fontWeight:500, whiteSpace:'nowrap', fontSize:11 }}>{s.serverName||s.ip||'Row '+(s.rowIndex+1)}</td>
                      <td style={{ padding:'3px 6px', fontFamily:'monospace', fontSize:10, color:'#6B6B6B' }}>{s.ip}</td>
                      <td style={{ padding:'3px 6px' }}>
                        <div style={{ display:'flex', gap:3, flexWrap:'wrap' }}>
                          {thRow3.warn!==''&&<span style={{ fontSize:9, padding:'1px 5px', borderRadius:8, background:'#FFF3DC', color:'#854F0B', border:'0.5px solid #EFCF80' }}>W:{thRow3.warn}{g.unit}</span>}
                          {thRow3.crit!==''&&<span style={{ fontSize:9, padding:'1px 5px', borderRadius:8, background:'#FCEAEA', color:'#A32D2D', border:'0.5px solid #F0A0A0' }}>C:{thRow3.crit}{g.unit}</span>}
                          <span style={{ fontSize:9, padding:'1px 5px', borderRadius:8, background:'#E0F2F7', color:'#005A75', border:'0.5px solid #90CCE0' }}>{thRow3.freq}s</span>
                        </div>
                      </td>
                      <td style={{ padding:'3px 6px' }}>
                        {result3==='pending'&&<span style={{ fontSize:11, color:'#9CA3AF' }}>not tested</span>}
                        {result3==='testing'&&<span style={{ fontSize:11, color:'#E89A2E' }}>testing...</span>}
                        {result3.startsWith('pass:')&&<span style={{ fontSize:11, fontWeight:600, color:'#0F6E56' }}>Pass {result3.slice(5)}</span>}
                        {result3.startsWith('fail:')&&<span style={{ fontSize:11, fontWeight:600, color:'#A32D2D' }}>Fail {result3.slice(5)}</span>}
                      </td>
                      <td style={{ padding:'3px 6px', textAlign:'center' }}>
                        <div style={{ display:'flex', justifyContent:'center' }}>
                          <div style={{ position:'relative', width:30, height:16, cursor:'pointer' }} onClick={()=>setBulkEnabled(prev=>({...prev,[key3]:!isEnabled3}))}>
                            <div style={{ position:'absolute', inset:0, borderRadius:8, background:isEnabled3?'#006D8C':'#cbd5e0' }} />
                            <div style={{ position:'absolute', top:2, left:isEnabled3?16:2, width:12, height:12, borderRadius:'50%', background:'#fff', transition:'left 0.15s' }} />
                          </div>
                        </div>
                      </td>
                      <td style={{ padding:'3px 6px', textAlign:'center' }}>
                        <div style={{ display:'flex', justifyContent:'center' }}>
                          <div style={{ position:'relative', width:30, height:16, cursor:'pointer' }} onClick={()=>setBulkAlertsEnabled(prev=>({...prev,[key3]:!isAlerts3}))}>
                            <div style={{ position:'absolute', inset:0, borderRadius:8, background:isAlerts3?'#E89A2E':'#cbd5e0' }} />
                            <div style={{ position:'absolute', top:2, left:isAlerts3?16:2, width:12, height:12, borderRadius:'50%', background:'#fff', transition:'left 0.15s' }} />
                          </div>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
            )}
          </div>
        );
      })}
    </div>
  );

  const handleGroupThresholdEdit = (groupId, groupName) => {
    const groupMonitors = monitors.filter(m =>
      m.GroupID === groupId || m.GroupName === groupName
    );
    if (groupMonitors.length === 0) {
      showConfirm('No monitors found in this group.', () => {});
      return;
    }
    const rows = groupMonitors.map(m => ({
      monitorId:      m.MonitorID,
      monitorName:    m.MonitorName || '',
      monitorSubType: m.MonitorSubType || '',
      targetHost:     m.TargetHost || m.IPAddress || '',
      warn:           String(m.FailureThreshold ?? ''),
      crit:           String(m.ResponseTimeThresholdMs ?? ''),
      freq:           String(m.CheckFrequencySeconds ?? 60),
      enabled:        m.IsEnabled !== false,
    }));
    setGroupThresholdTarget({ groupId, groupName, monitorCount: groupMonitors.length });
    setGroupThresholdRows(rows);
    setGroupThresholdErr('');
    setGroupThresholdOk('');
  };

  const resetBulkAdd = () => {
    setBulkRows([
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
      { name:'', ip:'', group:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) },
    ]);
    setBulkCtrlGroup('');
    setBulkCtrlProfile('');
    setBulkCtrlChecks(Array(BULK_TOTAL_COLS).fill(0));
    setBulkAddStep(1);
    setBulkThresholds({});
    setBulkTestResults({});
    setBulkTestRunning(false);
    setBulkEnabled({});
    setBulkAlertsEnabled({});
    setBulkStep3Collapsed({});
    setBulkDraftName('');
    setBulkDraftSaved(null);
  };

  const handleManageGroup = (groupId, groupName, tab = 'thresholds') => {
    setManageGroupTarget({ groupId, groupName, tab });
    setSubView('manage-group');
    if (tab === 'thresholds') {
      const groupMonitors = monitors.filter(m => m.GroupID === groupId || m.GroupName === groupName);
      if (groupMonitors.length > 0) handleGroupThresholdEdit(groupId, groupName);
    }
    if (tab === 'bulk-add') {
      setBulkAddGroupId(groupId);
      setBulkAddGroupName(groupName);
      resetBulkAdd();
      try {
        const d = localStorage.getItem('op1_bulk_draft_' + groupId);
        const saved = d ? JSON.parse(d) : null;
        if (saved) {
          setBulkRows(saved.rows || [{ name:'', ip:'', profile:'', checks: Array(BULK_TOTAL_COLS).fill(0) }]);
          setBulkDraftName(saved.name || '');
          setBulkDraftSaved(saved.savedAt ? new Date(saved.savedAt) : null);
          setBulkAddStep(saved.step || 1);
        }
      } catch(_) {}
    }
  };

  if (subView === 'manage-group' && manageGroupTarget) {
    const { groupId, groupName } = manageGroupTarget;
    const activeTab = manageGroupTarget.tab || 'thresholds';
    const monitorCount = monitors.filter(m => m.GroupID === groupId || m.GroupName === groupName).length;
    const setTab = (tab) => {
      setManageGroupTarget(prev => ({ ...prev, tab }));
      if (tab === 'thresholds') handleGroupThresholdEdit(groupId, groupName);
      if (tab === 'bulk-add') { setBulkAddGroupId(groupId); setBulkAddGroupName(groupName); }
    };
    const backToList = () => { setSubView('list'); setManageGroupTarget(null); setGroupThresholdTarget(null); setGroupThresholdRows([]); };
    const getCat = (subType) => {
      if (!subType) return 'other';
      if (subType.startsWith('infra-')) return 'infra';
      if (subType.startsWith('web-'))   return 'web';
      if (subType.startsWith('db-'))    return 'db';
      if (subType.startsWith('app-'))   return 'app';
      if (subType.startsWith('agt-'))   return 'agent';
      return 'other';
    };
    const catMeta = {
      infra: { label:'Infrastructure', color:'#0369a1' },
      web:   { label:'Website',        color:'#166534' },
      db:    { label:'Database',       color:'#854d0e' },
      app:   { label:'Application',    color:'#6b21a8' },
      agent: { label:'Agent',          color:'#475569' },
      other: { label:'Other',          color:'#4A4A4A' },
    };
    const catOrder = ['infra','web','db','app','agent','other'];
    const grouped = {};
    catOrder.forEach(k => { grouped[k] = []; });
    groupThresholdRows.forEach((row, idx) => { grouped[getCat(row.monitorSubType)].push({ ...row, idx }); });
    const updateRow = (idx, field, value) => setGroupThresholdRows(prev => prev.map((r, i) => i === idx ? { ...r, [field]: value } : r));
    const applyAll = (cat, field, value) => { if (!value) return; setGroupThresholdRows(prev => prev.map(r => getCat(r.monitorSubType) === cat ? { ...r, [field]: value } : r)); };
    const colGrid = '18px 1fr 110px 140px 74px 74px 74px 40px';
    const hCell = { fontSize:10, fontWeight:600, color:'#000', textTransform:'uppercase', letterSpacing:'0.04em' };
    const inputStyle = { fontSize:11, border:'1px solid #C0D8E8', borderRadius:4, padding:'2px 6px', width:'100%', color:'#1A1A1A', background:'#fff', fontFamily:'inherit' };
    const disabledInput = { ...inputStyle, background:'#F3F4F6', color:'#1A1A1A' };
    const saveDraftBulk = () => { try { localStorage.setItem('op1_bulk_draft_' + groupId, JSON.stringify({ rows: bulkRows, name: bulkDraftName, step: bulkAddStep, savedAt: new Date().toISOString() })); setBulkDraftSaved(new Date()); } catch(_) {} };
    return (
      <div style={{ display:'flex', flexDirection:'column', height:'100%' }}>
        {/* Topbar */}
        <div style={{ background:'#fff', borderBottom:'1px solid #e5e0d8', padding:'8px 16px', display:'flex', alignItems:'center', gap:8, flexShrink:0 }}>
          <button onClick={backToList} style={{ fontSize:12, color:'#006D8C', background:'none', border:'none', cursor:'pointer', padding:0, fontWeight:500 }}>← Monitors</button>
          <span style={{ color:'#94a3b8', fontSize:12 }}>/</span>
          <span style={{ color:'#1A1A1A', fontWeight:600, fontSize:13 }}>{groupName}</span>
          <span style={{ color:'#94a3b8', fontSize:12 }}>/</span>
          <span style={{ color:'#374151', fontSize:12 }}>Manage</span>
          <span style={{ background:'#e5e0d8', color:'#374151', fontSize:10, padding:'2px 8px', borderRadius:10, marginLeft:2 }}>{monitorCount} monitors</span>
          <div style={{ marginLeft:'auto', display:'flex', alignItems:'center', gap:8 }}>
            {canManage && (
              <button
                onClick={() => { backToList(); setTimeout(() => openAddForGroup(groupId, groupName), 50); }}
                style={{ fontSize:11, padding:'4px 12px', border:'1px solid #006D8C', borderRadius:6, background:'#fff', color:'#006D8C', cursor:'pointer', fontWeight:500 }}
              >+ Add Monitor</button>
            )}
            {canManage && (
              <button
                onClick={() => { backToList(); }}
                style={{ fontSize:11, padding:'4px 12px', border:'1px solid #e5e0d8', borderRadius:6, background:'#f8f5f0', color:'#374151', cursor:'pointer', fontWeight:500 }}
              >← Monitor List</button>
            )}
          </div>
        </div>
        {/* Tab bar */}
        <div style={{ background:'#fff', borderBottom:'1px solid #e5e0d8', display:'flex', gap:0, padding:'0 16px', flexShrink:0 }}>
          <button onClick={()=>setTab('thresholds')}
            style={{ padding:'10px 18px', fontSize:12, fontWeight:500, cursor:'pointer', border:'none', borderBottom: activeTab==='thresholds' ? '2px solid #006D8C' : '2px solid transparent', color: activeTab==='thresholds' ? '#006D8C' : '#6b7280', background:'none' }}>
            Bulk Edit
          </button>
          <button onClick={()=>setTab('bulk-add')}
            style={{ padding:'10px 18px', fontSize:12, fontWeight:500, cursor:'pointer', border:'none', borderBottom: activeTab==='bulk-add' ? '2px solid #006D8C' : '2px solid transparent', color: activeTab==='bulk-add' ? '#006D8C' : '#6b7280', background:'none' }}>
            Bulk Add
          </button>
        </div>
        {/* Tab content */}
        <div style={{ flex:1, overflowY:'auto' }}>
          {activeTab === 'thresholds' && groupThresholdTarget && groupThresholdRows.length > 0 && (
            <div style={{ padding:'12px 16px' }}>
              <div style={{ background:'#FDF3E0', borderLeft:'3px solid #E89A2E', borderRadius:'0 4px 4px 0', padding:'6px 12px', fontSize:11, color:'#854F0B', marginBottom:14 }}>
                Tip: use the amber control row to apply a value to all monitors of that type at once. Leave blank to keep each monitor's current value.
              </div>
              {catOrder.filter(cat => grouped[cat].length > 0).map(cat => {
                const meta = catMeta[cat];
                const catRows = grouped[cat];
                return (
                  <div key={cat} style={{ marginBottom:16 }}>
                    <div style={{ display:'flex', alignItems:'center', gap:8, padding:'5px 10px', background:'#E8DDD0', border:'0.5px solid #C0B090', borderRadius:'5px 5px 0 0', borderBottom:'none' }}>
                      <div style={{ width:8, height:8, borderRadius:2, background:meta.color, flexShrink:0 }} />
                      <span style={{ fontSize:11, fontWeight:600, color:'#000' }}>{meta.label}</span>
                      <span style={{ fontSize:10, color:'#555' }}>{catRows.length} monitor{catRows.length !== 1 ? 's' : ''}</span>
                      <button
                        onClick={() => {
                          showConfirm(
                            `Delete all ${catRows.length} ${meta.label} monitors in this group? This cannot be undone.`,
                            async () => {
                              const ids = catRows.map(r => r.monitorId);
                              let failed = 0;
                              for (const id of ids) {
                                try { await api('DELETE', `/monitors/${id}`); }
                                catch(_) { failed++; }
                              }
                              if (failed > 0) setErr(`${failed} monitor${failed!==1?'s':''} could not be deleted.`);
                              setGroupThresholdRows([]);
                              await load();
                              handleGroupThresholdEdit(groupId, groupName);
                            }
                          );
                        }}
                        style={{ marginLeft:'auto', fontSize:10, fontWeight:600, color:'#fff', background:'#D95C5C', border:'none', borderRadius:4, padding:'2px 10px', cursor:'pointer', flexShrink:0 }}
                      >Delete all</button>
                    </div>
                    <div style={{ display:'grid', gridTemplateColumns:colGrid, background:'#EBE5DC', border:'0.5px solid #C0D8E8', padding:'4px 8px', alignItems:'center', gap:4 }}>
                      <span /><span style={hCell}>Monitor</span><span style={hCell}>Type</span><span style={hCell}>Target</span><span style={hCell}>Warn</span><span style={hCell}>Crit</span><span style={hCell}>Freq (s)</span><span style={hCell}>On</span>
                    </div>
                    <div style={{ display:'grid', gridTemplateColumns:colGrid, background:'#F5EFE6', border:'0.5px solid #D4C4A8', borderTop:'none', padding:'5px 8px', alignItems:'center', gap:4 }}>
                      <span /><span style={{ fontSize:10, fontWeight:600, color:'#000' }}>Apply to all</span><span /><span />
                      <input style={{ ...inputStyle, fontSize:10 }} placeholder="warn" onChange={e=>{ if(e.target.value) applyAll(cat,'warn',e.target.value); }} />
                      <input style={{ ...inputStyle, fontSize:10 }} placeholder="crit" onChange={e=>{ if(e.target.value) applyAll(cat,'crit',e.target.value); }} />
                      <input style={{ ...inputStyle, fontSize:10 }} placeholder="freq" onChange={e=>{ if(e.target.value) applyAll(cat,'freq',e.target.value); }} />
                      <span />
                    </div>
                    {catRows.map((row, ri) => {
                      const isLast = ri === catRows.length - 1;
                      const mon = monitors.find(m => m.MonitorID === row.monitorId);
                      const dotColor = !row.enabled ? '#D1D5DB' : mon?.EffectiveStatus === 'critical' ? '#D95C5C' : mon?.EffectiveStatus === 'warning' ? '#E89A2E' : '#008C6F';
                      return (
                        <div key={row.monitorId} style={{ display:'grid', gridTemplateColumns:colGrid, gap:4, border:'0.5px solid #C0D8E8', borderTop:'none', padding:'4px 8px', alignItems:'center', background: ri%2===0 ? '#fff' : '#F0F7FB', borderRadius: isLast ? '0 0 5px 5px' : 0 }}>
                          <div style={{ width:7, height:7, borderRadius:'50%', background:dotColor, flexShrink:0 }} />
                          <span style={{ fontSize:12, color:'#1A1A1A', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{row.monitorName}</span>
                          <span style={{ fontSize:11, color:'#1A1A1A', whiteSpace:'nowrap' }}>{row.monitorSubType}</span>
                          <span style={{ fontSize:11, color:'#1A1A1A', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{row.targetHost}</span>
                          <input style={row.enabled ? inputStyle : disabledInput} value={row.warn} disabled={!row.enabled} onChange={e=>updateRow(row.idx,'warn',e.target.value)} />
                          <input style={row.enabled ? inputStyle : disabledInput} value={row.crit} disabled={!row.enabled} onChange={e=>updateRow(row.idx,'crit',e.target.value)} />
                          <input style={row.enabled ? inputStyle : disabledInput} value={row.freq} disabled={!row.enabled} onChange={e=>updateRow(row.idx,'freq',e.target.value)} />
                          <div onClick={()=>updateRow(row.idx,'enabled',!row.enabled)} style={{ width:28, height:15, borderRadius:8, background:row.enabled?'#006D8C':'#D1D5DB', position:'relative', cursor:'pointer', flexShrink:0 }}>
                            <div style={{ position:'absolute', width:11, height:11, borderRadius:'50%', background:'#fff', top:2, left:row.enabled?15:2, transition:'left 0.15s' }} />
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })}
              <div style={{ display:'flex', justifyContent:'flex-end', gap:8, paddingTop:10, borderTop:'0.5px solid #C0D8E8' }}>
                {groupThresholdErr && <span style={{ fontSize:12, color:'#D95C5C', alignSelf:'center' }}>{groupThresholdErr}</span>}
                {groupThresholdOk && <span style={{ fontSize:12, color:'#008C6F', alignSelf:'center' }}>{groupThresholdOk}</span>}
                <button onClick={backToList} style={{ fontSize:12, color:'#4A4A4A', border:'0.5px solid #C0D8E8', borderRadius:6, padding:'6px 14px', background:'#fff', cursor:'pointer' }}>Cancel</button>
                <button onClick={saveGroupThresholds} disabled={groupThresholdSaving} style={{ fontSize:12, fontWeight:600, color:'#fff', background:groupThresholdSaving?'#7AAEBB':'#006D8C', border:'none', borderRadius:6, padding:'6px 18px', cursor:groupThresholdSaving?'default':'pointer' }}>
                  {groupThresholdSaving ? 'Saving\u2026' : 'Save changes'}
                </button>
              </div>
            </div>
          )}
          {activeTab === 'thresholds' && (!groupThresholdTarget || groupThresholdRows.length === 0) && (
            <div style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'48px 24px', textAlign:'center' }}>
              <div style={{ fontSize:32, marginBottom:16 }}>📋</div>
              <div style={{ fontSize:15, fontWeight:600, color:'#1A1A1A', marginBottom:8 }}>No monitors in this group yet</div>
              <div style={{ fontSize:12, color:'#6B7280', marginBottom:24, maxWidth:360, lineHeight:1.6 }}>
                Use <strong>Bulk Add</strong> to add multiple monitors at once, or <strong>+ Add Monitor</strong> to add them individually.
              </div>
              <div style={{ display:'flex', gap:10 }}>
                <button
                  onClick={() => setTab('bulk-add')}
                  style={{ fontSize:12, fontWeight:600, padding:'8px 20px', background:'#006D8C', color:'#fff', border:'none', borderRadius:6, cursor:'pointer' }}
                >→ Go to Bulk Add</button>
                <button
                  onClick={() => { backToList(); setTimeout(() => openAddForGroup(groupId, groupName), 50); }}
                  style={{ fontSize:12, fontWeight:500, padding:'8px 20px', background:'#fff', color:'#006D8C', border:'1px solid #006D8C', borderRadius:6, cursor:'pointer' }}
                >+ Add Monitor</button>
              </div>
            </div>
          )}
          {activeTab === 'bulk-add' && (() => {
            const stepBar = (
              <div style={{ display:'flex', alignItems:'center', gap:0, marginBottom:14 }}>
                {[['1','Server matrix'],['2','Thresholds'],['3','Confirm & create']].map(([n,label], si) => {
                  const done = bulkAddStep > si+1;
                  const active = bulkAddStep === si+1;
                  return (
                    <React.Fragment key={n}>
                      {si > 0 && <span style={{ color:'#d0ccc4', margin:'0 8px', fontSize:14 }}>›</span>}
                      <div style={{ display:'flex', alignItems:'center', gap:6, fontSize:12, color: done?'#008C6F': active?'#006D8C':'#6b7280', fontWeight: active?600:400 }}>
                        <div style={{ width:22, height:22, borderRadius:'50%', display:'flex', alignItems:'center', justifyContent:'center', fontSize:11, fontWeight:700, border:done?'1.5px solid #008C6F':active?'1.5px solid #006D8C':'1.5px solid #d0ccc4', background: done?'#008C6F': active?'#006D8C':'transparent', color: (done||active)?'#fff':'#d0ccc4', flexShrink:0 }}>
                          {done ? '\u2713' : n}
                        </div>
                        <span>{label}</span>
                      </div>
                    </React.Fragment>
                  );
                })}
                <div style={{ marginLeft:'auto', display:'flex', alignItems:'center', gap:8 }}>
                  <input value={bulkDraftName} onChange={e=>setBulkDraftName(e.target.value)} placeholder="Name this draft to save\u2026"
                    style={{ fontSize:11, border:'0.5px solid #F59E0B', borderRadius:4, padding:'4px 8px', width:190, color:'#1A1A1A', background:'#fff', fontFamily:'inherit' }} />
                  <button onClick={()=>{ try { localStorage.setItem('op1_bulk_draft_'+bulkAddGroupId, JSON.stringify({ rows:bulkRows, name:bulkDraftName, step:bulkAddStep, savedAt:new Date().toISOString() })); setBulkDraftSaved(new Date()); } catch(_){} }}
                    style={{ fontSize:11, padding:'4px 12px', background:'#F59E0B', color:'#fff', border:'none', borderRadius:5, cursor:'pointer', fontWeight:600 }}>Save draft</button>
                  {bulkDraftSaved && <span style={{ fontSize:10, color:'#6b7280', whiteSpace:'nowrap' }}>Saved {bulkDraftSaved.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}</span>}
                </div>
              </div>
            );
            if (bulkAddStep === 1) {
              return (
                <div style={{ padding:'16px' }}>
                  {stepBar}
                  <div style={{ background:'#FDF3E0', borderLeft:'3px solid #E89A2E', borderRadius:'0 4px 4px 0', padding:'6px 12px', fontSize:11, color:'#854F0B', marginBottom:12 }}>
                    Tip: Paste a newline-separated list of IPs or names into any IP or Name cell and they fill down automatically.
                  </div>
                  <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:10, fontSize:11, color:'#4b5563' }}>
                    Apply profile to all rows:
                    <select value={bulkCtrlProfile} onChange={e => {
                      const pk = e.target.value;
                      setBulkCtrlProfile(pk);
                      const nc = [...(BULK_PROFILES[pk] || BULK_PROFILES['']).checks];
                      setBulkCtrlChecks(nc);
                      setBulkRows(prev => prev.map(r => ({ ...r, profile:pk, checks:[...nc] })));
                    }} style={{ fontSize:11, border:'0.5px solid #e2e5ea', borderRadius:4, padding:'0 6px', background:'#fff', outline:'none', fontFamily:'inherit', height:24 }}>
                      {Object.entries(BULK_PROFILES).map(([k,v]) => <option key={k} value={k}>{v.label}</option>)}
                    </select>
                  </div>
                  {bulkTableJsx}
                  <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginTop:8 }}>
                    <button onClick={()=>setBulkRows(prev=>[...prev,{name:'',ip:'',group:bulkAddGroupName,profile:'',checks:Array(BULK_TOTAL_COLS).fill(0)}])}
                      style={{ fontSize:11, padding:'4px 10px', border:'0.5px solid #d0ccc4', borderRadius:5, background:'#fff', color:'#4b5563', cursor:'pointer' }}>+ Add row</button>
                    <span style={{ fontSize:10, color:'#6b7280' }}>{bulkMonitorCount} monitors will be created</span>
                  </div>
                  <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:14, paddingTop:12, borderTop:'0.5px solid #e5e0d8' }}>
                    <button onClick={backToList} style={{ fontSize:12, color:'#4A4A4A', border:'0.5px solid #d0ccc4', borderRadius:5, padding:'5px 14px', background:'#fff', cursor:'pointer' }}>← Back to Monitors</button>
                    <button onClick={()=>{ if(!bulkHasAnyIP){showConfirm('Please enter at least one IP address.',()=>{}); return;} buildBulkThresholdGroups(); }}
                      style={{ fontSize:12, padding:'5px 18px', background: bulkHasAnyIP?'#006D8C':'#9CA3AF', color:'#fff', border:'none', borderRadius:5, cursor:bulkHasAnyIP?'pointer':'default', fontWeight:600 }}>Next: Thresholds →</button>
                  </div>
                </div>
              );
            }
            if (bulkAddStep === 2) {
              return (
                <div style={{ padding:'16px' }}>
                  {stepBar}
                  {step2Jsx}
                  <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:14, paddingTop:12, borderTop:'0.5px solid #e5e0d8' }}>
                    <button onClick={()=>setBulkAddStep(1)} style={{ fontSize:12, color:'#4A4A4A', border:'0.5px solid #d0ccc4', borderRadius:5, padding:'5px 14px', background:'#fff', cursor:'pointer' }}>← Back</button>
                    <button onClick={()=>buildStep3Preview()} style={{ fontSize:12, padding:'5px 18px', background:'#006D8C', color:'#fff', border:'none', borderRadius:5, cursor:'pointer', fontWeight:600 }}>Review & create →</button>
                  </div>
                </div>
              );
            }
            return (
              <div style={{ padding:'16px' }}>
                {stepBar}
                {step3Jsx}
                <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginTop:14, paddingTop:12, borderTop:'0.5px solid #e5e0d8' }}>
                  <button onClick={()=>setBulkAddStep(2)} style={{ fontSize:12, color:'#4A4A4A', border:'0.5px solid #d0ccc4', borderRadius:5, padding:'5px 14px', background:'#fff', cursor:'pointer' }}>← Back</button>
                  <button onClick={bulkCreate} disabled={bulkTestedCount===0&&!bulkSkipTest}
                    style={{ fontSize:12, padding:'5px 18px', background:bulkTestedCount===0&&!bulkSkipTest?'#9CA3AF':'#008C6F', color:'#fff', border:'none', borderRadius:5, cursor:bulkTestedCount===0&&!bulkSkipTest?'default':'pointer', fontWeight:600 }}>
                    Create {bulkTotalCount} monitor{bulkTotalCount !== 1 ? 's' : ''}
                  </button>
                </div>
              </div>
            );
          })()}
        </div>
        {confirmModal && (
          <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.45)', zIndex:3000, display:'flex', alignItems:'center', justifyContent:'center' }}>
            <div style={{ background:'#fff', borderRadius:8, padding:24, width:380, boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
              <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{confirmModal.msg}</div>
              <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
                <button onClick={()=>setConfirmModal(null)}
                  style={{ fontSize:12, padding:'6px 16px', border:'0.5px solid #C0D8E8', borderRadius:6, background:'#fff', color:'#4A4A4A', cursor:'pointer' }}>Cancel</button>
                <button onClick={()=>{ confirmModal.onOk(); setConfirmModal(null); }}
                  style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{confirmModal.okLabel || 'Confirm'}</button>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }


  // Monitoring control from parent
  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  // Stats for header + stat bar
  const mTotal    = monitors.length;
  const mHealthy  = monitors.filter(m=>m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus)==="healthy").length;
  const mWarning  = monitors.filter(m=>m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus)==="warning").length;
  const mCritical = monitors.filter(m=>m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus)==="critical").length;
  const mDisabled = monitors.filter(m=>m.IsEnabled===false).length;
  const mRespArr  = monitors.filter(m=>m.LastResponseTimeMs!=null).map(m=>m.LastResponseTimeMs);
  const mAvgResp  = mRespArr.length > 0 ? Math.round(mRespArr.reduce((a,b)=>a+b,0)/mRespArr.length) : null;
  const mUptArr   = monitors.filter(m=>m.Uptime30Day!=null).map(m=>Number(m.Uptime30Day));
  const mAvgUp    = mUptArr.length > 0 ? (mUptArr.reduce((a,b)=>a+b,0)/mUptArr.length).toFixed(1) : null;

  const handleMassEdit = async (set) => {
    try {
      const data = [];
      const monitors = Array.isArray(data) ? data : [];
      if (monitors.length === 0) { showConfirm('No monitors found in this set.', () => {}); return; }
      const thresholds = {};
      const enabled = {};
      monitors.forEach(m => {
        const id = m.MonitorId ?? m.monitorId;
        thresholds[id] = {
          warn: String(m.WarnThreshold ?? ''),
          crit: String(m.CritThreshold ?? ''),
          freq: String(m.CheckFrequencySeconds ?? 60),
        };
        enabled[id] = m.IsEnabled !== false;
      });
      setMassEditSet(set);
      setMassEditMonitors(monitors);
      setMassEditThresholds(thresholds);
      setMassEditEnabled(enabled);
      setBulkWizardMode('mass-edit');
      setBulkAddStep(2);
      setShowBulkAdd(true);
    } catch(err) {
      showConfirm('Failed to load set members: ' + (err?.message || 'error'), () => {});
    }
  };

  const massEditApply = async () => {
    const setName = massEditSet?._isGroupEdit
      ? (massEditSet?.setName || 'group')
      : (massEditSet?.setName || 'set');
    let updated = 0;
    const failures = [];
    for (const m of massEditMonitors) {
      const mid = m.MonitorId ?? m.monitorId;
      if (massEditEnabled[mid] === false) continue;
      const th = massEditThresholds[mid] || {};
      const warn = th.warn !== '' ? parseInt(th.warn) || null : null;
      const crit = th.crit !== '' ? parseInt(th.crit) || null : null;
      const freq = th.freq !== '' && parseInt(th.freq) >= 10 ? parseInt(th.freq) : null;
      try {
        await api('PATCH', '/monitors/' + mid + '/thresholds', {
          ResponseTimeThresholdMs: warn,
          CriticalThresholdMs:     crit,
          CheckFrequencySeconds:   freq,
        });
        updated++;
      } catch(err) {
        failures.push({ name: m.MonitorName || m.monitorName || ('monitor ' + mid), error: err?.message || String(err) });
      }
    }
    setShowBulkAdd(false);
    setBulkWizardMode('create');
    setBulkAddStep(1);
    setMassEditSet(null);
    setMassEditMonitors([]);
    setMassEditThresholds({});
    setMassEditEnabled({});
    load();
    if (failures.length > 0) {
      const summary = updated + ' updated, ' + failures.length + ' failed: ' +
        failures.slice(0, 5).map(f => f.name).join(', ') +
        (failures.length > 5 ? ' and ' + (failures.length - 5) + ' more' : '');
      setErr(summary);
    }
    const msg = '\u2713 ' + updated + ' monitor' + (updated===1?'':'s') + ' updated in ' + setName;
    setBulkSuccessMsg(msg);
    setTimeout(() => setBulkSuccessMsg(''), 5000);
  };




  // ── Manage Groups inline view JSX ──
  const manageGroupsView = (() => {
    const topGroups = groups.filter(g => !g.ParentGroupID);
    const childGroups = groups.filter(g => !!g.ParentGroupID);
    const btnSm = { height:24, fontSize:11, padding:'0 10px', borderRadius:4, cursor:'pointer', fontFamily:'inherit' };
    const monitorCountForGroup = (gid) => monitors.filter(m => m.GroupID === gid).length;

    const groupRow = (g, depth) => {
      const children = childGroups.filter(c => c.ParentGroupID === g.GroupID);
      const mCount = monitorCountForGroup(g.GroupID);
      return (
        <div key={g.GroupID}>
          <div style={{ display:'flex', alignItems:'center', gap:8, padding:'6px 12px', paddingLeft: 12 + depth * 20, borderBottom:`0.5px solid #e2e5ea`, background: depth > 0 ? '#FAFAF8' : '#fff' }}>
            <span style={{ flex:1, fontSize:12, fontWeight: depth===0 ? 500 : 400, color:'#1A1A1A' }}>{g.GroupName}</span>
            {g.ParentGroupID && (() => {
              const parent = groups.find(p => p.GroupID === g.ParentGroupID);
              return parent ? <span style={{ fontSize:10, color:'#6B6B6B', background:'#F0EDE8', borderRadius:3, padding:'1px 6px' }}>sub of {parent.GroupName}</span> : null;
            })()}
            <span style={{ fontSize:10, color:'#6B6B6B', minWidth:60, textAlign:'right' }}>{mCount} monitor{mCount!==1?'s':''}</span>
            {canManage && (
              <button onClick={() => openEditGroupForm(g)}
                style={{ ...btnSm, border:'0.5px solid #e2e5ea', background:'#fff', color:'#475569' }}>Edit</button>
            )}
            {isAdmin && (
              <button onClick={() => showConfirm(`Delete group "${g.GroupName}"? Monitors in this group will become ungrouped.`, async () => {
                try {
                  await api("DELETE", `/monitors/groups/${g.GroupID}`);
                  const updated = await api("GET", "/monitors/groups");
                  if (Array.isArray(updated)) setGroups(updated);
                  load();
                } catch(e) { setErr("Delete failed: " + e.message); }
              })} style={{ ...btnSm, border:'0.5px solid #D95C5C', background:'#fff', color:'#D95C5C' }}>Delete</button>
            )}
          </div>
          {children.map(child => groupRow(child, depth + 1))}
        </div>
      );
    };

    return (
      <div style={{ padding:'8px 16px' }}>
        {/* Header */}
        <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', marginBottom:14, gap:12 }}>
          <div>
            <div style={{ fontSize:13, fontWeight:500, color:'#1A1A1A' }}>Manage groups</div>
            <div style={{ fontSize:11, color:'#6B6B6B', marginTop:2 }}>Create, rename, and organise monitor groups. Sub-groups appear indented under their parent.</div>
          </div>
          {canManage && (
            <button onClick={openAddGroupForm}
              style={{ ...btnSm, height:28, border:'1px solid #006D8C', background:'#fff', color:'#006D8C', fontWeight:600, whiteSpace:'nowrap', flexShrink:0 }}>+ Add Group</button>
          )}
        </div>

        {/* Group list */}
        <div style={{ border:`0.5px solid #e2e5ea`, borderRadius:6, overflow:'hidden', background:'#fff' }}>
          {groups.length === 0 ? (
            <div style={{ padding:'24px 16px', textAlign:'center', fontSize:12, color:'#6B6B6B' }}>No groups yet. Add a group to organise your monitors.</div>
          ) : (
            topGroups.map(g => groupRow(g, 0))
          )}
        </div>

        {/* Add / Edit modal */}
        {groupFormOpen && (
          <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.35)', zIndex:2000, display:'flex', alignItems:'center', justifyContent:'center' }}
            onClick={e => { if (e.target === e.currentTarget) setGroupFormOpen(false); }}>
            <div style={{ background:'#fff', borderRadius:8, padding:24, width:380, boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
              <div style={{ fontSize:14, fontWeight:600, marginBottom:16 }}>{groupFormMode==='add' ? 'Add group' : 'Edit group'}</div>
              <div style={{ marginBottom:12 }}>
                <label style={{ fontSize:11, color:'#6B6B6B', display:'block', marginBottom:4 }}>Group name *</label>
                <input value={groupFormName} onChange={e => setGroupFormName(e.target.value)}
                  placeholder="e.g. Production Servers"
                  style={{ width:'100%', height:32, fontSize:12, border:'0.5px solid #e2e5ea', borderRadius:4, padding:'0 8px', outline:'none', fontFamily:'inherit', boxSizing:'border-box' }} />
              </div>
              <div style={{ marginBottom:16 }}>
                <label style={{ fontSize:11, color:'#6B6B6B', display:'block', marginBottom:4 }}>Parent group (optional)</label>
                <select value={groupFormParent} onChange={e => setGroupFormParent(e.target.value)}
                  style={{ width:'100%', height:32, fontSize:12, border:'0.5px solid #e2e5ea', borderRadius:4, padding:'0 8px', outline:'none', fontFamily:'inherit', background:'#fff', boxSizing:'border-box' }}>
                  <option value="">None (top-level)</option>
                  {groups.filter(g => g.GroupID !== groupFormId && !g.ParentGroupID).map(g => (
                    <option key={g.GroupID} value={String(g.GroupID)}>{g.GroupName}</option>
                  ))}
                </select>
              </div>
              {groupFormErr && <div style={{ fontSize:11, color:'#D95C5C', marginBottom:10 }}>{groupFormErr}</div>}
              <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
                <button onClick={() => setGroupFormOpen(false)}
                  style={{ ...btnSm, height:30, border:'0.5px solid #e2e5ea', background:'#fff', color:'#475569' }}>Cancel</button>
                <button onClick={saveGroupForm} disabled={groupFormSaving}
                  style={{ ...btnSm, height:30, border:'1px solid #006D8C', background:groupFormSaving?'#e2e5ea':'#006D8C', color:'#fff', fontWeight:600, opacity:groupFormSaving?0.7:1 }}>
                  {groupFormSaving ? 'Saving…' : groupFormMode==='add' ? 'Add group' : 'Save changes'}
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  })();

  return (
    <div style={{ animation:"fadeIn 0.3s ease", display:"flex", flexDirection:"column", height:"calc(100vh - 20px)", overflow:"hidden", background:"#ffffff" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0 }}>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <div style={{ width:36, height:36, borderRadius:8, background:"#006D8C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:700, color:"#ffffff", letterSpacing:"0.02em", flexShrink:0 }}>OP1</div>
          <div>
            <div style={{ fontSize:16, fontWeight:600, color:"#1e293b" }}>
              <span style={{ color:"#006D8C" }}>OP1</span> Operations Portal
            </div>
            <div style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.4.457</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          <div style={{ position:"relative" }}>
            <button onClick={()=>{ if(canManageConfig && setPausePopover) setPausePopover(p=>!p); }}
              style={{ display:"flex", alignItems:"center", gap:6, background: monIsPaused ? "rgba(180,83,9,0.08)" : "rgba(0,0,0,0.04)", border: `1px solid ${monIsPaused?"rgba(180,83,9,0.3)":"rgba(0,0,0,0.1)"}`, borderRadius:20, padding:"5px 12px", cursor:"pointer", color: monIsPaused ? "#B45309" : "#374151", fontSize:11, fontWeight:600 }}>
              <span style={{ width:7, height:7, borderRadius:"50%", background: monIsPaused ? "#F59E0B" : "#10B981", flexShrink:0, animation: monIsPaused ? "pulse 1.5s infinite" : "none" }} />
              {monIsPaused ? "Monitoring Paused" : "Monitoring Active"}
            </button>
            {pausePopover && canManageConfig && (
              <div style={{ position:"absolute", top:"calc(100% + 8px)", right:0, width:280, background:"#FFFFFF", border:`1px solid ${c.border}`, borderRadius:8, boxShadow:"0 8px 24px rgba(0,0,0,0.15)", zIndex:200, padding:16 }}>
                <div style={{ fontSize:13, fontWeight:700, color:c.text, marginBottom:8 }}>{monIsPaused ? "Resume Monitoring" : "Pause Monitoring"}</div>
                <div style={{ fontSize:11, color:c.textDim, marginBottom:10 }}>Machine: <span style={{ fontFamily:"monospace", color:c.text }}>{currentMachine}</span></div>
                {!monIsPaused && <textarea value={pauseReason||""} onChange={e=>setPauseReason&&setPauseReason(e.target.value)} placeholder="Reason (optional)" style={{ width:"100%", fontSize:11, padding:6, borderRadius:4, border:"1px solid #e2e5ea", resize:"none", height:48, marginBottom:8 }} />}
                {monIsPaused && currentCtrl?.PauseReason && <div style={{ fontSize:11, color:c.textDim, marginBottom:8 }}>Reason: {currentCtrl.PauseReason}</div>}
                {monIsPaused && currentCtrl?.PausedAt && <div style={{ fontSize:10, color:c.textDimmer, marginBottom:10 }}>Paused at: {parseUTC(currentCtrl.PausedAt)?.toLocaleString()}</div>}
                <div style={{ display:"flex", gap:6 }}>
                  <button onClick={togglePause} disabled={pauseBusy} style={{ flex:1, padding:"6px 0", borderRadius:6, border:"none", cursor:"pointer", background: monIsPaused ? "#16a34a" : "#dc2626", color:"#fff", fontSize:12, fontWeight:600 }}>
                    {pauseBusy ? "..." : monIsPaused ? "Resume" : "Pause"}
                  </button>
                  <button onClick={()=>{ if(setPausePopover)setPausePopover(false); if(setPauseReason)setPauseReason(""); }} style={{ padding:"6px 10px", borderRadius:6, border:"1px solid #e2e5ea", cursor:"pointer", background:"#fff", fontSize:12 }}>Cancel</button>
                </div>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* ── Stat bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#ffffff", borderBottom:"0.5px solid #e2e5ea", height:28, flexShrink:0 }}>
        {[
          ["TOTAL",          mTotal,    "#1e293b",  () => setStatusFilter("all"),      "View all monitors"],
          ["HEALTHY",        mHealthy,  "#16a34a",  () => setStatusFilter("healthy"),  "View healthy monitors"],
          ["WARNING",        mWarning,  mWarning  > 0 ? "#d97706" : "#1e293b", () => setStatusFilter("warning"),  "View warning monitors"],
          ["CRITICAL",       mCritical, mCritical > 0 ? "#dc2626" : "#1e293b", () => setStatusFilter("critical"), "View critical monitors"],
          ["DISABLED",       mDisabled, "#1e293b",  () => setStatusFilter("disabled"), "View disabled monitors"],
          ["AVG RESPONSE",   mAvgResp  != null ? `${mAvgResp}ms`  : "—", "#1e293b", null, null],
          ["AVG UPTIME 30D", mAvgUp    != null ? `${mAvgUp}%`     : "—", "#1e293b", null, null],
        ].map(([lbl, val, col, onClick, title]) => (
          <div key={lbl} style={{ padding:"4px 10px", borderRight:"0.5px solid #f1f5f9", display:"flex", alignItems:"center", gap:6, flexShrink:0 }}>
            <span style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</span>
            <span
              title={title||undefined}
              onClick={onClick||undefined}
              onMouseEnter={onClick ? ()=>setHovStat(lbl) : undefined}
              onMouseLeave={onClick ? ()=>setHovStat(null) : undefined}
              style={{ fontSize:13, fontWeight:600, color:col, cursor:onClick?"pointer":"default", textDecoration:onClick&&hovStat===lbl?"underline":"none" }}
            >{val}</span>
          </div>
        ))}
      </div>

      {/* ── Toolbar ── */}
      <div style={{ display:"flex", alignItems:"center", gap:8, padding:"6px 16px", background:"#ffffff", borderBottom:"0.5px solid #e2e8f0", flexShrink:0 }}>
        <div style={{ position:"relative", display:"inline-flex", alignItems:"center" }}>
          <span style={{ position:"absolute", left:7, fontSize:10, color:"#9CA3AF", pointerEvents:"none", lineHeight:1 }}>&#x1F50D;</span>
          <input placeholder="Search monitors…" value={search} onChange={e=>setSearch(e.target.value)}
            onFocus={()=>setFiltersOpen(true)} onBlur={()=>setTimeout(()=>setFiltersOpen(false),200)}
            style={{ height:24, width:170, paddingLeft:24, paddingRight:6, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, outline:"none", color:"#374151", background: filtersOpen?"#fff":"#f8fafc", fontFamily:"inherit" }} />
        </div>
        {groupFilter !== "all" && (
          <div style={{ display:"flex", alignItems:"center", gap:4, background:"#f1f5f9", border:"1px solid #e2e8f0", borderRadius:12, padding:"2px 8px", flexShrink:0 }}>
            <span style={{ fontSize:12, color:"#475569", whiteSpace:"nowrap" }}>Group: {groupFilter === "ungrouped" ? "Ungrouped" : (groups.find(g => String(g.GroupID) === groupFilter)?.GroupName || groupFilter)}</span>
            <span onClick={() => setGroupFilter("all")} style={{ fontSize:12, color:"#94a3b8", cursor:"pointer", lineHeight:1, marginLeft:2 }}>×</span>
          </div>
        )}
        <div style={{ display:"flex", gap:4, alignItems:"center", overflow:"hidden", maxWidth: filtersOpen ? 600 : 0, opacity: filtersOpen ? 1 : 0, transition:"max-width 0.25s ease, opacity 0.2s ease", whiteSpace:"nowrap" }}>
          <select value={typeFilter} onChange={e=>setTypeFilter(e.target.value)}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value="all">All Types</option>
            <option>Server</option><option>Application</option><option>Database</option><option>Website</option><option>Synthetic</option>
          </select>
          <select value={statusFilter} onChange={e=>setStatusFilter(e.target.value)}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value="all">All Statuses</option>
            <option>healthy</option><option>warning</option><option>critical</option><option value="disabled">disabled</option><option>suppressed</option>
          </select>
          <select value={groupFilter} onChange={e=>setGroupFilter(e.target.value)}
            style={{ height:24, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#374151", padding:"0 6px", outline:"none", cursor:"pointer" }}>
            <option value="all">All Groups</option>
            <option value="ungrouped">Ungrouped</option>
            {groups.map(g=><option key={g.GroupID} value={String(g.GroupID)}>{g.GroupName}</option>)}
          </select>
        </div>
        <div style={{ flex:1 }} />
        <button
          onClick={()=>{ const anyExpanded=expandedGroups.size>0; if(anyExpanded){setExpandedGroups(new Set());}else{const allIds=new Set(namedGroups.map(g=>g.id));if(ungrouped.length>0)allIds.add("ungrouped");setExpandedGroups(allIds);} }}
          style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer", whiteSpace:"nowrap" }}
        >{expandedGroups.size > 0 ? "Collapse All" : "Expand All"}</button>
        <button onClick={load}
          style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer" }}
        >Refresh</button>
        {canManage && (
          <button onClick={()=>{ setShowManageGroups(prev=>!prev); }}
            style={{ fontSize:11, padding:"3px 10px", border:showManageGroups?"1px solid #1E2B3C":"0.5px solid #e2e5ea", borderRadius:5, background:showManageGroups?"#1E2B3C":"#fff", color:showManageGroups?"#fff":"#6B6B6B", cursor:"pointer", whiteSpace:"nowrap", fontWeight:showManageGroups?600:400 }}
          >Manage Groups</button>
        )}
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"8px 0" }}>
        {showManageGroups ? manageGroupsView : (<React.Fragment>
        <ErrBox msg={err} />
      {loading ? <Spinner /> : (()=>{
        const hCell = { fontSize:10, textTransform:"uppercase", color:"#4A4A4A", fontWeight:500, letterSpacing:"0.04em", padding:"5px 12px", overflow:"hidden" };
        const subHeaderRow = (
          <div style={{ display:"grid", gridTemplateColumns:MONITOR_COL_GRID, background:"#FAF8F4", borderBottom:"1.5px solid #D8CCBA" }}>
            <div style={{ ...hCell, textAlign:"right" }}><SortTh col="type" label="Type" /></div>
            <div style={{ ...hCell, textAlign:"center", padding:"5px 3px" }}><SortTh col="health" label="Health" /></div>
            <div style={{ ...hCell, textAlign:"left" }}><SortTh col="name" label="Name" /></div>
            <div style={{ ...hCell, textAlign:"left" }}><span style={{ display:"inline-flex", alignItems:"center", gap:2 }}><SortTh col="source" label="Source Monitor" /><HelpIcon hkey="source-monitor" setHelpKey={setHelpKey} theme="light" /></span></div>
            <div style={{ ...hCell, textAlign:"left" }}><SortTh col="target" label="Target" /></div>
            <div style={{ ...hCell, textAlign:"center" }}><SortTh col="freq" label="Freq" /></div>
            <div style={{ ...hCell, textAlign:"center" }}><SortTh col="response" label="Last Value" /></div>
            <div style={{ ...hCell, textAlign:"center" }}><SortTh col="uptime" label="Uptime 30d" /></div>
            <div style={{ ...hCell, textAlign:"center" }}>Run</div>
            <div style={{ ...hCell, textAlign:"center" }}>Alerts</div>
            <div style={{ ...hCell, textAlign:"center" }}></div>
            <div style={{ ...hCell, textAlign:"center" }}>Enable</div>
            <div style={{ ...hCell, textAlign:"center" }}></div>
          </div>
        );
        return (
          <div style={{ background:"#FFFFFF", border:"1px solid #E5E7EB", borderRadius:10, overflow:"hidden" }}>
            <div style={{ overflowX:"auto" }}>
              {namedGroups.filter(g => groupFilter === "all" || String(g.id) === groupFilter || (subGroupsByParent[g.id] || []).some(sg => String(sg.id) === groupFilter)).map(g => {
                const grpData = groups.find(x => x.GroupID === g.id);
                const rows = buildRows(g.monitors);
                const isExpanded = expandedGroups.has(g.id);
                const gSyntheticFlows = syntheticFlows.filter(f => f.GroupName === g.name);
                const childGroups = subGroupsByParent[g.id] || [];
                return (
                  <React.Fragment key={g.id}>
                    {renderGroupHeader(g.id, g.name, g.monitors, grpData?.Color || null, false, 0)}
                    {isExpanded && childGroups.map(sg => {
                      const sgData = groups.find(x => x.GroupID === sg.id);
                      const sgRows = buildRows(sg.monitors);
                      const sgExpanded = expandedGroups.has(sg.id);
                      const sgSyntheticFlows = syntheticFlows.filter(f => f.GroupName === sg.name);
                      return (
                        <React.Fragment key={sg.id}>
                          {renderGroupHeader(sg.id, sg.name, sg.monitors, sgData?.Color || null, false, 1)}
                          {sgExpanded && (sgRows.length > 0 || sgSyntheticFlows.length > 0) && subHeaderRow}
                          {sgExpanded && sgRows.map((row, i) => renderMonitorRow(row, i))}
                          {sgExpanded && sgSyntheticFlows.map((f, i) => renderSyntheticFlowRow(f, sgRows.length + i))}
                          {sgExpanded && sgRows.length === 0 && sgSyntheticFlows.length === 0 && (
                            <div style={{ padding:'12px 16px 12px 40px', fontSize:11, color:'#9CA3AF', borderBottom:'0.5px solid #E5E7EB' }}>No monitors in this group.</div>
                          )}
                        </React.Fragment>
                      );
                    })}
                    {isExpanded && rows.length > 0 && (
                      <React.Fragment>
                        {subHeaderRow}
                        {rows.map((row, i) => renderMonitorRow(row, i))}
                      </React.Fragment>
                    )}
                    {isExpanded && rows.length === 0 && gSyntheticFlows.length > 0 && subHeaderRow}
                    {isExpanded && gSyntheticFlows.map((f, i) => renderSyntheticFlowRow(f, rows.length + i))}
                  </React.Fragment>
                );
              })}
              {ungrouped.length > 0 && (() => {
                const rows = buildRows(ungrouped);
                const isExpanded = expandedGroups.has("ungrouped");
                return (
                  <React.Fragment key="ungrouped">
                    {renderGroupHeader("ungrouped", "Ungrouped", ungrouped, null, true)}
                    {isExpanded && subHeaderRow}
                    {isExpanded && rows.map((row, i) => renderMonitorRow(row, i))}
                  </React.Fragment>
                );
              })()}
              {filtered.length === 0 && (
                <div style={{ padding:32, textAlign:"center", color:c.textDimmer }}>No monitors found.</div>
              )}
            </div>
          </div>
        );
      })()}

      {deleteTarget && (
        <div style={{ position:"fixed",inset:0,background:"rgba(0,0,0,0.75)",zIndex:2000,display:"flex",alignItems:"center",justifyContent:"center" }}>
          <Card style={{ maxWidth:380, width:"100%", margin:16 }}>
            <div style={{ fontSize:14, fontWeight:600, marginBottom:8 }}>Delete monitor?</div>
            <div style={{ fontSize:12, color:c.textMuted, marginBottom:6 }}>
              <strong style={{ color:c.text }}>{deleteTarget.MonitorName}</strong> and all its check history, alerts, and notification config will be permanently removed.
            </div>
            <div style={{ fontSize:11, color:c.red, marginBottom:20 }}>This action cannot be undone.</div>
            <div style={{ display:"flex", gap:10, justifyContent:"flex-end" }}>
              <Btn variant="secondary" onClick={()=>setDeleteTarget(null)}>Cancel</Btn>
              <Btn variant="danger" loading={deleting} onClick={confirmDelete}>Delete</Btn>
            </div>
          </Card>
        </div>
      )}

      {/* ── Group delete — Step 1: pick action ── */}
      {deleteGroupTarget && deleteGroupStep === 1 && (
        <div style={{ position:"fixed",inset:0,background:"rgba(0,0,0,0.75)",zIndex:2000,display:"flex",alignItems:"center",justifyContent:"center" }}>
          <Card style={{ maxWidth:420, width:"100%", margin:16 }}>
            <div style={{ fontSize:14, fontWeight:600, marginBottom:8, color:c.text }}>
              Delete group — {deleteGroupTarget.groupName}?
            </div>
            <div style={{ fontSize:12, color:c.textMuted, marginBottom:20 }}>
              This group contains <strong style={{ color:c.text }}>{deleteGroupTarget.count} monitor{deleteGroupTarget.count !== 1 ? "s" : ""}</strong>. What would you like to do?
            </div>
            <div style={{ display:"flex", flexDirection:"column", gap:8, marginBottom:16 }}>
              <Btn variant="secondary" onClick={() => doDeleteGroup(false)} loading={deletingGroup}
                style={{ textAlign:"left", justifyContent:"flex-start" }}>
                Delete Group Only — keep all monitors (unassigned)
              </Btn>
              <Btn variant="danger" onClick={() => { setDeleteGroupStep(2); }}
                style={{ textAlign:"left", justifyContent:"flex-start" }}>
                Delete Group + All Monitors — permanently delete {deleteGroupTarget.count} monitor{deleteGroupTarget.count !== 1 ? "s" : ""} and their history
              </Btn>
            </div>
            <div style={{ display:"flex", justifyContent:"flex-end" }}>
              <Btn variant="secondary" onClick={() => setDeleteGroupTarget(null)}>Cancel</Btn>
            </div>
          </Card>
        </div>
      )}

      {/* ── Group delete — Step 2: confirm full delete ── */}
      {deleteGroupTarget && deleteGroupStep === 2 && (
        <div style={{ position:"fixed",inset:0,background:"rgba(0,0,0,0.75)",zIndex:2000,display:"flex",alignItems:"center",justifyContent:"center" }}>
          <Card style={{ maxWidth:400, width:"100%", margin:16 }}>
            <div style={{ fontSize:14, fontWeight:600, marginBottom:8, color:c.text }}>Are you sure?</div>
            <div style={{ fontSize:12, color:c.textMuted, marginBottom:8 }}>
              This will permanently delete <strong style={{ color:c.text }}>{deleteGroupTarget.count} monitor{deleteGroupTarget.count !== 1 ? "s" : ""}</strong> and all their check history.
            </div>
            <div style={{ fontSize:11, color:c.red, marginBottom:20, fontWeight:600 }}>This cannot be undone.</div>
            <div style={{ display:"flex", gap:10, justifyContent:"flex-end" }}>
              <Btn variant="secondary" onClick={() => setDeleteGroupStep(1)}>Cancel</Btn>
              <Btn variant="danger" loading={deletingGroup} onClick={() => doDeleteGroup(true)}>Yes, Delete Everything</Btn>
            </div>
          </Card>
        </div>
      )}

      {/* ── Group delete success message ── */}
      {groupSuccessMsg && (
        <div style={{ position:"fixed", bottom:40, left:"50%", transform:"translateX(-50%)", background:"#1E2B3C", color:"#5DD4F4",
          padding:"10px 20px", borderRadius:8, fontSize:12, fontWeight:600, zIndex:3000, boxShadow:"0 4px 12px rgba(0,0,0,0.3)" }}>
          {groupSuccessMsg}
        </div>
      )}
      </React.Fragment>)}
      </div>

      {/* ── Group notification modal ── */}
      {groupNotifModal !== null && (()=>{
        const gid = groupNotifModal.groupId;
        const gname = groupNotifModal.groupName;
        const unassigned = groupNotifRecipients.filter(r=>!groupAssignments.some(a=>a.RecipientID===r.RecipientID));
        return (
          <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,0.45)", zIndex:1200, display:"flex", alignItems:"center", justifyContent:"center" }}>
            <div style={{ background:"#fff", borderRadius:10, width:580, maxWidth:"95vw", maxHeight:"90vh", display:"flex", flexDirection:"column", boxShadow:"0 8px 32px rgba(0,0,0,0.18)" }}>
              {/* Header */}
              <div style={{ background:"#1E2B3C", color:"#fff", padding:"14px 20px", borderRadius:"10px 10px 0 0", display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0 }}>
                <span style={{ fontWeight:700, fontSize:14 }}>Notification settings — {gname}</span>
                <button onClick={()=>setGroupNotifModal(null)} style={{ background:"none", border:"none", color:"rgba(255,255,255,0.7)", fontSize:18, cursor:"pointer", lineHeight:1 }}>×</button>
              </div>
              {/* Subheader toggle */}
              <div style={{ background:groupNotifEnabled?c.greenLight:c.redLight, padding:"10px 16px", display:"flex", alignItems:"center", gap:10, flexShrink:0 }}>
                <div onClick={async()=>{ const newVal=!groupNotifEnabled; setGroupNotifEnabled(newVal); try { await api("PUT",`/notifications/groups/${gid}/enabled`,{enabled:newVal}); } catch{} }}
                  style={{ width:28, height:15, borderRadius:8, background:groupNotifEnabled?c.green:c.red, position:"relative", cursor:"pointer", flexShrink:0 }}>
                  <div style={{ position:"absolute", top:2, left:groupNotifEnabled?14:2, width:11, height:11, borderRadius:"50%", background:"#fff", transition:"left 0.15s" }} />
                </div>
                <div>
                  <div style={{ fontSize:12, fontWeight:700, color:groupNotifEnabled?c.green:c.red }}>{groupNotifEnabled?"Notifications enabled for this group":"Notifications disabled for this group"}</div>
                  <div style={{ fontSize:11, color:c.textMuted, marginTop:1 }}>Disable to suppress all alerts for every monitor in this group</div>
                </div>
              </div>
              {/* Body */}
              <div style={{ overflowY:"auto", padding:16, display:"flex", flexDirection:"column", gap:16, flex:1 }}>
                {groupAssignmentsLoading ? (
                  <div style={{ textAlign:"center", padding:32, color:c.textMuted, fontSize:12 }}>Loading…</div>
                ) : (<>
                  {/* Section 1: Group-level recipients */}
                  <div>
                    <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.06em", borderBottom:`1px solid ${c.border}`, paddingBottom:4, marginBottom:8 }}>Group-level recipients</div>
                    <div style={{ fontSize:11, color:c.textMuted, marginBottom:8 }}>These recipients receive alerts from all monitors in this group.</div>
                    {groupAssignments.length === 0 ? (
                      <div style={{ fontSize:12, color:c.textMuted, padding:"8px 0" }}>No group-level recipients. Add one below.</div>
                    ) : (
                      <div style={{ display:"flex", flexDirection:"column", gap:6, marginBottom:8 }}>
                        {groupAssignments.map(a=>(
                          <div key={a.AssignmentID||a.ID} style={{ display:"flex", alignItems:"center", gap:8, border:`1px solid ${c.border}`, borderRadius:6, padding:"6px 10px", background:c.surfaceAlt }}>
                            <div style={{ width:24, height:24, borderRadius:"50%", background:a.ChannelType==='webhook'?c.yellowLight:c.blueLight, color:a.ChannelType==='webhook'?c.yellow:c.blue, display:"flex", alignItems:"center", justifyContent:"center", fontSize:9, fontWeight:700, flexShrink:0 }}>
                              {(a.RecipientName||a.DisplayName||'?').split(' ').map(w=>w[0]||'').join('').slice(0,2).toUpperCase()}
                            </div>
                            <div style={{ flex:1, minWidth:0 }}>
                              <div style={{ fontSize:12, fontWeight:600, color:c.text }}>{a.RecipientName||a.DisplayName}</div>
                              <div style={{ fontSize:10, color:c.textMuted, fontFamily:"monospace" }}>{a.Destination}</div>
                            </div>
                            <span style={{ fontSize:9, borderRadius:4, padding:"1px 6px", background:a.ChannelType==='webhook'?c.yellowLight:c.blueLight, color:a.ChannelType==='webhook'?c.yellow:c.blue, fontWeight:600, textTransform:"uppercase", flexShrink:0 }}>{a.ChannelType||'email'}</span>
                            <select value={a.MinSeverity||'warning'} style={{ fontSize:10, padding:"1px 4px", border:`1px solid ${c.border}`, borderRadius:4, background:"#fff", width:90 }}
                              onChange={async e=>{ await api("PUT",`/notifications/assignments/${a.AssignmentID||a.ID}`,{MinSeverity:e.target.value,CooldownMinutes:a.CooldownMinutes,IsEnabled:a.IsEnabled}); loadGroupNotifModal(gid,gname); }}>
                              <option value="warning">Warning+</option>
                              <option value="critical">Critical only</option>
                            </select>
                            <input type="number" value={a.CooldownMinutes||30} min={0} style={{ width:44, fontSize:10, padding:"1px 4px", border:`1px solid ${c.border}`, borderRadius:4, textAlign:"center" }}
                              onBlur={async e=>{ await api("PUT",`/notifications/assignments/${a.AssignmentID||a.ID}`,{MinSeverity:a.MinSeverity,CooldownMinutes:parseInt(e.target.value)||30,IsEnabled:a.IsEnabled}); loadGroupNotifModal(gid,gname); }}
                              onChange={e=>setGroupAssignments(prev=>prev.map(x=>(x.AssignmentID||x.ID)===(a.AssignmentID||a.ID)?{...x,CooldownMinutes:parseInt(e.target.value)||0}:x))} />
                            <span style={{ fontSize:10, color:c.textMuted }}>min</span>
                            <div onClick={async()=>{ await api("PUT",`/notifications/assignments/${a.AssignmentID||a.ID}`,{MinSeverity:a.MinSeverity,CooldownMinutes:a.CooldownMinutes,IsEnabled:!a.IsEnabled}); loadGroupNotifModal(gid,gname); }}
                              style={{ width:28, height:15, borderRadius:8, background:a.IsEnabled?c.green:c.border, position:"relative", cursor:"pointer", flexShrink:0 }}>
                              <div style={{ position:"absolute", top:2, left:a.IsEnabled?14:2, width:11, height:11, borderRadius:"50%", background:"#fff", transition:"left 0.15s" }} />
                            </div>
                            <button onClick={()=>{ showConfirm('Remove this recipient from group?', async () => { await api("DELETE",`/notifications/assignments/${a.AssignmentID||a.ID}`); loadGroupNotifModal(gid,gname); loadGroupAssignmentCounts(groups); }); }}
                              style={{ background:"none", border:`1px solid ${c.border}`, borderRadius:4, color:c.red, cursor:"pointer", fontSize:13, padding:"0 6px", lineHeight:"20px", flexShrink:0 }}>×</button>
                          </div>
                        ))}
                      </div>
                    )}
                    <button onClick={()=>{ setGroupAssignPicker(p=>!p); setGroupPickerRecipient(''); }}
                      style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${c.blue}`, borderRadius:5, background:"#fff", color:c.blue, cursor:"pointer" }}>
                      + Assign recipient
                    </button>
                    {groupAssignPicker && (
                      <div style={{ marginTop:8, padding:"10px 12px", background:c.surfaceAlt, border:`1px solid ${c.border}`, borderRadius:6, display:"flex", alignItems:"center", gap:8, flexWrap:"wrap" }}>
                        {unassigned.length === 0 ? (
                          <span style={{ fontSize:12, color:c.textMuted }}>All recipients already assigned.</span>
                        ) : (<>
                          <select value={groupPickerRecipient} onChange={e=>setGroupPickerRecipient(e.target.value)} style={{ fontSize:11, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, minWidth:140 }}>
                            <option value="">Select recipient…</option>
                            {unassigned.map(r=><option key={r.RecipientID} value={r.RecipientID}>{r.DisplayName}</option>)}
                          </select>
                          <select value={groupPickerSeverity} onChange={e=>setGroupPickerSeverity(e.target.value)} style={{ fontSize:11, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4 }}>
                            <option value="warning">Warning+</option>
                            <option value="critical">Critical only</option>
                          </select>
                          <input type="number" value={groupPickerCooldown} min={0} onChange={e=>setGroupPickerCooldown(parseInt(e.target.value)||0)} style={{ width:50, fontSize:11, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, textAlign:"center" }} />
                          <span style={{ fontSize:11, color:c.textMuted }}>min</span>
                          <button disabled={!groupPickerRecipient} onClick={async()=>{ await api("POST","/notifications/assignments",{GroupID:gid,RecipientID:parseInt(groupPickerRecipient),MinSeverity:groupPickerSeverity,CooldownMinutes:groupPickerCooldown}); setGroupAssignPicker(false); setGroupPickerRecipient(''); loadGroupNotifModal(gid,gname); loadGroupAssignmentCounts(groups); }}
                            style={{ fontSize:11, padding:"3px 10px", border:"none", borderRadius:4, background:groupPickerRecipient?c.blue:"#ccc", color:"#fff", cursor:groupPickerRecipient?"pointer":"not-allowed" }}>Add</button>
                          <button onClick={()=>setGroupAssignPicker(false)} style={{ fontSize:11, padding:"3px 8px", border:`1px solid ${c.border}`, borderRadius:4, background:"#fff", color:c.textMuted, cursor:"pointer" }}>Cancel</button>
                        </>)}
                      </div>
                    )}
                  </div>
                  {/* Section 2: Per-monitor coverage */}
                  <div>
                    <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.06em", borderBottom:`1px solid ${c.border}`, paddingBottom:4, marginBottom:8 }}>Monitor coverage</div>
                    <div style={{ fontSize:11, color:c.textMuted, marginBottom:8 }}>Showing notification coverage for all monitors in this group.</div>
                    <div style={{ border:`1px solid ${c.border}`, borderRadius:6, overflow:"hidden" }}>
                      {groupMonitorsForModal.length === 0 ? (
                        <div style={{ padding:16, fontSize:12, color:c.textMuted, textAlign:"center" }}>No monitors in this group.</div>
                      ) : groupMonitorsForModal.map((m, idx)=>{
                        const ownCount = monitorAssignmentCounts[m.MonitorID] || 0;
                        const groupCount = groupAssignments.length;
                        const hasOwn = ownCount > 0;
                        const hasGroup = groupCount > 0;
                        const status = (m.EffectiveStatus||m.CurrentStatus||'unknown').toLowerCase();
                        const dotCol = status==='healthy'?c.green:status==='warning'?c.yellow:status==='critical'?c.red:'#9CA3AF';
                        return (
                          <div key={m.MonitorID} style={{ display:"flex", alignItems:"center", gap:10, padding:"8px 12px", borderBottom:idx<groupMonitorsForModal.length-1?`1px solid ${c.border}`:"none" }}>
                            <div style={{ width:8, height:8, borderRadius:"50%", background:dotCol, flexShrink:0 }} />
                            <span style={{ flex:1, fontSize:12, fontWeight:600, color:c.text }}>{m.MonitorName}</span>
                            {hasOwn && hasGroup && <span style={{ fontSize:10, borderRadius:4, padding:"1px 7px", background:c.blueLight, color:c.blue, fontWeight:600 }}>+{ownCount} own</span>}
                            {!hasOwn && hasGroup && <span style={{ fontSize:10, borderRadius:4, padding:"1px 7px", background:"#F5F0E8", color:c.textMuted, fontWeight:600 }}>Group only</span>}
                            {!hasGroup && <span style={{ fontSize:10, borderRadius:4, padding:"1px 7px", background:c.yellowLight, color:c.yellow, fontWeight:600 }}>No coverage</span>}
                            <button onClick={()=>{ setGroupNotifModal(null); openEdit(m); }}
                              style={{ fontSize:11, color:c.blue, background:"none", border:"none", cursor:"pointer", padding:0, textDecoration:"underline" }}>Edit monitor →</button>
                          </div>
                        );
                      })}
                    </div>
                  </div>
                </>)}
              </div>
              {/* Footer */}
              <div style={{ padding:"10px 16px", borderTop:`1px solid ${c.border}`, display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0, background:c.surfaceAlt }}>
                <span style={{ fontSize:11, color:c.textMuted }}>Changes apply to all monitors in {gname}</span>
                <div style={{ display:"flex", gap:8 }}>
                  <Btn variant="secondary" onClick={()=>setGroupNotifModal(null)}>Close</Btn>
                </div>
              </div>
            </div>
          </div>
        );
      })()}

      {bulkSuccessMsg && (
        <div style={{ position:'fixed', bottom:20, right:20, background:'#0F6E56', color:'#fff', padding:'10px 18px', borderRadius:8, fontSize:13, fontWeight:600, boxShadow:'0 4px 16px rgba(0,0,0,0.3)', zIndex:2000, pointerEvents:'none' }}>
          {bulkSuccessMsg}
        </div>
      )}

      {confirmModal && (
        <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,0.75)', zIndex:1300, display:'flex', alignItems:'center', justifyContent:'center' }}
          onClick={e => { if(e.target===e.currentTarget) setConfirmModal(null); }}>
          <div style={{ background:'#fff', borderRadius:8, width:380, maxWidth:'90vw', padding:'24px', boxShadow:'0 8px 32px rgba(0,0,0,0.18)' }}>
            <div style={{ fontSize:13, color:'#1A1A1A', marginBottom:20, lineHeight:1.5 }}>{confirmModal.msg}</div>
            <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
              <button onClick={()=>setConfirmModal(null)}
                style={{ fontSize:12, padding:'6px 16px', border:'1px solid #006D8C', borderRadius:6, background:'#fff', color:'#006D8C', cursor:'pointer' }}>Cancel</button>
              <button onClick={()=>{ confirmModal.onOk(); setConfirmModal(null); }}
                style={{ fontSize:12, padding:'6px 16px', border:'none', borderRadius:6, background:'#D95C5C', color:'#fff', cursor:'pointer', fontWeight:600 }}>{confirmModal.okLabel || 'Confirm'}</button>
            </div>
          </div>
        </div>
      )}

      {/* ── News ticker ── */}
      <div style={{ height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
        <div style={{ paddingLeft:10, paddingRight:10, borderRight:"0.5px solid rgba(255,255,255,0.15)", flexShrink:0, height:"100%", display:"flex", alignItems:"center" }}>
          <span style={{ fontSize:9, color:"rgba(255,255,255,0.4)", textTransform:"uppercase", letterSpacing:"0.08em" }}>&#9679; LIVE</span>
        </div>
        {recentAlerts.length > 0 ? (
          <div style={{ display:"inline-flex", animation:`op1-ticker ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message ? ` \u00b7 ${item.Message}` : ""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

    </div>
  );
}
