// OP1 Operations Portal v1.0 — CDN build (React 18 UMD + Babel standalone, no build step)
// React hooks pulled from the global React object loaded by index.html
const { useState, useEffect, useCallback, useRef } = React;

// ═══════════════════════════════════════════════════════════════════════════
// OP1 Operations Portal v1.0 — Dashboard
// Role-based: Admin (full) | User/Operator | Viewer (read-only)
// ═══════════════════════════════════════════════════════════════════════════

const API_BASE = (typeof window !== "undefined" && window.OP1_API) || "";
const API = `${API_BASE}/api`;

// ── Color palette ──
const c = {
  bg: "#FFFFFF", surface: "#FFFFFF", surfaceAlt: "#F0F7FB",
  border: "#C0D8E8", borderLight: "#D4E8F4",
  text: "#1A1A1A", textMuted: "#4A4A4A", textDim: "#4A4A4A", textDimmer: "#6B6B6B",
  green: "#008C6F", yellow: "#E89A2E", red: "#D95C5C", blue: "#006D8C",
  cyan: "#006D8C", purple: "#5E5A9E", amber: "#E89A2E", teal: "#008C6F",
  greenLight: "#DCF2EA", yellowLight: "#FDF3E0", redLight: "#FAEAEA", blueLight: "#E0F2F7",
  sidebarBg: "#1E2B3C", sidebarText: "#F4F8FC", sidebarText2: "#FFFFFF",
  sidebarMuted: "#7A9AB8", sidebarActiveBg: "rgba(0,109,140,0.35)",
  sidebarActive: "#5DD4F4", sidebarBorder: "rgba(255,255,255,0.1)",
};

const TYPE_COLOR = { Server: c.blue, Application: c.purple, Database: c.amber, Website: c.teal, Synthetic: c.green };
const STATUS_COLOR = { healthy: c.green, warning: c.yellow, degraded: c.yellow, critical: c.red, unknown: "#9CA3AF", suppressed: "#9CA3AF" };

// ── Help content ──
const HELP_CONTENT = {
  'server-health': {
    title: 'OP1 Server Health',
    sections: [
      {
        heading: 'CPU',
        text: 'Shows the percentage of total server CPU capacity used by the OP1 monitoring process. Measured across all CPU cores — on a 14-core machine, 1% here equals roughly 14% of a single core. Green below 70%, amber 71–85%, red above 85%.'
      },
      {
        heading: 'System Memory',
        text: 'Total RAM usage across the entire server — all running applications combined, not just OP1. If this is consistently red, consider closing other applications or upgrading server RAM.'
      },
      {
        heading: 'App Memory',
        text: 'RAM used by the OP1 process specifically (WorkingSet). This should match the memory shown for OpsPortal.Api.exe in Windows Task Manager → Details tab. Informational only — does not affect the status indicator.'
      },
      {
        heading: 'Disk',
        text: 'Percentage of disk space used on the server drive where OP1 is installed. Monitor check history and logs are stored here — if this reaches red, consider adjusting data retention settings in Config → Retention.'
      },
      {
        heading: 'Database',
        text: 'Response time of a lightweight ping (SELECT 1) to the OpsPortal SQL Server database. Green = connected under 500ms. Amber = connected but slow over 500ms. Red = unreachable. If red, monitor checks will fail to record results.'
      },
      {
        heading: 'Checks/min',
        text: 'Number of monitor checks executed in the last 60 seconds by the OP1 scheduler. A healthy platform typically shows 1 check per minute per active monitor.'
      }
    ],
    learnMore: null
  },
  'source-monitor': {
    title: 'Source Monitor',
    sections: [
      {
        heading: 'What is Source Monitor?',
        text: 'Identifies which OP1 instance created and monitors this check. When multiple OP1 instances share the same database (e.g. development and production servers), Source Monitor lets you see exactly which machine owns each monitor and generated each result.'
      },
      {
        heading: 'Multiple instances',
        text: 'Each OP1 server automatically registers itself using its Windows machine name. You can assign a friendly alias (e.g. "Production Server") in Config → Source Monitor.'
      },
      {
        heading: 'Filtering',
        text: 'Use the Source Monitor filter on the Logs and Alerts screens to view results from a specific machine only.'
      }
    ],
    learnMore: null,
    position: { left: 500, top: 120 }
  }
};

// ── UTC timestamp parser ──
function parseUTC(ts) {
  if (!ts) return null;
  const s = String(ts);
  return new Date(s.endsWith('Z') || s.includes('+') ? s : s + 'Z');
}

// ── Subtype → primary metric metadata ──
function primaryMetric(subtype) {
  const s = (subtype || '').toLowerCase();
  if (s === 'infra-cpu')                          return { label:'Avg CPU',      unit:'%',    kpiLabel:'AVG CPU' };
  if (s === 'infra-mem')                          return { label:'Avg Memory',   unit:'%',    kpiLabel:'AVG MEMORY' };
  if (s === 'infra-disk' || s === 'infra-diskio') return { label:'Avg Disk',     unit:'%',    kpiLabel:'AVG DISK' };
  if (s === 'infra-net')                          return { label:'Network',       unit:'Mbps', kpiLabel:'AVG NETWORK' };
  if (s === 'infra-ping')                         return { label:'Avg RTT',       unit:'ms',   kpiLabel:'AVG RTT' };
  if (s === 'infra-tcp' || s === 'app-port')      return { label:'Avg Connect',   unit:'ms',   kpiLabel:'AVG CONNECT' };
  if (s === 'web-ssl')                            return { label:'Days Left',     unit:'d',    kpiLabel:'AVG DAYS' };
  if (s === 'web-dns')                            return { label:'Resolve Time',  unit:'ms',   kpiLabel:'AVG RESOLVE' };
  if (s === 'db-query' || s === 'db-sqlctr')      return { label:'Avg Query',     unit:'ms',   kpiLabel:'AVG QUERY' };
  return { label:'Avg Response', unit:'ms', kpiLabel:'AVG RESPONSE' };
}

// ── Utility Components ──
const StatusDot = ({ status }) => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
    <span style={{ width: 8, height: 8, borderRadius: "50%", background: STATUS_COLOR[status] || STATUS_COLOR.unknown, boxShadow: status === "critical" ? `0 0 0 3px rgba(239,68,68,0.4)` : "none", animation: status === "critical" ? "pulse 1.5s infinite" : "none" }} />
    <span style={{ textTransform: "uppercase", fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", color: STATUS_COLOR[status] || STATUS_COLOR.unknown }}>{status}</span>
  </span>
);

const Badge = ({ children, color = "#9CA3AF" }) => (
  <span style={{ display: "inline-block", padding: "2px 8px", borderRadius: 4, fontSize: 10, fontWeight: 600, background: color + "22", color, border: `1px solid ${color}35`, fontFamily: "mono", whiteSpace: "nowrap" }}>{children}</span>
);

const SevBadge = ({ severity }) => {
  const m = { critical: c.red, warning: c.yellow, info: c.blue, low: c.teal };
  return <Badge color={m[severity] || "#6b7280"}>{severity}</Badge>;
};

const TypeBadge = ({ type }) => <Badge color={TYPE_COLOR[type] || c.textDim}>{type}</Badge>;

const Card = ({ children, style = {}, ...rest }) => (
  <div style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 10, padding: 20, ...style }} {...rest}>{children}</div>
);

const MetricCard = ({ label, value, sub, accent = c.blue }) => (
  <Card style={{ textAlign: "center", flex: "1 1 130px", minWidth: 120, borderTop: `2px solid ${accent}` }}>
    <div style={{ fontSize: 10, color: c.textDim, fontWeight: 600, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 6 }}>{label}</div>
    <div style={{ fontSize: 24, fontWeight: 800, color: c.text, fontFamily: "mono" }}>{value ?? "—"}</div>
    {sub && <div style={{ fontSize: 10, color: c.textDimmer, marginTop: 4 }}>{sub}</div>}
  </Card>
);

const WmiCombo = ({ label, hint, value, onChange, discoveryType, host, credentialId, api }) => {
  const [open,    setOpen]    = React.useState(false);
  const [items,   setItems]   = React.useState([]);
  const [loading, setLoading] = React.useState(false);
  const [error,   setError]   = React.useState(null);
  const [filter,  setFilter]  = React.useState("");
  const wrapRef = React.useRef(null);

  const canDiscover = host && credentialId && parseInt(credentialId) > 0;

  const colDefs = {
    processes:  [{ k:"name", lbl:"Name", flex:2 }, { k:"pid",  lbl:"PID",     flex:1, mono:true }, { k:"cpu", lbl:"CPU", flex:1 }, { k:"memory", lbl:"Memory", flex:1 }],
    services:   [{ k:"name", lbl:"Service name", flex:1, mono:true }, { k:"displayName", lbl:"Display name", flex:2 }, { k:"state", lbl:"State", flex:1 }, { k:"startMode", lbl:"Start", flex:1 }],
    drives:     [{ k:"device", lbl:"Device", flex:1 }, { k:"label", lbl:"Label", flex:1 }, { k:"size", lbl:"Size", flex:1 }, { k:"free", lbl:"Free", flex:1 }, { k:"usedPct", lbl:"Used", flex:1 }],
    interfaces: [{ k:"name", lbl:"Interface name", flex:3 }, { k:"speed", lbl:"Speed", flex:1 }, { k:"mac", lbl:"MAC", flex:2, mono:true }],
    eventlogs:  [{ k:"name", lbl:"Channel", flex:3 }, { k:"entries", lbl:"Entries", flex:1, mono:true }],
  };

  const cols = colDefs[discoveryType] || colDefs.processes;

  const statusColor = s => s === "running" || s === "connected" || s === "healthy" ? "#008C6F" : s === "stopped" || s === "disconnected" ? "#E89A2E" : "#9CA3AF";

  const fetchItems = async () => {
    if (!canDiscover) return;
    setLoading(true); setError(null); setItems([]);
    try {
      const data = await api("GET", `/discover?type=${discoveryType}&host=${encodeURIComponent(host)}&credentialId=${credentialId}`);
      setItems(Array.isArray(data) ? data : []);
    } catch(e) {
      setError(e?.message || "Discovery failed");
    } finally {
      setLoading(false);
    }
  };

  const handleOpen = () => {
    if (!canDiscover) return;
    setOpen(o => {
      if (!o) { setFilter(""); fetchItems(); }
      return !o;
    });
  };

  const handleSelect = item => {
    onChange(item.value);
    setOpen(false);
  };

  React.useEffect(() => {
    const handler = e => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const filtered = items.filter(item =>
    !filter || (item.value || "").toLowerCase().includes(filter.toLowerCase()) ||
    (item.displayName || "").toLowerCase().includes(filter.toLowerCase())
  );

  const gridCols = `14px ${cols.map(c => `${c.flex}fr`).join(" ")}`;

  return (
    <div ref={wrapRef} style={{ display:"flex", flexDirection:"column", gap:4, position:"relative" }}>
      {label && <label style={{ fontSize:11, color:c.textMuted, fontWeight:600, letterSpacing:"0.05em", textTransform:"uppercase" }}>{label}</label>}
      <div style={{ position:"relative" }}>
        <input
          readOnly
          value={value || ""}
          placeholder={canDiscover ? "Click to browse..." : "Enter host + credential first"}
          onClick={handleOpen}
          style={{ background:c.surface, border:`1px solid ${open ? "#006D8C" : c.border}`, borderRadius:open?"6px 6px 0 0":6, padding:"8px 28px 8px 12px", color:value?c.text:c.textDimmer, fontSize:13, fontFamily:"inherit", outline:"none", width:"100%", cursor:canDiscover?"pointer":"default" }}
        />
        <span style={{ position:"absolute", right:10, top:"50%", transform:"translateY(-50%)", fontSize:10, color:c.textDim, pointerEvents:"none" }}>{open ? "▲" : "▼"}</span>
      </div>
      {open && (
        <div style={{ position:"absolute", top:"100%", left:0, right:0, zIndex:200, background:c.surface, border:`1px solid #006D8C`, borderTop:"none", borderRadius:"0 0 6px 6px", overflow:"hidden" }}>
          <div style={{ display:"grid", gridTemplateColumns:gridCols, gap:"0 8px", padding:"4px 8px", background:c.bg, borderBottom:`1px solid ${c.border}` }}>
            <span></span>
            {cols.map(col => (
              <span key={col.k} style={{ fontSize:10, color:c.textDim, textTransform:"uppercase", letterSpacing:"0.04em", fontWeight:600 }}>{col.lbl}</span>
            ))}
          </div>
          <div style={{ maxHeight:192, overflowY:"auto" }}>
            {loading && (
              <div style={{ padding:"16px 12px", textAlign:"center", fontSize:12, color:c.textDim }}>Connecting to {host}...</div>
            )}
            {error && (
              <div style={{ padding:"12px", fontSize:11, color:"#D95C5C", background:"#FFF5F5" }}>{error}</div>
            )}
            {!loading && !error && filtered.length === 0 && (
              <div style={{ padding:"12px", fontSize:12, color:c.textDim, textAlign:"center" }}>No items found</div>
            )}
            {!loading && filtered.map((item, i) => (
              <div key={i} onClick={() => handleSelect(item)}
                style={{ display:"grid", gridTemplateColumns:gridCols, gap:"0 8px", padding:"3px 8px", alignItems:"center", cursor:"pointer", borderBottom:`1px solid ${c.border}`, background: item.value === value ? "#E0F2F7" : "transparent" }}
                onMouseEnter={e => { if(item.value !== value) e.currentTarget.style.background = c.bg; }}
                onMouseLeave={e => { if(item.value !== value) e.currentTarget.style.background = "transparent"; }}
              >
                <span style={{ width:6, height:6, borderRadius:"50%", background:statusColor(item.status), display:"inline-block" }}></span>
                {cols.map(col => (
                  <span key={col.k} style={{ fontSize:11, color:col.mono?c.textDim:c.text, fontFamily:col.mono?"monospace":"inherit", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" }}>
                    {item[col.k] ?? ""}
                  </span>
                ))}
              </div>
            ))}
          </div>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", padding:"5px 8px", borderTop:`1px solid ${c.border}`, background:c.bg }}>
            <span style={{ fontSize:10, color:c.textDim }}>{loading ? "Loading..." : `${filtered.length} item${filtered.length!==1?"s":""}`}</span>
            <input
              type="text"
              placeholder="Filter..."
              value={filter}
              onChange={e => setFilter(e.target.value)}
              onClick={e => e.stopPropagation()}
              style={{ fontSize:11, padding:"2px 7px", border:`1px solid ${c.border}`, borderRadius:4, background:c.surface, color:c.text, width:110, outline:"none" }}
            />
          </div>
        </div>
      )}
      {hint && <div style={{ fontSize:11, color:c.textDimmer, marginTop:1 }}>{hint}</div>}
    </div>
  );
};

const Input = ({ label, hint, ...p }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
    {label && <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase" }}>{label}</label>}
    <input {...p} style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", width: "100%", ...p.style }} />
    {hint && <div style={{ fontSize: 11, color: c.textDimmer, marginTop: 1 }}>{hint}</div>}
  </div>
);

const Sel = ({ label, options, ...p }) => (
  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
    {label && <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase" }}>{label}</label>}
    <select {...p} style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", ...p.style }}>
      {options.map(o => <option key={o.value ?? o} value={o.value ?? o}>{o.label ?? o}</option>)}
    </select>
  </div>
);

const Btn = ({ children, variant = "primary", loading, ...p }) => {
  const s = {
    primary:   { background: c.blue,        color: "#fff",  border: "none" },
    secondary: { background: c.surface,     color: c.text,  border: `1px solid ${c.border}` },
    danger:    { background: "transparent", color: c.red,   border: `1px solid ${c.red}` },
    ghost:     { background: "transparent", color: c.textDim,   border: "none" },
    success:   { background: c.green,       color: "#fff",  border: "none" },
    warning:   { background: c.amber,       color: "#fff",  border: "none" },
  };
  return (
    <button {...p} disabled={loading || p.disabled}
      style={{ ...s[variant], borderRadius: 6, padding: "8px 16px", fontSize: 12, fontWeight: 700,
               cursor: loading || p.disabled ? "not-allowed" : "pointer",
               opacity: loading || p.disabled ? 0.5 : 1, transition: "opacity 0.15s", ...p.style }}>
      {loading ? "…" : children}
    </button>
  );
};

const SectionHead = ({ children }) => (
  <h3 style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 14 }}>{children}</h3>
);

const Spinner = () => (
  <div style={{ display: "flex", alignItems: "center", justifyContent: "center", padding: 40, color: c.textDim, fontSize: 12 }}>Loading…</div>
);

const ErrBox = ({ msg }) => msg ? (
  <div style={{ background: `${c.red}18`, border: `1px solid ${c.red}40`, borderRadius: 8, padding: "10px 14px", color: c.red, fontSize: 12, marginBottom: 12 }}>{msg}</div>
) : null;

const OkBox = ({ msg }) => msg ? (
  <div style={{ background: `${c.green}18`, border: `1px solid ${c.green}40`, borderRadius: 8, padding: "10px 14px", color: c.green, fontSize: 12, marginBottom: 12 }}>{msg}</div>
) : null;

// ── HelpIcon — ? circle trigger for contextual help popovers ──
const HelpIcon = ({ hkey, setHelpKey, theme = "dark" }) => {
  const [hov, setHov] = useState(false);
  const borderColor = theme === "light"
    ? (hov ? "#7A9AB8" : "#C0D8E8")
    : (hov ? "rgba(255,255,255,1)" : "rgba(255,255,255,0.6)");
  const textColor = theme === "light"
    ? (hov ? "#006D8C" : "#7A9AB8")
    : (hov ? "#FFFFFF" : "rgba(255,255,255,0.75)");
  return (
    <span
      onMouseDown={e => e.stopPropagation()}
      onClick={() => setHelpKey(k => k === hkey ? null : hkey)}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        width: 13, height: 13, borderRadius: "50%",
        background: "transparent",
        border: `1px solid ${borderColor}`,
        color: textColor,
        fontSize: 9, fontWeight: 600,
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer", marginLeft: 5, flexShrink: 0, userSelect: "none",
      }}
    >?</span>
  );
};

// ── HelpPopover — contextual help panel, anchored via fixed positioning ──
const HelpPopover = ({ helpKey, setHelpKey }) => {
  const panelRef = useRef(null);
  const content = helpKey ? HELP_CONTENT[helpKey] : null;

  useEffect(() => {
    if (!helpKey) return;
    const onDown = e => {
      if (panelRef.current && !panelRef.current.contains(e.target)) setHelpKey(null);
    };
    const onKey = e => { if (e.key === "Escape") setHelpKey(null); };
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [helpKey]);

  if (!content) return null;
  const pos = content.position || { left: 225, bottom: 140 };
  return (
    <div ref={panelRef} style={{
      position: "fixed", ...pos,
      width: 280, background: "#FFFFFF",
      border: "1px solid #C0D8E8", borderRadius: 8,
      boxShadow: "0 4px 12px rgba(0,0,0,0.12)",
      padding: "14px 16px", zIndex: 1000,
      animation: "fadeIn 0.15s ease",
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 10 }}>
        <span style={{ fontSize: 13, fontWeight: 700, color: "#1A1A1A" }}>{content.title}</span>
        <button onClick={() => setHelpKey(null)} style={{
          background: "none", border: "none", cursor: "pointer",
          color: "#7A9AB8", fontSize: 16, lineHeight: 1, padding: "0 0 0 8px", flexShrink: 0,
        }}>×</button>
      </div>
      {content.sections.map((s, i) => (
        <div key={i}>
          {i > 0 && <div style={{ height: 1, background: "#F0F7FB", margin: "8px 0" }} />}
          <div style={{ fontSize: 11, fontWeight: 700, color: "#1A1A1A", marginBottom: 3 }}>{s.heading}</div>
          <div style={{ fontSize: 12, color: "#4A4A4A", lineHeight: 1.6 }}>{s.text}</div>
        </div>
      ))}
      {content.learnMore && (
        <div style={{ marginTop: 10, paddingTop: 8, borderTop: "1px solid #F0F7FB" }}>
          <a href={content.learnMore} target="_blank" rel="noreferrer"
            style={{ fontSize: 11, color: "#006D8C", textDecoration: "none" }}>Learn more →</a>
        </div>
      )}
    </div>
  );
};

// ── CollapsibleSection ──
function CollapsibleSection({ title, defaultOpen = true, alwaysOpen = false, children }) {
  const [open, setOpen] = useState(defaultOpen);
  if (alwaysOpen) {
    return (
      <div style={{ marginBottom: 14 }}>
        <div style={{ padding: "8px 0 4px 0" }}>
          <span className="cs-title" style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase" }}>{title}</span>
        </div>
        {children}
      </div>
    );
  }
  return (
    <div style={{ marginBottom: 18 }}>
      <button onClick={() => setOpen(o => !o)} className="cs-btn"
        style={{ background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", gap: 6, padding: "0 0 8px 0", width: "100%" }}>
        <span className="cs-title" style={{ fontSize: 11, fontWeight: 700, color: c.blue, letterSpacing: "0.08em", textTransform: "uppercase" }}>{title}</span>
        <span style={{ fontSize: 10, color: c.textDimmer, marginLeft: "auto" }}>{open ? "▲" : "▼"}</span>
      </button>
      {open && children}
    </div>
  );
}

// ── GroupAutocomplete ──
function GroupAutocomplete({ value, groups, onChange, tabIndex }) {
  // value = group name string; onChange(name)
  const [open, setOpen] = useState(false);
  const [input, setInput] = useState(value || "");
  const ref = useRef(null);

  useEffect(() => { setInput(value || ""); }, [value]);

  useEffect(() => {
    const handler = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  const matches = (groups || []).filter(g =>
    !input || g.GroupName.toLowerCase().includes(input.toLowerCase())
  ).slice(0, 8);

  const select = g => { onChange(g.GroupName); setInput(g.GroupName); setOpen(false); };
  const clear  = () => { onChange(""); setInput(""); };

  return (
    <div ref={ref} style={{ position: "relative" }}>
      <label style={{ fontSize: 11, color: c.textMuted, fontWeight: 600, letterSpacing: "0.05em", textTransform: "uppercase", display: "block", marginBottom: 4 }}>Group</label>
      <div style={{ position: "relative" }}>
        <input
          value={input}
          onFocus={() => setOpen(true)}
          onChange={e => { setInput(e.target.value); onChange(e.target.value); setOpen(true); }}
          placeholder="Type or select group…"
          tabIndex={tabIndex}
          style={{ background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, padding: "8px 28px 8px 12px", color: c.text, fontSize: 13, fontFamily: "inherit", outline: "none", width: "100%" }}
        />
        {input && (
          <button onClick={clear} style={{ position: "absolute", right: 6, top: "50%", transform: "translateY(-50%)", background: "none", border: "none", cursor: "pointer", color: c.textDimmer, fontSize: 14, padding: 0 }}>×</button>
        )}
        {open && matches.length > 0 && (
          <div style={{ position: "absolute", top: "100%", left: 0, right: 0, background: c.surface, border: `1px solid ${c.border}`, borderRadius: 6, zIndex: 100, boxShadow: "0 4px 12px rgba(0,0,0,0.1)", maxHeight: 180, overflowY: "auto", marginTop: 2 }}>
            {matches.map(g => (
              <div key={g.GroupID} onClick={() => select(g)}
                style={{ padding: "8px 12px", cursor: "pointer", display: "flex", alignItems: "center", gap: 8, fontSize: 12 }}
                onMouseEnter={e => e.currentTarget.style.background = c.surfaceAlt}
                onMouseLeave={e => e.currentTarget.style.background = ""}>
                <span>{g.GroupName}</span>
                <span style={{ marginLeft: "auto", fontSize: 10, color: c.textDimmer }}>{g.MonitorCount} monitors</span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ── Tabs ──
const ADMIN_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "synthetic", label: "Synthetic",  icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "reports",   label: "Reports",    icon: "📋" },
  { key: "config",    label: "Config",     icon: "⚙" },
  { key: "agents",    label: "Agents",     icon: "▣" },
  { key: "users",     label: "Users",      icon: "👤" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];
const USER_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "synthetic", label: "Synthetic",  icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "reports",   label: "Reports",    icon: "📋" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];
const VIEWER_TABS = [
  { key: "dashboard", label: "Dashboard",  icon: "◉" },
  { key: "monitors",  label: "Monitors",   icon: "⬡" },
  { key: "topology",  label: "Topology",   icon: "⬡" },
  { key: "alerts",    label: "Alerts",     icon: "!" },
  { key: "logs",      label: "Logs",       icon: "☰" },
  { key: "profile",   label: "My Profile", icon: "🔑" },
];

// ── API helper ──
function useApi(token, onUnauthorized) {
  return useCallback(async (method, path, body, signal) => {
    const headers = { "Content-Type": "application/json" };
    if (token) headers["Authorization"] = `Bearer ${token}`;
    const res = await fetch(`${API}${path}`, {
      method, headers,
      body: body !== undefined ? JSON.stringify(body) : undefined,
      signal,
    });
    if (res.status === 401) {
      localStorage.removeItem("op1_token");
      localStorage.removeItem("op1_user");
      localStorage.setItem("op1_session_expired", "true");
      if (onUnauthorized) onUnauthorized();
      throw new Error("Session expired");
    }
    if (res.status === 204) return null;
    const text = await res.text();
    let data;
    try { data = text ? JSON.parse(text) : null; } catch { data = text; }
    if (!res.ok) throw new Error(data?.title || data?.message || data?.Error || `HTTP ${res.status}`);
    return data;
  }, [token, onUnauthorized]);
}

// ── Sidebar nav button ──
function TabBtn({ t, active, onClick, api }) {
  const [alertCount, setAlertCount] = useState(0);
  const [hov, setHov] = useState(false);
  useEffect(() => {
    if (t.key !== "alerts" || !api) return;
    api("GET", "/logs/alerts?unresolvedOnly=true&top=1000")
      .then(r => setAlertCount(Array.isArray(r) ? r.length : 0))
      .catch(() => {});
  }, [t.key, api]);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: active ? "rgba(255,255,255,0.10)" : hov ? "rgba(255,255,255,0.08)" : "transparent",
        border: "none",
        borderLeft: active ? "2px solid #FFFFFF" : "2px solid transparent",
        color: "#FFFFFF",
        borderRadius: "0 6px 6px 0",
        padding: "9px 14px 9px 13px",
        fontSize: 12, fontWeight: 400,
        cursor: "pointer", display: "flex", alignItems: "center",
        width: "100%", textAlign: "left", marginBottom: 2, transition: "all 0.15s",
      }}>
      <span style={{ width: 18, flexShrink: 0, textAlign: "center", marginRight: 8, fontSize: 12 }}>{t.icon}</span>
      <span>{t.label}</span>
      {t.key === "alerts" && alertCount > 0 && (
        <span style={{ background: c.red, color: "#fff", borderRadius: 8, padding: "1px 5px", fontSize: 9, fontWeight: 700, marginLeft: "auto" }}>{alertCount}</span>
      )}
    </button>
  );
}

// ── Group delete trash icon button ──
const GroupDeleteBtn = ({ onClick }) => {
  const [hov, setHov] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      title="Delete group"
      style={{ background:"transparent", border:"none", cursor:"pointer", padding:"2px 3px",
        display:"flex", alignItems:"center", color: hov ? "#D95C5C" : "#7A9AB8", transition:"color 0.15s" }}
    >
      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
        <polyline points="3 6 5 6 21 6"/>
        <path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/>
        <path d="M10 11v6M14 11v6"/>
        <path d="M9 6V4a1 1 0 011-1h4a1 1 0 011 1v2"/>
      </svg>
    </button>
  );
};

// ── Add Monitor sidebar sub-item ──
const AddMonitorNavItem = ({ active, onClick }) => {
  const [hov, setHov] = useState(false);
  return (
    <button
      onClick={onClick}
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      style={{
        background: hov ? "rgba(255,255,255,0.08)" : "transparent",
        border: "none",
        borderLeft: "2px solid transparent",
        padding: "5px 14px 5px 13px",
        fontSize: 11, cursor: "pointer",
        color: "#FFFFFF",
        display: "flex", alignItems: "center",
        width: "100%", textAlign: "left", marginBottom: 2,
        transition: "all 0.15s",
      }}
    >
      <div style={{ width: 26, flexShrink: 0 }} />
      <span>+ Add Monitor</span>
    </button>
  );
};

// ── Server Health Widget (sidebar bottom) ────────────────────────────────
function ServerHealthWidget({ api, setHelpKey }) {
  const [health, setHealth] = useState(null);
  const [unavail, setUnavail] = useState(false);
  const [lastUpdated, setLastUpdated] = useState(null);

  const fetchHealth = useCallback(() => {
    api("GET", "/dashboard/health")
      .then(d => { setHealth(d); setUnavail(false); setLastUpdated(new Date()); })
      .catch(() => { setUnavail(true); });
  }, [api]);

  useEffect(() => {
    fetchHealth();
    const id = setInterval(fetchHealth, 30000);
    return () => clearInterval(id);
  }, [fetchHealth]);

  const barColor = pct => pct > 85 ? "#D95C5C" : pct > 70 ? "#E89A2E" : "#008C6F";

  const statusDot = !health ? "#7A9AB8"
    : (health.CpuPercent > 85 || (health.SystemMemoryPercent ?? health.MemoryPercent) > 85 || (health.DiskPercent ?? 0) > 85 || health.DbStatus === "error") ? "#D95C5C"
    : (health.CpuPercent > 70 || (health.SystemMemoryPercent ?? health.MemoryPercent) > 70 || (health.DiskPercent ?? 0) > 70 || health.DbStatus === "slow") ? "#E89A2E"
    : "#008C6F";

  const updatedStr = lastUpdated
    ? lastUpdated.toLocaleTimeString([], { hour:"2-digit", minute:"2-digit", second:"2-digit" })
    : null;

  return (
    <div style={{ margin:"0 8px 6px", borderTop:`1px solid rgba(255,255,255,0.08)`, paddingTop:8, flexShrink:0 }}>
      <div style={{ display:"flex", flexDirection:"column", gap:3, marginBottom:5 }}>
        <span style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.08em" }}>OP1 Server Health</span>
        <div style={{ display:"flex", alignItems:"center", gap:4 }}>
          <span style={{ width:7, height:7, borderRadius:"50%", background:statusDot, display:"inline-block", flexShrink:0 }} />
          {setHelpKey && <HelpIcon hkey="server-health" setHelpKey={setHelpKey} />}
          {updatedStr && <span style={{ fontSize:8, color:"#F4F8FC" }}>{updatedStr}</span>}
        </div>
      </div>
      {unavail ? (
        <div style={{ fontSize:10, color:"#7A9AB8", textAlign:"center", paddingTop:8 }}>Unavailable</div>
      ) : !health ? (
        <div style={{ fontSize:10, color:"#7A9AB8" }}>Loading…</div>
      ) : (
        <div style={{ display:"flex", flexDirection:"column", gap:4 }}>
          {[
            { label:"CPU",        pct: health.CpuPercent,                                                          sub: null },
            { label:"System Mem", pct: health.SystemMemoryPercent ?? health.MemoryPercent,                         sub: health.SystemMemoryTotalMb != null ? `${(health.SystemMemoryUsedMb/1024).toFixed(1)}gb used of ${(health.SystemMemoryTotalMb/1024).toFixed(1)}gb` : null },
            { label:"App Mem",    pct: health.AppMemoryPercent ?? 0,                                               sub: health.AppMemoryMb != null ? `${health.AppMemoryMb}mb (OP1 process)` : null },
            { label:"Disk",       pct: health.DiskPercent ?? 0,                                                    sub: health.DiskGB || null },
          ].map(row => (
            <div key={row.label}>
              <div style={{ display:"flex", alignItems:"center", gap:5 }}>
                <span style={{ fontSize:9, color:"#7A9AB8", width:60, flexShrink:0 }}>{row.label}</span>
                <div style={{ flex:1, height:4, background:"rgba(255,255,255,0.1)", borderRadius:2, overflow:"hidden" }}>
                  <div style={{ width:`${Math.min(100,row.pct)}%`, height:"100%", background:barColor(row.pct), borderRadius:2, transition:"width 0.4s" }} />
                </div>
                <span style={{ fontSize:9, color:"#7A9AB8", width:28, textAlign:"right", flexShrink:0 }}>{row.pct}%</span>
              </div>
              {row.sub && <div style={{ fontSize:8, color:"#4A6A80", marginLeft:65, marginTop:1 }}>{row.sub}</div>}
            </div>
          ))}
          <div style={{ height:1, background:"rgba(255,255,255,0.06)", margin:"5px 0" }} />
          <div style={{ display:"flex", alignItems:"center", gap:6, padding:"2px 0" }}>
            <span style={{ width:7, height:7, borderRadius:"50%", flexShrink:0, display:"inline-block",
              background: health.DbStatus === "online" ? "#008C6F" : health.DbStatus === "slow" ? "#E89A2E" : "#D95C5C" }} />
            <span style={{ fontSize:9, color:"#7A9AB8", flex:1 }}>Database</span>
            <span style={{ fontSize:9, fontWeight:600, color:"#5DD4F4" }}>
              {health.DbResponseMs >= 0 ? `${health.DbResponseMs}ms` : "—"}
            </span>
            <span style={{ fontSize:9, color:"#4A6A80", marginLeft:4 }}>
              · {health.DbStatus === "online" ? "Online" : health.DbStatus === "slow" ? "Slow" : "Error"}
            </span>
          </div>
          {health.DbName && (
            <div style={{ fontSize:8, color:"#4A6A80", marginLeft:13, marginBottom:2 }}>
              {health.DbName} · {health.DbHost}
            </div>
          )}
          <div style={{ height:1, background:"rgba(255,255,255,0.06)", margin:"5px 0" }} />
          <div style={{ display:"flex", alignItems:"center", gap:5 }}>
            <span style={{ fontSize:9, color:"#7A9AB8", width:60, flexShrink:0 }}>Checks/min</span>
            <span style={{ fontSize:10, fontWeight:600, color:"#5DD4F4" }}>{health.ChecksPerMin ?? "—"}</span>
          </div>
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════
// MAIN APP
// ═══════════════════════════════════════════════════════════════════
function OpsPortalApp() {
  const [token, setToken] = useState(() => localStorage.getItem("op1_token") || "");
  const [user, setUser]   = useState(() => { try { return JSON.parse(localStorage.getItem("op1_user") || "null"); } catch { return null; } });
  const [tab, setTab]     = useState("dashboard");
  const navInterceptRef = useRef(null);
  const pendingAddMonitorRef = useRef(false);
  const [addMonitorActive, setAddMonitorActive] = useState(false);
  const handleTabClick = (key) => {
    if (key === "monitors") { setAddMonitorActive(false); setMonitorsNav({}); }
    if (navInterceptRef.current) navInterceptRef.current(key);
    else setTab(key);
  };
  useEffect(() => { if (tab !== "monitors") setAddMonitorActive(false); }, [tab]);

  const [helpKey, setHelpKey] = useState(null);

  const [loginForm, setLoginForm]       = useState({ email: "", password: "", rememberMe: false });
  const [loginStep, setLoginStep]       = useState("login");
  const [mfaChallenge, setMfaChallenge] = useState("");
  const [mfaCode, setMfaCode]           = useState("");
  const [ssoProviders, setSsoProviders] = useState([]);
  const [loginErr, setLoginErr]         = useState("");
  const [loginLoading, setLoginLoading] = useState(false);
  const [changePassForm, setChangePassForm] = useState({ current: "", newPass: "", confirm: "" });
  const [changePassErr, setChangePassErr]   = useState("");
  const [sessionExpired, setSessionExpired] = useState(() => {
    const f = localStorage.getItem("op1_session_expired");
    if (f) { localStorage.removeItem("op1_session_expired"); return true; }
    return false;
  });

  const handleUnauthorized = useCallback(() => {
    setToken(""); setUser(null); setSessionExpired(true);
  }, []);
  const api = useApi(token, handleUnauthorized);

  // ── Monitoring control state ──
  const [monCtrl, setMonCtrl]           = useState(null);
  const [machines, setMachines]         = useState([]);
  const [pausePopover, setPausePopover] = useState(false);
  const [pauseReason, setPauseReason]   = useState("");
  const [pauseBusy, setPauseBusy]       = useState(false);
  const currentMachine = monCtrl?.currentMachine || "";
  const currentCtrl    = monCtrl?.machines?.find(m => m.MachineName === currentMachine);
  const isPaused       = currentCtrl ? !currentCtrl.IsActive : false;

  const loadMonCtrl = useCallback(() => {
    if (!token) return;
    api("GET", "/config/monitoring-control")
      .then(r => { setMonCtrl(r); setMachines(r?.machines || []); })
      .catch(() => {});
  }, [api, token]);

  useEffect(() => {
    loadMonCtrl();
    const id = setInterval(loadMonCtrl, 30000);
    return () => clearInterval(id);
  }, [loadMonCtrl]);

  const togglePause = async () => {
    setPauseBusy(true);
    try {
      await api("POST", "/config/monitoring-control/toggle", {
        MachineName: currentMachine,
        IsActive: isPaused,
        Reason: isPaused ? null : (pauseReason || "Manual pause"),
      });
      setPausePopover(false);
      setPauseReason("");
      loadMonCtrl();
    } catch(e) { /* ignore */ }
    finally { setPauseBusy(false); }
  };

  useEffect(() => {
    fetch(`${API}/auth/sso/providers`)
      .then(r => r.ok ? r.json() : [])
      .then(setSsoProviders)
      .catch(() => {});
  }, []);

  const saveSession = (tok, usr) => {
    localStorage.setItem("op1_token", tok);
    localStorage.setItem("op1_user", JSON.stringify(usr));
    setToken(tok); setUser(usr); setLoginStep("login"); setSessionExpired(false);
  };

  const handleLogin = async () => {
    setLoginErr(""); setLoginLoading(true);
    try {
      const r = await fetch(`${API}/auth/login`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ Email: loginForm.email, Password: loginForm.password, RememberMe: loginForm.rememberMe }),
      });
      const data = await r.json();
      if (!r.ok) { setLoginErr(data?.title || data?.Error || "Login failed"); return; }
      if (data.MFARequired)        { setMfaChallenge(data.MFAChallenge); setLoginStep("mfa"); return; }
      if (data.MustChangePassword) {
        saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
        setLoginStep("changepass"); return;
      }
      saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
    } catch (e) { setLoginErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleMfa = async () => {
    setLoginErr(""); setLoginLoading(true);
    try {
      const r = await fetch(`${API}/auth/mfa-verify`, {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ MFAChallenge: mfaChallenge, Code: mfaCode }),
      });
      const data = await r.json();
      if (!r.ok) { setLoginErr(data?.title || data?.Error || "MFA failed"); return; }
      if (data.MustChangePassword) {
        saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
        setLoginStep("changepass"); return;
      }
      saveSession(data.Token, { UserID: data.UserID, DisplayName: data.DisplayName, RoleName: data.RoleName, Username: data.Username });
    } catch (e) { setLoginErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleChangePass = async () => {
    if (changePassForm.newPass !== changePassForm.confirm) { setChangePassErr("Passwords do not match"); return; }
    setChangePassErr(""); setLoginLoading(true);
    try {
      await api("POST", "/auth/change-password", { CurrentPassword: changePassForm.current, NewPassword: changePassForm.newPass, ConfirmPassword: changePassForm.confirm });
      setLoginStep("login"); setLoginForm(p => ({ ...p, password: "" }));
    } catch (e) { setChangePassErr(e.message); }
    finally { setLoginLoading(false); }
  };

  const handleLogout = async () => {
    try { await api("POST", "/auth/logout", {}); } catch {}
    localStorage.removeItem("op1_token"); localStorage.removeItem("op1_user");
    setToken(""); setUser(null); setTab("dashboard"); setLoginStep("login");
  };

  const role = user?.RoleName || "";
  const isAdmin  = role === "Admin";
  const isUser   = role === "User" || role === "Operator";
  const tabs     = isAdmin ? ADMIN_TABS : isUser ? USER_TABS : VIEWER_TABS;
  const canManageMonitors = isAdmin || isUser;
  const canAckAlerts      = isAdmin || isUser;
  const canManageUsers    = isAdmin;
  const canManageConfig   = isAdmin;

  // Navigation state for cross-view navigation
  const [monitorsNav, setMonitorsNav] = useState({});
  const [syntheticNav, setSyntheticNav] = useState({});
  const navigateToSynthetic = (flowId) => { setSyntheticNav({ editFlowId: flowId }); setTab("synthetic"); };
  const navigateToSyntheticResults = (flowId, flowResultId, autodiagnose) => { setSyntheticNav({ resultsFlowId: flowId, resultsResultId: flowResultId, autodiagnose: !!autodiagnose }); setTab("synthetic"); };
  const navigateToMonitors = (groupId, editId, editMonitor, options) => {
    setMonitorsNav({ groupId: groupId || null, editId: editId || null, editMonitor: editMonitor || null, ...(options||{}) });
    setTab("monitors");
  };

  // Wrapped navigate passed to MonitorsView — handles pending "open add form" after dirty-discard
  const handleMonitorFormNavigate = useCallback((key) => {
    if (key === "correlation analysis") return; // onClose handles subView; stay on monitors tab
    if (pendingAddMonitorRef.current && key === "monitors") {
      setMonitorsNav({ openAdd: true });
      setAddMonitorActive(true);
      pendingAddMonitorRef.current = false;
    }
    setTab(key);
  }, []);

  const handleAddMonitorClick = () => {
    if (navInterceptRef.current) {
      // Dirty form is open — queue the openAdd action, then trigger dirty check
      pendingAddMonitorRef.current = true;
      navInterceptRef.current("monitors");
    } else {
      setMonitorsNav({ openAdd: true });
      setAddMonitorActive(true);
      setTab("monitors");
    }
  };

  const GLOBAL_STYLES = `
    @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
    @keyframes pulse    { 0%,100%{opacity:1} 50%{opacity:.4} }
    @keyframes fadeIn   { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
    @keyframes slideIn  { from{opacity:0;transform:translateX(-12px)} to{opacity:1;transform:translateX(0)} }
    @keyframes spin     { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
    input:focus, select:focus { border-color:${c.blue}!important; box-shadow:0 0 0 3px rgba(0,109,140,0.12)!important; }
    button:hover { opacity:.85; }
    ::-webkit-scrollbar       { width:6px; }
    ::-webkit-scrollbar-track { background:${c.surfaceAlt}; }
    ::-webkit-scrollbar-thumb { background:${c.border}; border-radius:3px; }
    input, select, textarea { color-scheme: light; }
    /* White panel overrides — form page light panels */
    .dp label { color: #4A4A4A !important; }
    .dp .cs-title { color: #1A1A1A !important; }
    .dp .cs-btn > span:last-child { color: #7A9AB8 !important; }
    .dp select { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    .dp input[type=text],.dp input[type=number],.dp input[type=email],.dp input[type=password] { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    .dp textarea { background:#FFFFFF; color:#1A1A1A; border-color:#C0D8E8; }
    /* Clickable monitor row hover */
    .mon-row:hover { background:#F8FAFC !important; cursor:pointer; }
  `;

  // ── LOGIN / MFA / CHANGE-PASS SCREENS ──
  if (!user || loginStep === "mfa" || loginStep === "changepass") {
    return (
      <div style={{ minHeight:"100vh", background:"#4DA2CC", display:"flex", alignItems:"center", justifyContent:"center", fontFamily:"'IBM Plex Sans',system-ui,sans-serif" }}>
        <style>{GLOBAL_STYLES}</style>

        {loginStep === "changepass" && (
          <Card style={{ width:420, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.amber}` }}>
            <div style={{ textAlign:"center", marginBottom:20 }}>
              <div style={{ fontSize:18, fontWeight:800, color:c.text }}>Change Password Required</div>
              <p style={{ fontSize:12, color:c.textDim, marginTop:6 }}>You must set a new password before continuing.</p>
            </div>
            <ErrBox msg={changePassErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <Input label="Current Password" type="password" value={changePassForm.current} onChange={e => setChangePassForm(p => ({...p, current:e.target.value}))} />
              <Input label="New Password" type="password" placeholder="Min 12 chars, upper+lower+digit+special" value={changePassForm.newPass} onChange={e => setChangePassForm(p => ({...p, newPass:e.target.value}))} />
              <Input label="Confirm New Password" type="password" value={changePassForm.confirm} onChange={e => setChangePassForm(p => ({...p, confirm:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleChangePass()} />
              <Btn onClick={handleChangePass} loading={loginLoading} style={{ width:"100%", padding:12, marginTop:4 }}>Set New Password</Btn>
            </div>
          </Card>
        )}

        {loginStep === "mfa" && (
          <Card style={{ width:360, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.blue}` }}>
            <div style={{ textAlign:"center", marginBottom:20 }}>
              <div style={{ fontSize:18, fontWeight:800, color:c.text }}>Two-Factor Authentication</div>
              <p style={{ fontSize:12, color:c.textDim, marginTop:6 }}>Enter the 6-digit code from your authenticator app.</p>
            </div>
            <ErrBox msg={loginErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <Input label="Authenticator Code" placeholder="000000" value={mfaCode} onChange={e => setMfaCode(e.target.value)} onKeyDown={e => e.key==="Enter" && handleMfa()} style={{ textAlign:"center", fontSize:22, letterSpacing:"0.3em" }} />
              <Btn onClick={handleMfa} loading={loginLoading} style={{ width:"100%", padding:12 }}>Verify</Btn>
              <Btn variant="ghost" onClick={() => { setLoginStep("login"); setMfaCode(""); setLoginErr(""); }} style={{ width:"100%", fontSize:11 }}>← Back</Btn>
            </div>
          </Card>
        )}

        {loginStep === "login" && (
          <Card style={{ width:400, animation:"fadeIn 0.4s ease", borderTop:`3px solid ${c.blue}` }}>
            {sessionExpired && (
              <div style={{ background:"#fffbeb", border:"0.5px solid #fde68a", color:"#b45309",
                borderRadius:6, padding:"10px 14px", fontSize:13, marginBottom:16 }}>
                Your session has expired. Please sign in to continue.
              </div>
            )}
            <div style={{ textAlign:"center", marginBottom:24 }}>
              <div style={{ width:52, height:52, borderRadius:14, background:"linear-gradient(135deg,#006D8C,#5DD4F4)", display:"inline-flex", alignItems:"center", justifyContent:"center", fontSize:13, fontWeight:800, color:"#fff", marginBottom:12, letterSpacing:"-0.02em" }}>OP1</div>
              <h1 style={{ fontSize:22, fontWeight:800, color:c.text, margin:0, letterSpacing:"-0.02em" }}>OP1 Operations Portal</h1>
              <p style={{ fontSize:12, color:c.textDim, marginTop:4 }}>v1.0 — Operations Monitoring Platform</p>
            </div>
            <ErrBox msg={loginErr} />
            <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
              <Input label="Email" type="email" placeholder="admin@localhost" autoComplete="email" value={loginForm.email} onChange={e => setLoginForm(p => ({...p, email:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleLogin()} />
              <Input label="Password" type="password" autoComplete="current-password" value={loginForm.password} onChange={e => setLoginForm(p => ({...p, password:e.target.value}))} onKeyDown={e => e.key==="Enter" && handleLogin()} />
              <label style={{ display:"flex", alignItems:"center", gap:8, fontSize:12, color:c.textMuted, cursor:"pointer" }}>
                <input type="checkbox" checked={loginForm.rememberMe} onChange={e => setLoginForm(p => ({...p, rememberMe:e.target.checked}))} style={{ accentColor:c.blue }} />
                Remember me for 30 days
              </label>
              <Btn onClick={handleLogin} loading={loginLoading} style={{ width:"100%", padding:12, fontSize:14, marginTop:4 }}>Sign In</Btn>
            </div>
            {ssoProviders.length > 0 && (
              <div style={{ marginTop:16 }}>
                <div style={{ textAlign:"center", fontSize:11, color:c.textDimmer, marginBottom:8 }}>— or sign in with —</div>
                <div style={{ display:"flex", gap:8, flexWrap:"wrap", justifyContent:"center" }}>
                  {ssoProviders.map(p => (
                    <Btn key={p.ConfigID} variant="secondary" onClick={() => window.location.href=`${API}/auth/sso/initiate/${p.ConfigID}`} style={{ flex:"1 1 auto" }}>
                      {p.DisplayName || p.Provider}
                    </Btn>
                  ))}
                </div>
              </div>
            )}
            <div style={{ marginTop:20, textAlign:"center", fontSize:11, color:c.textDimmer }}>© 2026 OP1 Operations Portal · All rights reserved.</div>
          </Card>
        )}
      </div>
    );
  }

  // ═══ AUTHENTICATED SHELL ═══
  return (
    <div style={{ minHeight:"100vh", background:c.bg, color:c.text, fontFamily:"'IBM Plex Sans',system-ui,sans-serif", display:"flex" }}>
      <style>{GLOBAL_STYLES}</style>

      {/* ── Sidebar ── */}
      <aside style={{ width:150, flexShrink:0, background:c.sidebarBg, display:"flex", flexDirection:"column", position:"fixed", top:0, left:0, bottom:0, zIndex:100 }}>

        <div style={{ padding:"18px 16px 14px", borderBottom:`1px solid ${c.sidebarBorder}` }}>
          <div style={{ display:"flex", alignItems:"center", gap:10 }}>
            <div style={{ width:34, height:34, borderRadius:8, background:"linear-gradient(135deg,#006D8C,#5DD4F4)", display:"flex", alignItems:"center", justifyContent:"center", fontSize:11, fontWeight:800, color:"#fff", letterSpacing:"-0.02em", flexShrink:0 }}>OP1</div>
            <div>
              <div style={{ fontWeight:800, fontSize:13, color:c.sidebarText, letterSpacing:"-0.02em", lineHeight:1.2 }}>OP1 Operations</div>
              <div style={{ fontSize:10, color:c.sidebarMuted }}>Portal v1.0</div>
            </div>
          </div>
        </div>
        <nav style={{ flex:1, padding:"10px 8px", overflowY:"auto" }}>
          {tabs.flatMap(t => {
            const btn = <TabBtn key={t.key} t={t} active={tab===t.key} onClick={() => handleTabClick(t.key)} api={api} />;
            if (t.key === "monitors" && canManageMonitors) {
              return [btn, <AddMonitorNavItem key="add-monitor" active={addMonitorActive && tab==="monitors"} onClick={handleAddMonitorClick} />];
            }
            return [btn];
          })}
        </nav>
        {/* ── Server Health Widget ── */}
        <ServerHealthWidget api={api} setHelpKey={setHelpKey} />
        <div style={{ padding:"12px 16px", borderTop:`1px solid ${c.sidebarBorder}` }}>
          <div style={{ fontSize:11, color:c.sidebarMuted, marginBottom:6, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{user.DisplayName}</div>
          <Badge color={isAdmin ? "#D95C5C" : isUser ? "#5DD4F4" : c.sidebarMuted}>{role}</Badge>
          <button onClick={handleLogout} style={{ display:"block", marginTop:10, background:"transparent", border:`1px solid ${c.sidebarBorder}`, borderRadius:5, color:c.sidebarMuted, fontSize:11, padding:"5px 10px", cursor:"pointer", width:"100%" }}>Sign Out</button>
          <div style={{ marginTop:10, fontSize:9, color:c.sidebarMuted, lineHeight:1.3, opacity:0.6, wordBreak:"break-word" }}>© 2026 OP1 Operations Portal. All rights reserved.</div>
        </div>
      </aside>

      {/* ── Contextual help popovers ── */}
      <HelpPopover helpKey={helpKey} setHelpKey={setHelpKey} />

      {/* ── Main content ── */}
      <div style={{ marginLeft:150, flex:1, minWidth:0, background:"#ffffff" }}>
        {/* Pause banner */}
        {isPaused && (
          <div style={{ background:"#B45309", color:"#FEF3C7", padding:"8px 20px", fontSize:12, fontWeight:600, display:"flex", alignItems:"center", gap:10, position:"sticky", top:0, zIndex:50 }}>
            <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:"#FDE68A", animation:"pulse 1.5s infinite", flexShrink:0 }} />
            ⚠ Monitoring paused by {currentCtrl?.PausedBy || "unknown"} at {currentCtrl?.PausedAt ? parseUTC(currentCtrl.PausedAt)?.toLocaleString() : "—"} — all checks suspended on {currentCtrl?.MachineAlias || currentMachine}
            {canManageConfig && <button onClick={()=>{ setPausePopover(true); }} style={{ marginLeft:"auto", background:"#FDE68A", color:"#78350F", border:"none", borderRadius:4, padding:"3px 10px", fontSize:11, fontWeight:700, cursor:"pointer" }}>Resume Monitoring</button>}
          </div>
        )}
        <main style={{ padding:"8px 16px 8px 20px" }}>
          {/* Topbar monitoring status indicator — hidden on all branded screens (each has its own pill) */}
          {!["dashboard","monitors","topology","alerts","logs","reports","config","agents","users","profile"].includes(tab) && canManageConfig && monCtrl && (
            <div style={{ display:"flex", justifyContent:"flex-end", marginBottom:10, position:"relative" }}>
              <button
                onClick={()=>setPausePopover(p=>!p)}
                style={{ display:"flex", alignItems:"center", gap:7, background: isPaused ? "rgba(180,83,9,0.08)" : "rgba(0,0,0,0.05)", border: `1px solid ${isPaused?"rgba(180,83,9,0.3)":"rgba(0,0,0,0.12)"}`, borderRadius:20, padding:"5px 12px", cursor:"pointer", color: isPaused ? "#B45309" : "#374151", fontSize:11, fontWeight:600 }}
              >
                <span style={{ width:8, height:8, borderRadius:"50%", background: isPaused ? "#F59E0B" : "#10B981", flexShrink:0, animation: isPaused ? "pulse 1.5s infinite" : "none" }} />
                {isPaused ? "Monitoring Paused" : "Monitoring Active"}
              </button>
              {pausePopover && (
                <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 }}>
                    {isPaused ? "Resume Monitoring" : "Pause Monitoring"}
                  </div>
                  <div style={{ fontSize:11, color:c.textDim, marginBottom:10 }}>
                    Machine: <span style={{ fontFamily:"mono", color:c.text }}>{currentMachine}</span>
                  </div>
                  {!isPaused && (
                    <Input label="Reason (optional)" value={pauseReason} onChange={e=>setPauseReason(e.target.value)} placeholder="e.g. Maintenance window" style={{ marginBottom:10, fontSize:11 }} />
                  )}
                  {isPaused && currentCtrl?.PauseReason && (
                    <div style={{ fontSize:11, color:c.textDim, marginBottom:8 }}>Pause reason: {currentCtrl.PauseReason}</div>
                  )}
                  {isPaused && currentCtrl?.PausedAt && (
                    <div style={{ fontSize:10, color:c.textDimmer, marginBottom:10 }}>Paused at: {parseUTC(currentCtrl.PausedAt)?.toLocaleString()}</div>
                  )}
                  <div style={{ display:"flex", gap:6 }}>
                    <Btn loading={pauseBusy} onClick={togglePause} variant={isPaused?"success":"warning"} style={{ flex:1, fontSize:11 }}>
                      {isPaused ? "Resume" : "Pause"}
                    </Btn>
                    <Btn variant="ghost" onClick={()=>{ setPausePopover(false); setPauseReason(""); }} style={{ fontSize:11 }}>Cancel</Btn>
                  </div>
                </div>
              )}
            </div>
          )}
          {tab==="dashboard" && <DashboardView api={api} onNavigate={navigateToMonitors} onNavigateAlerts={() => setTab("alerts")} machines={machines} currentMachine={currentMachine} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="monitors"  && <MonitorsView  api={api} canManage={canManageMonitors} isAdmin={isAdmin} initialNav={monitorsNav} onNavConsumed={()=>setMonitorsNav({})} navInterceptRef={navInterceptRef} onNavigate={handleMonitorFormNavigate} machines={machines} currentMachine={currentMachine} setHelpKey={setHelpKey} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} onNavigateSynthetic={navigateToSynthetic} />}
          {tab==="topology"  && <TopologyView  api={api} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl, currentMachine }} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Topology"})} />}
          {tab==="synthetic" && <SyntheticView api={api} canManage={canManageMonitors} initialNav={syntheticNav} onNavConsumed={()=>setSyntheticNav({})} />}
          {tab==="alerts"    && <AlertsView    api={api} canAck={canAckAlerts} machines={machines} currentMachine={currentMachine} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Alerts"})} onNavigateSynthetic={navigateToSyntheticResults} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="logs"      && <LogsView      api={api} machines={machines} currentMachine={currentMachine} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Logs"})} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="reports"   && <ReportsView   api={api} canGenerate={canManageMonitors} onNavigate={(id)=>navigateToMonitors(null,id,null,{origin:"Reports"})} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="config"    && canManageConfig && <ConfigView api={api} machines={machines} currentMachine={currentMachine} onMachinesChange={loadMonCtrl} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="agents"    && isAdmin         && <AgentsView api={api} />}
          {tab==="users"     && canManageUsers  && <UsersView  api={api} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
          {tab==="profile"   && <ProfileView   api={api} user={user} monitoringControl={{ isPaused, canManageConfig, monCtrl, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl }} />}
        </main>
      </div>
    </div>
  );
}

// ═══ DASHBOARD ═══

// ── Dashboard chart renderer (top-level, no inner functions) ──
function renderDashChart(chartType, canvasEl, summary, responseTrend, alertTrend, uptimeTrend) {
  if (!canvasEl || typeof Chart === "undefined") return null;
  const s = summary;
  const allMonitors = s.Monitors || [];
  const groupSummaries = s.GroupSummaries || [];
  const FONT = { size:10, family:"ui-sans-serif, system-ui, sans-serif" };

  if (chartType === "status-donut") {
    return new Chart(canvasEl, {
      type: "doughnut",
      data: {
        labels: ["Healthy","Warning","Critical","Disabled"],
        datasets: [{ data:[s.HealthyCount||0,s.WarningCount||0,s.CriticalCount||0,s.UnknownCount||0],
          backgroundColor:["#86efac","#fcd34d","#fca5a5","#e2e8f0"], borderWidth:0, hoverOffset:4 }]
      },
      options: { responsive:true, maintainAspectRatio:false, cutout:"72%", plugins:{ legend:{ display:false } } }
    });
  }

  if (chartType === "type-health-bar") {
    const types = ["Server","Application","Database","Website","Synthetic"];
    const healthy = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="healthy").length);
    const warning = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="warning").length);
    const critical = types.map(t => allMonitors.filter(m=>m.IsEnabled!==false&&m.MonitorType===t&&(m.EffectiveStatus||m.CurrentStatus)==="critical").length);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels: types, datasets: [
        { label:"Healthy", data:healthy, backgroundColor:"#86efac", borderRadius:2, stack:"s" },
        { label:"Warning", data:warning, backgroundColor:"#fcd34d", borderRadius:2, stack:"s" },
        { label:"Critical",data:critical,backgroundColor:"#fca5a5", borderRadius:2, stack:"s" },
      ]},
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:8 } },
        plugins:{ legend:{ display:false } },
        scales:{ x:{ stacked:true, grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ stacked:true, grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "group-health-bar") {
    const labels = groupSummaries.map(g=>g.GroupName||"Ungrouped");
    const colors = groupSummaries.map(g=>g.CriticalCount>0?"#fca5a5":g.WarningCount>0?"#fcd34d":"#86efac");
    const vals   = groupSummaries.map(g=>g.TotalCount||0);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels, datasets:[{ data:vals, backgroundColor:colors, borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "slowest-monitors") {
    const sorted = [...allMonitors].filter(m=>m.LastResponseTimeMs!=null).sort((a,b)=>b.LastResponseTimeMs-a.LastResponseTimeMs).slice(0,5);
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:sorted.map(m=>m.MonitorName), datasets:[{ data:sorted.map(m=>m.LastResponseTimeMs), backgroundColor:"#3b82f6", borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ callback:v=>v+"ms", font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "most-alerted") {
    const sorted = [...allMonitors].filter(m=>(m.OpenAlerts||0)>0).sort((a,b)=>(b.OpenAlerts||0)-(a.OpenAlerts||0)).slice(0,5);
    if (sorted.length === 0) return null;
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:sorted.map(m=>m.MonitorName), datasets:[{ data:sorted.map(m=>m.OpenAlerts||0), backgroundColor:"#fca5a5", borderRadius:3 }] },
      options: { indexAxis:"y", responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false } },
        scales:{ x:{ grid:{ color:"rgba(0,0,0,0.04)" }, ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT } }, y:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "response-trend" && responseTrend.length > 0) {
    // Sort ascending: oldest (left) → newest (right)
    const sorted = [...responseTrend].sort((a, b) => {
      if ((a.date||"") !== (b.date||"")) return (a.date||"") < (b.date||"") ? -1 : 1;
      return (a.hour||0) - (b.hour||0);
    });
    const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    // d.date arrives as a full datetime string (e.g. "2026-03-30T00:00:00") because
    // Dapper maps SQL date → C# DateTime. Use the date portion only for parsing.
    const dateKey = (d) => typeof d.date === "string" ? d.date.slice(0, 10) : "";
    const rtLabels = sorted.map((d, i) => {
      const hh = String(d.hour ?? 0).padStart(2,"0") + ":00";
      const dk = dateKey(d);
      const prevDk = i > 0 ? dateKey(sorted[i-1]) : null;
      if (dk && (!prevDk || dk !== prevDk)) {
        const dt = new Date(dk + "T00:00:00");
        if (!isNaN(dt.getTime()))
          return `${MONTHS[dt.getMonth()]} ${dt.getDate()} ${hh}`;
      }
      return hh;
    });
    return new Chart(canvasEl, {
      type: "line",
      data: { labels: rtLabels, datasets:[{ data: sorted.map(d=>d.avgMs),
        borderColor:"#3b82f6", backgroundColor:"rgba(59,130,246,0.06)", fill:true, tension:0.4, pointRadius:3, borderWidth:2 }] },
      options: { responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:4 } },
        plugins:{ legend:{ display:false }, title:{ display:true, text:"Hourly avg \u2014 last 24h", font:FONT, color:"#94a3b8", padding:{ bottom:0 } } },
        scales:{ y:{ ticks:{ callback:v=>v+"ms", font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } },
          x:{ grid:{ display:false }, ticks:{ maxTicksLimit:8, font:FONT, maxRotation:30 },
            title:{ display:true, text:"\u2190 24 hours ago\u2003\u2003Now \u2192", font:{ size:9, family:FONT.family }, color:"#94a3b8", padding:{ top:2 } } } } }
    });
  }

  if (chartType === "alert-trend" && alertTrend.length > 0) {
    return new Chart(canvasEl, {
      type: "bar",
      data: { labels:alertTrend.map(d=>d.day), datasets:[{ data:alertTrend.map(d=>d.count), backgroundColor:"#fca5a5", borderRadius:3 }] },
      options: { responsive:true, maintainAspectRatio:false, plugins:{ legend:{ display:false } },
        scales:{ y:{ ticks:{ stepSize:1, callback:v=>Number.isInteger(v)?v:null, font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } }, x:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  if (chartType === "uptime-trend" && uptimeTrend.length > 0) {
    return new Chart(canvasEl, {
      type: "line",
      data: { labels:uptimeTrend.map(d=>d.day), datasets:[{ data:uptimeTrend.map(d=>d.avgUptime),
        borderColor:"#86efac", backgroundColor:"rgba(134,239,172,0.1)", fill:true, tension:0.4, pointRadius:3, borderWidth:2 }] },
      options: { responsive:true, maintainAspectRatio:false, layout:{ padding:{ bottom:8 } }, plugins:{ legend:{ display:false } },
        scales:{ y:{ min:0, max:100, ticks:{ callback:v=>v+"%", font:FONT }, grid:{ color:"rgba(0,0,0,0.04)" } }, x:{ grid:{ display:false }, ticks:{ font:FONT } } } }
    });
  }

  return null;
}

function computeSparkPoints(trend) {
  if (!trend || trend.length < 2) return null;
  const vals = trend.map(d => Number(d.avgMs) || 0);
  const mn = Math.min(...vals), mx = Math.max(...vals);
  const range = mx - mn || 1;
  const W = 120, yMin = 3, yMax = 21;
  return trend.map((d, i) => {
    const x = Math.round((i / (trend.length - 1)) * W);
    const y = Math.round(yMax - ((Number(d.avgMs) || 0) - mn) / range * (yMax - yMin));
    return `${x},${y}`;
  }).join(" ");
}

const CHART_LIBRARY = [
  { id:"status-donut",     label:"Status Donut",        alwaysData:true  },
  { id:"type-health-bar",  label:"Health by Type",       alwaysData:true  },
  { id:"group-health-bar", label:"Health by Group",      alwaysData:true  },
  { id:"slowest-monitors", label:"Slowest Monitors",     alwaysData:true  },
  { id:"most-alerted",     label:"Most Alerted",         alwaysData:false },
  { id:"response-trend",   label:"Response Trend (24h)", alwaysData:false },
  { id:"alert-trend",      label:"Alert Trend (7d)",     alwaysData:false },
  { id:"uptime-trend",     label:"Uptime Trend (7d)",    alwaysData:false },
];
const CHART_DEFAULTS = ["status-donut","type-health-bar","group-health-bar","response-trend"];

function DashboardView({ api, onNavigate, onNavigateAlerts, machines, monitoringControl }) {
  const [summary, setSummary]           = useState(null);
  const [responseTrend, setRespTrend]   = useState([]);
  const [alertTrend, setAlertTrend]     = useState([]);
  const [uptimeTrend, setUptimeTrend]   = useState([]);
  const [groupTrends, setGroupTrends]   = useState({});
  const [err, setErr]                   = useState("");
  const [countdown, setCountdown]       = useState(30);
  const [refreshing, setRefreshing]     = useState(false);
  const [flashUpdated, setFlashUpdated] = useState(false);
  const [refreshInterval, setRefreshInterval] = useState(() => {
    const saved = localStorage.getItem("op1_refresh_interval");
    return saved ? parseInt(saved, 10) : 30;
  });
  const [slots, setSlots] = useState(() => [
    localStorage.getItem("op1_chart_slot_1") || CHART_DEFAULTS[0],
    localStorage.getItem("op1_chart_slot_2") || CHART_DEFAULTS[1],
    localStorage.getItem("op1_chart_slot_3") || CHART_DEFAULTS[2],
    localStorage.getItem("op1_chart_slot_4") || CHART_DEFAULTS[3],
  ]);
  const [openPicker, setOpenPicker] = useState(null);
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [runningGroup, setRunningGroup] = useState(null);
  const [hovStat, setHovStat] = useState(null);
  const [aiSummaryOpen, setAiSummaryOpen]       = useState(false);
  const [aiSummaryLoading, setAiSummaryLoading] = useState(false);
  const [aiSummaryData, setAiSummaryData]       = useState(null);
  const [aiSummaryError, setAiSummaryError]     = useState(null);
  const mc = monitoringControl || {};

  const fetchAiSummary = useCallback(async () => {
    setAiSummaryLoading(true);
    setAiSummaryError(null);
    try {
      const result = await api("POST", "/dashboard/ai-summary");
      setAiSummaryData(result?.summary ?? null);
    } catch(e) {
      setAiSummaryError(e.message);
    } finally {
      setAiSummaryLoading(false);
    }
  }, [api]);

  const handleAiPillClick = useCallback(() => {
    if (!aiSummaryOpen) {
      setAiSummaryOpen(true);
      if (!aiSummaryData) fetchAiSummary();
    } else {
      setAiSummaryOpen(false);
    }
  }, [aiSummaryOpen, aiSummaryData, fetchAiSummary]);

  const canvas0 = useRef(null); const canvas1 = useRef(null); const canvas2 = useRef(null); const canvas3 = useRef(null);
  const chart0  = useRef(null); const chart1  = useRef(null); const chart2  = useRef(null); const chart3  = useRef(null);
  const canvasRefs = [canvas0, canvas1, canvas2, canvas3];
  const chartRefs  = [chart0,  chart1,  chart2,  chart3];

  const fetchData = useCallback((signal) => {
    setRefreshing(true);
    Promise.all([
      api("GET", "/dashboard/summary",        undefined, signal),
      api("GET", "/dashboard/response-trend", undefined, signal).catch(() => []),
      api("GET", "/dashboard/alert-trend",    undefined, signal).catch(() => []),
      api("GET", "/dashboard/uptime-trend",   undefined, signal).catch(() => []),
      api("GET", "/alerts/recent?top=20",     undefined, signal).catch(() => []),
    ]).then(([s, rt, at, ut, ra]) => {
      setSummary(s);
      setRespTrend(Array.isArray(rt) ? rt : []);
      setAlertTrend(Array.isArray(at) ? at : []);
      setUptimeTrend(Array.isArray(ut) ? ut : []);
      setRecentAlerts(Array.isArray(ra) ? ra : []);
      setErr("");
      setRefreshing(false);
      setFlashUpdated(true);
      setTimeout(() => setFlashUpdated(false), 1000);
      // Phase 2: fetch group trends in parallel
      const groupIds = [0, ...((s && s.GroupSummaries) || []).map(g => g.GroupID)];
      Promise.all(
        groupIds.map(id => api("GET", `/dashboard/group-trend/${id}`, undefined, signal).catch(() => []))
      ).then(results => {
        const gt = {};
        groupIds.forEach((id, i) => { gt[id] = Array.isArray(results[i]) ? results[i] : []; });
        setGroupTrends(gt);
      }).catch(() => {});
    }).catch(e => {
      if (e.name === "AbortError") return;
      setRefreshing(false);
      setSummary(prev => { if (!prev) setErr(e.message); return prev; });
    });
  }, [api]);

  useEffect(() => { setCountdown(refreshInterval); }, [refreshInterval]);

  useEffect(() => {
    const ctrl = new AbortController();
    fetchData(ctrl.signal);
    const pollId = setInterval(() => { setCountdown(refreshInterval); fetchData(ctrl.signal); }, refreshInterval * 1000);
    const onVisible = () => { if (document.visibilityState === "visible") { setCountdown(refreshInterval); fetchData(ctrl.signal); } };
    document.addEventListener("visibilitychange", onVisible);
    return () => { ctrl.abort(); clearInterval(pollId); document.removeEventListener("visibilitychange", onVisible); };
  }, [fetchData, refreshInterval]);

  useEffect(() => {
    const id = setInterval(() => setCountdown(n => n > 0 ? n - 1 : 0), 1000);
    return () => clearInterval(id);
  }, []);

  useEffect(() => {
    if (!summary || typeof Chart === "undefined") return;
    const destroyAll = () => {
      chartRefs.forEach(r => { if (r.current) { r.current.destroy(); r.current = null; } });
    };
    destroyAll();
    slots.forEach((chartType, i) => {
      if (!canvasRefs[i].current) return;
      chartRefs[i].current = renderDashChart(chartType, canvasRefs[i].current, summary, responseTrend, alertTrend, uptimeTrend);
    });
    return destroyAll;
  }, [summary, responseTrend, alertTrend, uptimeTrend, slots]);

  // Close picker on outside click
  useEffect(() => {
    if (openPicker === null) return;
    const handler = (e) => {
      if (!e.target.closest("[data-chartpicker]")) setOpenPicker(null);
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [openPicker]);

  // Inject ticker keyframe CSS once
  useEffect(() => {
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style");
      el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
  }, []);

  const hasData = (chartType) => {
    const lib = CHART_LIBRARY.find(c => c.id === chartType);
    if (!lib) return false;
    if (lib.alwaysData) return true;
    if (chartType === "most-alerted") return (summary?.Monitors||[]).some(m=>(m.OpenAlerts||0)>0);
    if (chartType === "response-trend") return responseTrend.length > 0;
    if (chartType === "alert-trend") return alertTrend.length > 0;
    if (chartType === "uptime-trend") return uptimeTrend.length > 0;
    return false;
  };

  const changeSlot = (slotIdx, chartId) => {
    const newSlots = [...slots];
    newSlots[slotIdx] = chartId;
    setSlots(newSlots);
    localStorage.setItem(`op1_chart_slot_${slotIdx+1}`, chartId);
    setOpenPicker(null);
  };

  const handleIntervalChange = (e) => {
    const v = parseInt(e.target.value, 10);
    setRefreshInterval(v);
    localStorage.setItem("op1_refresh_interval", String(v));
  };

  if (err) return <ErrBox msg={err} />;
  if (!summary) return <Spinner />;

  const s = summary;
  const allMonitors = s.Monitors || [];
  const groupSummaries = s.GroupSummaries || [];
  const ungroupedMonitors = allMonitors.filter(m => !m.GroupID && !m.GroupName);
  const hasUngrouped = ungroupedMonitors.length > 0;
  const ugCritical = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "critical").length;
  const ugWarning  = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "warning").length;
  const ugHealthy  = ungroupedMonitors.filter(m => m.IsEnabled!==false&&(m.EffectiveStatus||m.CurrentStatus) === "healthy").length;
  const grpTopBorder  = (gs) => gs.CriticalCount>0?"#ef4444":gs.WarningCount>0?"#f59e0b":(gs.UnknownCount>0||gs.TotalCount===0)?"#d1d5db":"#22c55e";
  const grpWorstStatus = (gs) => gs.CriticalCount>0?"critical":gs.WarningCount>0?"warning":(gs.UnknownCount>0||gs.TotalCount===0)?"unknown":"healthy";
  const statusDotColor = { healthy:"#22c55e", warning:"#f59e0b", critical:"#ef4444", unknown:"#d1d5db" };
  const MON_TYPES = ["Server","Application","Database","Website","Synthetic"];

  // Ungrouped per-type breakdown — enabled dots first, disabled grey dots after
  const ugByType = MON_TYPES.map(t => {
    const en = ungroupedMonitors.filter(m => m.MonitorType === t && m.IsEnabled !== false && m.IsEnabled !== 0);
    const dis = ungroupedMonitors.filter(m => m.MonitorType === t && (m.IsEnabled === false || m.IsEnabled === 0));
    return { type: t, monitors: [...en, ...dis] };
  }).filter(x => x.monitors.length > 0);

  return (
    <div style={{ height:"calc(100vh - 20px)", display:"flex", flexDirection:"column", gap:0, overflow:"hidden" }}>

      {/* ── 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:18, fontWeight:600, color:"#1e293b" }}>
              <span style={{ color:"#006D8C" }}>OP1</span> Operations Portal
            </div>
            <div style={{ fontSize:11, color:"#94a3b8" }}>Enterprise Monitoring · v1.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:8 }}>
          <button onClick={handleAiPillClick}
            style={{ display:"flex", alignItems:"center", gap:5, background: aiSummaryOpen ? "#E0F2F7" : "transparent",
              border:"1px solid #006D8C", borderRadius:20, padding:"5px 12px", cursor:"pointer",
              color:"#006D8C", fontSize:12, fontWeight:600, transition:"background 0.15s" }}
            onMouseEnter={e=>{ if(!aiSummaryOpen) e.currentTarget.style.background="#E0F2F7"; }}
            onMouseLeave={e=>{ if(!aiSummaryOpen) e.currentTarget.style.background="transparent"; }}>
            ⚡ AI Summary
          </button>
          {mc.canManageConfig && mc.monCtrl && (
            <div style={{ position:"relative" }}>
              <button onClick={() => mc.setPausePopover(p => !p)}
                style={{ display:"flex", alignItems:"center", gap:6, background: mc.isPaused ? "rgba(180,83,9,0.08)" : "rgba(0,0,0,0.04)", border: `1px solid ${mc.isPaused?"rgba(180,83,9,0.3)":"rgba(0,0,0,0.1)"}`, borderRadius:20, padding:"5px 12px", cursor:"pointer", color: mc.isPaused ? "#B45309" : "#374151", fontSize:11, fontWeight:600 }}>
                <span style={{ width:7, height:7, borderRadius:"50%", background: mc.isPaused ? "#F59E0B" : "#10B981", flexShrink:0, animation: mc.isPaused ? "pulse 1.5s infinite" : "none" }} />
                {mc.isPaused ? "Monitoring Paused" : "Monitoring Active"}
              </button>
              {mc.pausePopover && (
                <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 }}>{mc.isPaused ? "Resume Monitoring" : "Pause Monitoring"}</div>
                  <div style={{ fontSize:11, color:c.textDim, marginBottom:10 }}>Machine: <span style={{ fontFamily:"monospace", color:c.text }}>{mc.currentCtrl?.MachineName}</span></div>
                  {!mc.isPaused && <textarea value={mc.pauseReason} onChange={e=>mc.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 }} />}
                  {mc.isPaused && mc.currentCtrl?.PauseReason && <div style={{ fontSize:11, color:c.textDim, marginBottom:8 }}>Reason: {mc.currentCtrl.PauseReason}</div>}
                  {mc.isPaused && mc.currentCtrl?.PausedAt && <div style={{ fontSize:10, color:c.textDimmer, marginBottom:10 }}>Paused at: {parseUTC(mc.currentCtrl.PausedAt)?.toLocaleString()}</div>}
                  <div style={{ display:"flex", gap:6 }}>
                    <button onClick={mc.togglePause} disabled={mc.pauseBusy} style={{ flex:1, padding:"6px 0", borderRadius:6, border:"none", cursor:"pointer", background: mc.isPaused ? "#16a34a" : "#dc2626", color:"#fff", fontSize:12, fontWeight:600 }}>
                      {mc.pauseBusy ? "..." : mc.isPaused ? "Resume" : "Pause"}
                    </button>
                    <button onClick={() => mc.setPausePopover(false)} 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", borderBottom:"0.5px solid #e2e5ea", height:28, flexShrink:0, background:"#ffffff" }}>
        {[
          ["TOTAL",          s.TotalMonitors ?? 0,  "#1e293b",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"all",expandAll:true}),      "View all monitors"],
          ["HEALTHY",        s.HealthyCount ?? 0,   "#16a34a",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"healthy",expandAll:true}),   "View healthy monitors"],
          ["WARNING",        s.WarningCount ?? 0,   "#d97706",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"warning",expandAll:true}),   "View warning monitors"],
          ["CRITICAL",       s.CriticalCount ?? 0,  s.CriticalCount > 0 ? "#dc2626" : "#1e293b", () => onNavigate && onNavigate(null,null,null,{statusFilter:"critical",expandAll:true}), "View critical monitors"],
          ["DISABLED",       s.UnknownCount ?? 0,   "#1e293b",  () => onNavigate && onNavigate(null,null,null,{statusFilter:"disabled",expandAll:true}),  "View disabled monitors"],
          ["AVG RESPONSE",   s.AvgResponseTimeMs != null ? `${Number(s.AvgResponseTimeMs).toFixed(0)}ms` : "—", "#1e293b", null, null],
          ["AVG UPTIME 30D", s.AvgUptime30Day != null ? `${Number(s.AvgUptime30Day).toFixed(1)}%` : "—", "#1e293b", null, null],
          ["OPEN ALERTS",    s.OpenAlertCount ?? 0, s.OpenAlertCount > 0 ? "#dc2626" : "#1e293b", () => onNavigateAlerts && onNavigateAlerts(), "View all alerts"],
          ["CHECKS/MIN",     s.ChecksPerSecond != null ? `${(Number(s.ChecksPerSecond)*60).toFixed(0)}` : "—", "#1e293b", null, null],
          ["INGEST LATENCY", s.IngestionLatencyMs != null ? `${Number(s.IngestionLatencyMs).toFixed(0)}ms` : "—", "#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 style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:5, paddingLeft:10, paddingRight:8, borderLeft:"0.5px solid #e2e5ea", flexShrink:0 }}>
          <span style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Refresh:</span>
          <select value={refreshInterval} onChange={handleIntervalChange}
            style={{ fontSize:10, border:"1px solid #e2e5ea", borderRadius:4, padding:"1px 4px", color:"#374151", background:"#f8fafc", cursor:"pointer" }}>
            <option value={10}>10s</option>
            <option value={30}>30s</option>
            <option value={60}>60s</option>
            <option value={300}>5min</option>
          </select>
          <button title="Refresh now" onClick={() => { setCountdown(refreshInterval); fetchData(); }}
            style={{ background:"none", border:"none", cursor:"pointer", padding:0, lineHeight:1, fontSize:14, color:"#64748b",
              opacity:refreshing?1:0.5, animation:refreshing?"spin 1s linear infinite":"none" }}>&#x27F3;</button>
          <span style={{ fontSize:10, color:flashUpdated?"#16a34a":refreshing?"#3b82f6":"#64748b" }}>
            {flashUpdated ? "✓" : refreshing ? "…" : `${countdown}s`}
          </span>
        </div>
      </div>

      {/* ── AI Summary panel ── */}
      {aiSummaryOpen && (
        <div style={{ flexShrink:0, background:"#fff", borderBottom:"1px solid #D8CCBA", padding:"16px 20px" }}>
          {/* Header row */}
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:12 }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <div style={{ width:24, height:24, borderRadius:4, background:"#1E2B3C", display:"flex", alignItems:"center", justifyContent:"center", fontSize:13, flexShrink:0 }}>⚡</div>
              <span style={{ fontSize:13, fontWeight:700, color:"#1E2B3C" }}>AI Dashboard Summary</span>
              {aiSummaryData?.generatedAt && (
                <span style={{ fontFamily:"'DM Mono',monospace", fontSize:10, color:"#94a3b8" }}>
                  Generated {new Date(aiSummaryData.generatedAt).toLocaleTimeString([], {hour:"2-digit", minute:"2-digit"})}
                </span>
              )}
            </div>
            <div style={{ display:"flex", alignItems:"center", gap:12 }}>
              <button onClick={fetchAiSummary} disabled={aiSummaryLoading}
                style={{ fontFamily:"'DM Mono',monospace", fontSize:11, color:"#006D8C", background:"none", border:"none", cursor:"pointer", textDecoration:"underline", padding:0 }}>
                {aiSummaryLoading ? "Refreshing…" : "Refresh"}
              </button>
              <button onClick={() => setAiSummaryOpen(false)}
                style={{ background:"none", border:"none", cursor:"pointer", fontSize:16, color:"#94a3b8", lineHeight:1, padding:0 }}>×</button>
            </div>
          </div>

          {/* Loading state */}
          {aiSummaryLoading && !aiSummaryData && (
            <div style={{ display:"flex", alignItems:"center", justifyContent:"center", gap:10, padding:"20px 0", color:"#64748b", fontSize:13 }}>
              <span style={{ display:"inline-block", width:16, height:16, border:"2px solid #e2e5ea", borderTopColor:"#006D8C", borderRadius:"50%", animation:"spin 1s linear infinite" }} />
              Analysing dashboard data…
            </div>
          )}

          {/* Error state */}
          {aiSummaryError && !aiSummaryLoading && (
            <div style={{ color:"#D95C5C", fontSize:12, padding:"8px 0" }}>⚠ {aiSummaryError}</div>
          )}

          {/* Loaded state */}
          {aiSummaryData && !aiSummaryLoading && (() => {
            const d = aiSummaryData;
            const dotCol = d.statusColour === "healthy" ? "#008C6F" : d.statusColour === "critical" ? "#D95C5C" : "#E89A2E";
            const healthy  = summary?.HealthyCount  ?? 0;
            const critical = summary?.CriticalCount ?? 0;
            const openAlerts = summary?.OpenAlertCount ?? 0;
            return (
              <div>
                {/* Stat pills row */}
                <div style={{ display:"flex", gap:10, marginBottom:12 }}>
                  {[
                    { label:"Overall Status", content: (
                      <span style={{ display:"flex", alignItems:"center", gap:6, fontSize:12, color:"#1E2B3C" }}>
                        <span style={{ width:8, height:8, borderRadius:"50%", background:dotCol, flexShrink:0 }} />
                        {d.overallStatus}
                      </span>
                    )},
                    { label:"Monitors", content: (
                      <span style={{ fontSize:12 }}>
                        <span style={{ color:"#008C6F", fontWeight:600 }}>{healthy} healthy</span>
                        {critical > 0 && <span style={{ color:"#D95C5C", fontWeight:600 }}> · {critical} critical</span>}
                      </span>
                    )},
                    { label:"Open Alerts", content: (
                      <span style={{ fontSize:12, fontWeight:600, color: openAlerts > 0 ? "#D95C5C" : "#008C6F" }}>
                        {openAlerts}
                      </span>
                    )},
                  ].map(pill => (
                    <div key={pill.label} style={{ flex:1, background:"#F5F0E8", border:"1px solid #D8CCBA", borderRadius:6, padding:"10px 12px" }}>
                      <div style={{ fontSize:9, fontWeight:600, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>{pill.label}</div>
                      {pill.content}
                    </div>
                  ))}
                </div>

                {/* Active Issues */}
                <div style={{ background:"#F5F0E8", border:"1px solid #D8CCBA", borderLeft:"3px solid #E89A2E", borderRadius:4, padding:"10px 12px", marginBottom:10 }}>
                  <div style={{ fontFamily:"'DM Mono',monospace", fontSize:9, fontWeight:600, color:"#E89A2E", textTransform:"uppercase", letterSpacing:"0.08em", marginBottom:6 }}>Active Issues</div>
                  {(!d.issues || d.issues.length === 0) ? (
                    <div style={{ fontSize:12, color:"#64748b" }}>No active issues detected — all monitors healthy</div>
                  ) : (
                    <div style={{ display:"flex", flexDirection:"column", gap:4 }}>
                      {d.issues.map((iss, i) => (
                        <div key={i} style={{ display:"flex", alignItems:"flex-start", gap:6, fontSize:12 }}>
                          <span style={{ color: iss.severity === "critical" ? "#D95C5C" : "#E89A2E", fontSize:13, lineHeight:1.3, flexShrink:0 }}>
                            {iss.severity === "critical" ? "!" : "~"}
                          </span>
                          <span><strong style={{ color:"#1E2B3C" }}>{iss.monitorName}</strong>{iss.groupName ? <span style={{ color:"#94a3b8" }}> · {iss.groupName}</span> : null} — {iss.description}</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                {/* Recommended Actions */}
                <div style={{ background:"#F5F0E8", border:"1px solid #D8CCBA", borderLeft:"3px solid #006D8C", borderRadius:4, padding:"10px 12px", marginBottom:10 }}>
                  <div style={{ fontFamily:"'DM Mono',monospace", fontSize:9, fontWeight:600, color:"#006D8C", textTransform:"uppercase", letterSpacing:"0.08em", marginBottom:6 }}>Recommended Actions</div>
                  <div style={{ display:"flex", flexDirection:"column", gap:3 }}>
                    {(d.recommendations || []).map((rec, i) => (
                      <div key={i} style={{ display:"flex", gap:6, fontSize:12, color:"#1E2B3C" }}>
                        <span style={{ fontFamily:"'DM Mono',monospace", color:"#006D8C", fontWeight:600, flexShrink:0 }}>{i+1}.</span>
                        {rec}
                      </div>
                    ))}
                  </div>
                </div>

                {/* Footer */}
                <div style={{ display:"flex", justifyContent:"flex-end", alignItems:"center", gap:5 }}>
                  <span style={{ width:6, height:6, borderRadius:"50%", background:"#006D8C", display:"inline-block", animation:"pulse 2s infinite" }} />
                  <span style={{ fontFamily:"'DM Mono',monospace", fontSize:10, color:"#94a3b8" }}>Powered by Claude · claude-haiku-4-5</span>
                </div>
              </div>
            );
          })()}
        </div>
      )}

      {/* ── Body: zone row + ticker ── */}
      <div style={{ flex:1, minHeight:0, display:"flex", flexDirection:"column", overflow:"hidden" }}>

        {/* ── Zone row ── */}
        <div style={{ flex:1, minHeight:0, display:"flex", overflow:"hidden" }}>

          {/* ── Zone Center: Monitor groups ── */}
          <div style={{ flex:1, minWidth:0, overflowY:"auto", padding:"8px 10px", borderRight:"0.5px solid #e2e5ea" }}>
            <div style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase", marginBottom:6 }}>Monitor Groups</div>
            <div style={{ display:"grid", gridTemplateColumns:"repeat(5,1fr)", gap:5, alignContent:"start" }}>
              {groupSummaries.map(gs => {
                const ws = grpWorstStatus(gs);
                const dc = statusDotColor[ws] || "#d1d5db";
                const grpMonitors = allMonitors.filter(m => m.GroupID === gs.GroupID);
                const typeRows = MON_TYPES.map(t => {
                  const en = grpMonitors.filter(m => m.MonitorType === t && m.IsEnabled !== false && m.IsEnabled !== 0);
                  const dis = grpMonitors.filter(m => m.MonitorType === t && (m.IsEnabled === false || m.IsEnabled === 0));
                  return { type: t, items: [...en, ...dis] };
                }).filter(x => x.items.length > 0);
                const sparkPts = computeSparkPoints(groupTrends[gs.GroupID] || []);
                return (
                  <div key={gs.GroupID}
                    onClick={() => onNavigate && onNavigate(gs.GroupID)}
                    style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:7, borderTop:`3px solid ${grpTopBorder(gs)}`, padding:"6px 9px", minWidth:0, cursor:"pointer", display:"flex", flexDirection:"column" }}
                    onMouseEnter={e => e.currentTarget.style.background="#f8fafc"}
                    onMouseLeave={e => e.currentTarget.style.background="#ffffff"}>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", flexWrap:"wrap", gap:2, marginBottom:4 }}>
                      <span style={{ fontSize:11, fontWeight:600, color:"#1e293b", minWidth:0, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{gs.GroupName}</span>
                      <div style={{ display:"flex", alignItems:"center", gap:3, flexShrink:0 }}>
                        <span style={{ width:6, height:6, borderRadius:"50%", background:dc }} />
                        <span style={{ fontSize:9, color:dc, fontWeight:600, textTransform:"uppercase" }}>{ws}</span>
                      </div>
                    </div>
                    <div style={{ flex:1 }}>
                    <div style={{ borderBottom:"0.5px solid #f1f5f9", margin:"4px 0" }} />
                    {typeRows.map(({ type, items }) => (
                      <div key={type} style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                        <span style={{ fontSize:10, color:"#64748b" }}>{type}</span>
                        <div style={{ display:"flex", gap:2 }}>
                          {items.map((m, idx) => {
                            const isDisabledMon = m.IsEnabled === false;
                            const st = isDisabledMon ? "disabled" : (m.EffectiveStatus || m.CurrentStatus || "unknown");
                            return (
                              <span key={idx}
                                title={`${m.MonitorName} — ${st}`}
                                onClick={ev => { ev.stopPropagation(); onNavigate && onNavigate(null, m.MonitorID, m); }}
                                style={{ width:7, height:7, borderRadius:"50%", cursor:"pointer",
                                  background: isDisabledMon?"#9CA3AF":st==="healthy"?"#22c55e":st==="warning"?"#f59e0b":st==="critical"?"#ef4444":"#9CA3AF" }} />
                            );
                          })}
                        </div>
                      </div>
                    ))}
                    {gs.SyntheticFlows && gs.SyntheticFlows.length > 0 && (
                      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                        <span style={{ fontSize:10, color:"#64748b" }}>Synthetic</span>
                        <div style={{ display:"flex", gap:2 }}>
                          {gs.SyntheticFlows.map((f, idx) => {
                            const dotColor = (!f.IsEnabled || f.LastRunPassed == null) ? "#9CA3AF" : !f.LastRunPassed ? "#ef4444" : (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs) ? "#ef4444" : (f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs) ? "#F59E0B" : "#22c55e";
                            const label = !f.IsEnabled ? "disabled" : f.LastRunPassed == null ? "no runs" : !f.LastRunPassed ? "failing" : (f.DurationCritMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationCritMs) ? "slow (critical)" : (f.DurationWarnMs > 0 && f.LastDurationMs != null && f.LastDurationMs >= f.DurationWarnMs) ? "slow (warning)" : "passing";
                            return (
                              <span key={idx}
                                title={`${f.FlowName} — ${label}`}
                                style={{ width:7, height:7, borderRadius:"50%", background:dotColor }} />
                            );
                          })}
                        </div>
                      </div>
                    )}
                    <div style={{ width:"100%", height:24, margin:"4px 0" }}>
                      <svg viewBox="0 0 120 24" width="100%" height="100%" preserveAspectRatio="none">
                        {sparkPts
                          ? <polyline points={sparkPts} fill="none" stroke={dc} strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
                          : <line x1="0" y1="12" x2="120" y2="12" stroke="#e2e5ea" strokeWidth="1.5" />}
                      </svg>
                    </div>
                    </div>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginTop:"auto" }}>
                      <span style={{ fontSize:9, color:"#94a3b8" }}>
                        {runningGroup === gs.GroupID
                          ? `Running ${grpMonitors.length} check${grpMonitors.length!==1?"s":""}…`
                          : `${grpMonitors.length} monitor${grpMonitors.length!==1?"s":""} · Click to view`}
                      </span>
                      <div style={{ display:"flex", gap:4 }} onClick={e=>e.stopPropagation()}>
                        <button
                          disabled={runningGroup !== null}
                          onClick={async (e) => {
                            e.stopPropagation();
                            setRunningGroup(gs.GroupID);
                            try {
                              await api("POST", `/monitors/groups/${gs.GroupID}/run`);
                              fetchData();
                            } catch(_) {}
                            finally { setRunningGroup(null); }
                          }}
                          style={{
                            fontSize:9, padding:"1px 6px", border:"0.5px solid #94a3b8", borderRadius:3,
                            background:"#ffffff", color:"#64748b", cursor: runningGroup !== null ? "not-allowed" : "pointer",
                            opacity: runningGroup !== null ? 0.5 : 1, lineHeight:1.6,
                          }}
                          title="Run all checks in this group now"
                        >▶ Run All</button>
                      </div>
                    </div>
                  </div>
                );
              })}
              {hasUngrouped && (() => {
                const ugSparkPts = computeSparkPoints(groupTrends[0] || []);
                return (
                  <div onClick={() => onNavigate && onNavigate("ungrouped")}
                    style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:7, borderTop:"2px dashed #d1d5db", padding:"6px 9px", minWidth:0, cursor:"pointer", display:"flex", flexDirection:"column" }}
                    onMouseEnter={e => e.currentTarget.style.background="#f8fafc"}
                    onMouseLeave={e => e.currentTarget.style.background="#ffffff"}>
                    <div style={{ fontSize:11, fontWeight:600, color:"#64748b", marginBottom:4 }}>Ungrouped</div>
                    <div style={{ flex:1 }}>
                    <div style={{ borderBottom:"0.5px solid #f1f5f9", margin:"4px 0" }} />
                    {ugByType.slice(0,4).map(({ type, monitors }) => (
                      <div key={type} style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:2 }}>
                        <span style={{ fontSize:10, color:"#64748b" }}>{type}</span>
                        <div style={{ display:"flex", gap:2 }}>
                          {monitors.map((m, idx) => {
                            const isDisabledMon = m.IsEnabled === false;
                            const st = isDisabledMon ? "disabled" : (m.EffectiveStatus || m.CurrentStatus || "unknown");
                            return (
                              <span key={idx}
                                title={`${m.MonitorName} — ${st}`}
                                onClick={ev => { ev.stopPropagation(); onNavigate && onNavigate(null, m.MonitorID, m); }}
                                style={{ width:7, height:7, borderRadius:"50%", cursor:"pointer",
                                  background: isDisabledMon?"#9CA3AF":st==="healthy"?"#22c55e":st==="warning"?"#f59e0b":st==="critical"?"#ef4444":"#9CA3AF" }} />
                            );
                          })}
                        </div>
                      </div>
                    ))}
                    <div style={{ width:"100%", height:24, margin:"4px 0" }}>
                      <svg viewBox="0 0 120 24" width="100%" height="100%" preserveAspectRatio="none">
                        {ugSparkPts
                          ? <polyline points={ugSparkPts} fill="none" stroke="#d1d5db" strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
                          : <line x1="0" y1="12" x2="120" y2="12" stroke="#e2e5ea" strokeWidth="1.5" />}
                      </svg>
                    </div>
                    </div>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginTop:"auto" }} onClick={e=>e.stopPropagation()}>
                      <span style={{ fontSize:9, color:"#94a3b8" }}>
                        {runningGroup === "ungrouped"
                          ? `Running ${ungroupedMonitors.length} check${ungroupedMonitors.length!==1?"s":""}…`
                          : `${ungroupedMonitors.length} monitor${ungroupedMonitors.length!==1?"s":""} · Click to view`}
                      </span>
                      <button
                        disabled={runningGroup !== null}
                        onClick={async (e) => {
                          e.stopPropagation();
                          setRunningGroup("ungrouped");
                          try {
                            for (const m of ungroupedMonitors) {
                              await api("POST", `/monitors/${m.MonitorID}/check`);
                            }
                            fetchData();
                          } catch(_) {}
                          finally { setRunningGroup(null); }
                        }}
                        style={{
                          fontSize:9, padding:"1px 6px", border:"0.5px solid #94a3b8", borderRadius:3,
                          background:"#ffffff", color:"#64748b", cursor: runningGroup !== null ? "not-allowed" : "pointer",
                          opacity: runningGroup !== null ? 0.5 : 1, lineHeight:1.6,
                        }}
                        title="Run all checks for ungrouped monitors now"
                      >&#x25B6; Run All</button>
                    </div>
                  </div>
                );
              })()}
            </div>
          </div>

          {/* ── Zone Right: Health overview ── */}
          <div style={{ width:320, flexShrink:0, display:"flex", flexDirection:"column", overflow:"hidden", padding:"8px 10px", gap:6 }}>
            <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0 }}>
              <span style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase" }}>Health Overview</span>
              <button onClick={() => setOpenPicker(openPicker === 0 ? null : 0)}
                style={{ fontSize:9, color:"#94a3b8", background:"none", border:"none", cursor:"pointer", padding:"2px 4px" }}>
                &#x229E; Customize
              </button>
            </div>
            {slots.map((chartType, slotIdx) => {
              const libEntry = CHART_LIBRARY.find(x => x.id === chartType) || { label: chartType };
              const isDonut = chartType === "status-donut";
              const pickerOpen = openPicker === slotIdx;
              return (
                <div key={slotIdx} data-chartpicker style={{ flex:1, minHeight:0, background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:"6px 8px", display:"flex", flexDirection:"column", position:"relative", overflow:"hidden" }}>
                  <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:4, flexShrink:0 }}>
                    <span style={{ fontSize:9, fontWeight:600, color:"#64748b", letterSpacing:"0.08em", textTransform:"uppercase" }}>{libEntry.label}</span>
                    <button onClick={() => setOpenPicker(pickerOpen ? null : slotIdx)}
                      style={{ fontSize:9, color:"#94a3b8", background:"none", border:"none", cursor:"pointer", padding:"2px 4px", borderRadius:3 }}>
                      &#x229E;
                    </button>
                  </div>
                  {pickerOpen && (
                    <div style={{ position:"absolute", top:28, right:0, zIndex:100, width:200, background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, boxShadow:"0 4px 16px rgba(0,0,0,0.12)", padding:8 }}>
                      {CHART_LIBRARY.map(lib => {
                        const available = hasData(lib.id);
                        const selected = slots[slotIdx] === lib.id;
                        return (
                          <div key={lib.id} onClick={() => available && changeSlot(slotIdx, lib.id)}
                            style={{ padding:"6px 8px", borderRadius:4, cursor:available?"pointer":"default",
                              background:selected?"#f0f9ff":"transparent", color:available?"#1e293b":"#94a3b8",
                              display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                            <span style={{ fontSize:12 }}>{lib.label}</span>
                            {!available && <span style={{ fontSize:10, color:"#94a3b8" }}>No data</span>}
                          </div>
                        );
                      })}
                    </div>
                  )}
                  {isDonut ? (
                    <div style={{ flex:1, minHeight:0, display:"flex", alignItems:"center", gap:8, overflow:"hidden" }}>
                      <div style={{ width:70, height:70, flexShrink:0, position:"relative" }}>
                        <canvas ref={canvasRefs[slotIdx]} style={{ display:"block", width:"100%", height:"100%" }} />
                        {summary && (
                          <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center", pointerEvents:"none" }}>
                            <div style={{ textAlign:"center" }}>
                              <div style={{ fontSize:14, fontWeight:600, color:"#1e293b", lineHeight:1 }}>{s.TotalMonitors ?? 0}</div>
                              <div style={{ fontSize:8, color:"#94a3b8" }}>total</div>
                            </div>
                          </div>
                        )}
                      </div>
                      <div style={{ flex:1, minWidth:0 }}>
                        {[["Healthy",s.HealthyCount??0,"#86efac"],["Warning",s.WarningCount??0,"#fcd34d"],["Critical",s.CriticalCount??0,"#fca5a5"],["Disabled",s.UnknownCount??0,"#e2e8f0"]].map(([lbl,val,col]) => (
                          <div key={lbl} style={{ display:"flex", alignItems:"center", gap:4, marginBottom:3 }}>
                            <span style={{ width:6, height:6, borderRadius:"50%", background:col, flexShrink:0 }} />
                            <span style={{ fontSize:10, color:"#64748b", flex:1 }}>{lbl}</span>
                            <span style={{ fontSize:10, fontWeight:600, color:"#1e293b" }}>{val}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  ) : (
                    <div style={{ flex:1, minHeight:0, position:"relative", paddingBottom:4 }}>
                      <canvas ref={canvasRefs[slotIdx]} style={{ display:"block", width:"100%", height:"100%" }} />
                      {!hasData(chartType) && (
                        <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center", fontSize:11, color:"#94a3b8" }}>No data yet</div>
                      )}
                    </div>
                  )}
                </div>
              );
            })}
          </div>

        </div>

        {/* ── News ticker ── */}
        <div style={{ height:28, background:"#1e2b3c", overflow:"hidden", display:"flex", alignItems:"center", flexShrink:0 }}>
          {recentAlerts.length > 0 ? (
            <div style={{ display:"inline-flex", animation:"op1-ticker 35s 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.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>

    </div>
  );
}

// ═══ TOGGLE ═══
const Toggle = ({ label, checked, onChange }) => (
  <label style={{ display:"flex", alignItems:"center", gap:8, cursor:"pointer", userSelect:"none" }}>
    <div onClick={()=>onChange(!checked)}
      style={{ width:34, height:18, borderRadius:9, background:checked?c.blue:c.border, position:"relative", transition:"background 0.2s", cursor:"pointer", flexShrink:0 }}>
      <div style={{ position:"absolute", top:2, left:checked?16:2, width:14, height:14, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
    </div>
    <span style={{ fontSize:12, color:c.textMuted }}>{label}</span>
  </label>
);

// ═══ MONITOR FORM PAGE ═══
const FREQ_OPTS = [
  {value:60,label:"1 min"},{value:300,label:"5 min"},{value:600,label:"10 min"},
  {value:900,label:"15 min"},{value:1800,label:"30 min"},{value:3600,label:"60 min"},
];
const NOTIF_METHODS = ["email","slack","teams","pagerduty","opsgenie","sms","webhook"];
const NOTIF_PLACEHOLDER = {
  email:"alerts@company.com", slack:"https://hooks.slack.com/services/…",
  teams:"https://outlook.office.com/webhook/…", webhook:"https://your-endpoint.com/hook",
  pagerduty:"integration-key", opsgenie:"api-key", sms:"+1-555-000-0000",
};

const BLANK_MONITOR = {
  MonitorName:"", MonitorType:"Server", IPAddressOrUrl:"", Port:"", Protocol:"HTTPS",
  CheckFrequencySeconds:300, FailureThreshold:3, TimeoutMs:5000, ResponseTimeThresholdMs:2000,
  GroupID:"", GroupName:"", ParentMonitorID:"",
  EnableAnomalyDetection:false, CheckSSLCertificate:false, SSLCertExpiryWarningDays:30,
  EscalationAfterAlerts:2, EscalationRepeatMinutes:60,
  RunbookUrl:"", RunbookNotes:"",
};

// ── TestResultPane — inline, no modal ───────────────────────────────────────
function TestResultPane({ runs, meta }) {

  if (!runs || runs.length === 0) {
    return (
      <div style={{ display:"flex",alignItems:"center",justifyContent:"center",height:"100%",color:c.textDimmer,fontSize:13 }}>
        Click <strong style={{ color:c.textMuted,margin:"0 5px" }}>Test Monitor</strong> to run a check. Results will appear here.
      </div>
    );
  }

  const latest  = runs[0];
  const history = runs.slice(1);
  const sev     = (latest.Severity || (latest.IsSuccess ? "healthy" : "failed")).toLowerCase();
  const ts      = latest.Timestamp ? parseUTC(latest.Timestamp)?.toLocaleString() : "—";

  let sevColor = sev === "healthy" ? c.green : sev === "warning" ? "#E89A2E" : c.red;
  let sevIcon  = sev === "healthy" ? "✅" : sev === "warning" ? "⚠" : "❌";
  let sevLabel = sev === "healthy"  ? "Test Passed"
               : sev === "warning"  ? `Warning — ${latest.ErrorMessage || "threshold breached"}`
               : sev === "critical" ? `Critical — ${latest.ErrorMessage || "threshold exceeded"}`
               : `Test Failed`;

  // ── Protocol label ──────────────────────────────────────────────────────
  const proto       = (latest.Protocol || "").toUpperCase();
  const monSubLower = (meta?.monitorSubType || "").toLowerCase();
  const cmLower     = (meta?.typeConfig?.CheckMethod || "").toLowerCase();
  let protocolLabel = null;
  if (proto === "WMI" || monSubLower.includes("wmi") || cmLower === "wmi") protocolLabel = "WMI/DCOM";
  else if (proto === "ICMP" || monSubLower === "infra-ping")               protocolLabel = "ICMP";
  else if (proto === "TCP"  || monSubLower === "infra-tcp" || monSubLower === "app-port") protocolLabel = "TCP";
  else if (proto === "DNS"  || monSubLower === "web-dns")                  protocolLabel = "DNS";
  else if (proto === "SNMP")                                               protocolLabel = "SNMP";
  else if (proto === "SSH")                                                protocolLabel = "SSH";
  else if (proto === "HTTP" || proto === "HTTPS")                          protocolLabel = proto;
  else if (proto)                                                          protocolLabel = proto;

  // ── Credentials ─────────────────────────────────────────────────────────
  const credUser = meta?.credentialUsername || null;
  const credName = meta?.credentialName     || null;

  // ── Stat bar ────────────────────────────────────────────────────────────
  const statItems = [
    { label:"TARGET",           value: latest.Target || meta?.monitorName || "—", mono:true, accent:true },
    { label:"METHOD",           value: protocolLabel || "—",                       mono:false, accent:true },
    ...(credUser ? [{ label:"LOGIN AS",  value: credUser,  mono:true,  accent:true }] : []),
    { label:"CREDENTIALS USED", value: credName || (credUser ? "Custom (inline)" : "None"), mono:false, accent:false },
  ];
  const statBar = (
    <div style={{ display:"flex", flexWrap:"nowrap", borderBottom:`1px solid ${c.border}`, background:c.surfaceAlt, overflowX:"auto" }}>
      {statItems.map((item, i) => (
        <div key={i} style={{ padding:"4px 16px", borderRight: i < statItems.length-1 ? `0.5px solid ${c.border}` : "none", flexShrink:0 }}>
          <div style={{ fontSize:10, fontWeight:600, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:2 }}>{item.label}</div>
          <div style={{ fontSize:13, fontWeight:500, color: item.accent ? c.blue : c.text, fontFamily: item.mono ? "monospace" : "inherit", whiteSpace:"nowrap" }}>{item.value}</div>
        </div>
      ))}
    </div>
  );

  // ── Thresholds ───────────────────────────────────────────────────────────
  const tc2       = meta?.typeConfig || {};
  const warnThr   = parseFloat(tc2.WarnThreshold || tc2.CpuWarnPercent || tc2.MemWarnPercent || tc2.DiskWarnPercent || 80);
  const critThr   = parseFloat(tc2.CritThreshold || tc2.CpuCritPercent || tc2.MemCritPercent || tc2.DiskCritPercent || 95);
  const timeoutMs = meta?.timeoutMs || 5000;
  const rtMs      = latest.ResponseTimeMs || 0;
  const rtPct     = timeoutMs > 0 ? Math.round((rtMs / timeoutMs) * 100) : 0;
  const rtFillColor = rtPct > 80 ? c.red : rtPct > 40 ? "#E89A2E" : c.green;

  // ── Primary metric ───────────────────────────────────────────────────────
  const pctMatch  = (latest.ErrorMessage || "").match(/(\d+(?:\.\d+)?)%/);
  const metricPct = pctMatch ? parseFloat(pctMatch[1]) : null;

  // ── infra-diskio message parsing ──────────────────────────────────────────
  const diskIoMsg    = (monSubLower === "infra-diskio") ? (latest.ErrorMessage || "") : "";
  const diskIoRead   = diskIoMsg.match(/read=(\d+)KB\/s/);
  const diskIoWrite  = diskIoMsg.match(/write=(\d+)KB\/s/);
  const diskIoBusy   = diskIoMsg.match(/busy=(\d+)%/);
  const diskIoInst   = diskIoMsg.match(/on (.+)$/);
  const diskIoReadKB = diskIoRead  ? parseInt(diskIoRead[1],  10) : null;
  const diskIoWriteKB= diskIoWrite ? parseInt(diskIoWrite[1], 10) : null;
  const diskIoBusyPct= diskIoBusy  ? parseInt(diskIoBusy[1],  10) : null;
  const diskIoName   = diskIoInst  ? diskIoInst[1].trim()         : null;

  // ── infra-net message parsing ─────────────────────────────────────────────
  const netMsg    = (monSubLower === "infra-net") ? (latest.ErrorMessage || "") : "";
  const netSentM  = netMsg.match(/sent=(\d+)KB\/s/);
  const netRecvM  = netMsg.match(/recv=(\d+)KB\/s/);
  const netBwM    = netMsg.match(/bw=([\d.]+)Mbps/);
  const netIfaceM = netMsg.match(/on (.+)$/);
  const netSentKB = netSentM  ? parseInt(netSentM[1],  10)   : null;
  const netRecvKB = netRecvM  ? parseInt(netRecvM[1],  10)   : null;
  const netBwMbps = netBwM    ? parseFloat(netBwM[1])         : null;
  const netIface  = netIfaceM ? netIfaceM[1].trim()           : null;

  let primaryLabel = "CHECK RESULT";
  let primaryValue = null;
  let primaryUnit  = "";
  let primarySub   = null;

  if (["infra-cpu","infra-memory","infra-mem","infra-disk"].includes(monSubLower)) {
    primaryLabel = monSubLower === "infra-cpu" ? "CPU UTILIZATION"
                 : monSubLower === "infra-disk" ? "DISK UTILIZATION"
                 : "MEMORY UTILIZATION";
    primaryValue = metricPct;
    primaryUnit  = "%";
    if (primaryValue !== null) {
      if (primaryValue >= critThr)          primarySub = "Critical threshold exceeded";
      else if (primaryValue >= warnThr)     primarySub = "Above warning threshold";
      else if (primaryValue >= warnThr*0.8) primarySub = "Approaching warning threshold";
      else                                  primarySub = "Well below warning threshold";
    }
  } else if (["web-http","web-https","web-content"].includes(monSubLower) || proto === "HTTP" || proto === "HTTPS") {
    primaryLabel = "HTTP STATUS";
    primaryValue = latest.StatusCode != null ? latest.StatusCode : (latest.IsSuccess ? 200 : null);
    primarySub   = latest.IsSuccess ? `Expected: ${latest.StatusCode || 200}` : "Unexpected status code";
  } else if (["db-query","db-conn","db-sqlctr"].includes(monSubLower)) {
    primaryLabel = "QUERY RESULT";
    primaryValue = latest.QueryResult?.RowCount ?? (latest.IsSuccess ? 1 : null);
    primaryUnit  = " rows";
    primarySub   = latest.IsSuccess ? "Query executed successfully" : "Query failed";
  } else {
    primarySub = latest.IsSuccess
      ? (latest.ErrorMessage ? latest.ErrorMessage.slice(0,40) : "Connected")
      : null;
  }

  // ── Metrics gauges (success only) ────────────────────────────────────────
  const showGauge     = ["infra-cpu","infra-memory","infra-mem","infra-disk"].includes(monSubLower) && primaryValue !== null;
  const gaugeClamp    = v => Math.max(0, Math.min(100, v));
  const gaugeFill     = primaryValue != null
    ? (primaryValue >= critThr ? c.red : primaryValue >= warnThr ? "#E89A2E" : c.green)
    : c.green;

  const metricsGrid = latest.IsSuccess ? (
    <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", borderTop:`1px solid ${c.border}`, borderBottom:`1px solid ${c.border}` }}>
      <div style={{ padding:"10px 16px", borderRight:`1px solid ${c.border}` }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>{primaryLabel}</div>
        {primaryValue != null ? (
          <div>
            <div style={{ fontSize:22, fontWeight:700, color:c.text, lineHeight:1.2 }}>{typeof primaryValue === "number" ? primaryValue.toLocaleString() : primaryValue}<span style={{ fontSize:13, fontWeight:500, color:c.textDimmer }}>{primaryUnit}</span></div>
            {showGauge && (
              <div style={{ position:"relative", height:8, background:"#E5E7EB", borderRadius:4, marginTop:8, marginBottom:4 }}>
                <div style={{ position:"absolute", left:0, top:0, height:"100%", width:gaugeClamp(primaryValue)+"%", background:gaugeFill, borderRadius:4, transition:"width 0.4s ease" }} />
                <div style={{ position:"absolute", left:gaugeClamp(warnThr)+"%", top:-3, bottom:-3, width:2, background:"#E89A2E", borderRadius:1 }} />
                <div style={{ position:"absolute", left:gaugeClamp(critThr)+"%", top:-3, bottom:-3, width:2, background:c.red, borderRadius:1 }} />
              </div>
            )}
            {primarySub && <div style={{ fontSize:10, color:c.textDimmer, marginTop:2 }}>{primarySub}</div>}
          </div>
        ) : (
          <div style={{ fontSize:13, color:c.text, marginTop:4 }}>{primarySub || "Check passed"}</div>
        )}
      </div>
      <div style={{ padding:"10px 16px" }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>RESPONSE TIME</div>
        <div style={{ fontSize:22, fontWeight:700, color:c.text, lineHeight:1.2 }}>{rtMs.toLocaleString()}<span style={{ fontSize:13, fontWeight:500, color:c.textDimmer }}>ms</span></div>
        <div style={{ position:"relative", height:8, background:"#E5E7EB", borderRadius:4, marginTop:8, marginBottom:4 }}>
          <div style={{ position:"absolute", left:0, top:0, height:"100%", width:Math.min(100,rtPct)+"%", background:rtFillColor, borderRadius:4, transition:"width 0.4s ease" }} />
          <div style={{ position:"absolute", left:"40%", top:-3, bottom:-3, width:2, background:"#E89A2E", borderRadius:1 }} />
          <div style={{ position:"absolute", left:"80%", top:-3, bottom:-3, width:2, background:c.red, borderRadius:1 }} />
        </div>
        <div style={{ display:"flex", justifyContent:"space-between", fontSize:9, color:c.textDimmer }}>
          <span>fast</span><span>slow</span><span>{timeoutMs.toLocaleString()}ms</span>
        </div>
        <div style={{ fontSize:10, color:c.textDimmer, marginTop:2 }}>{rtPct}% of {timeoutMs.toLocaleString()}ms timeout budget</div>
      </div>
    </div>
  ) : null;

  // ── Disk I/O metrics grid (infra-diskio, success only) ──────────────────
  const diskIoMetricsGrid = (monSubLower === "infra-diskio" && latest.IsSuccess && diskIoReadKB !== null) ? (
    <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", borderTop:`1px solid ${c.border}`, borderBottom:`1px solid ${c.border}` }}>
      <div style={{ padding:"10px 16px", borderRight:`1px solid ${c.border}` }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>READ</div>
        <div style={{ fontSize:20, fontWeight:700, color:c.text }}>{diskIoReadKB != null ? diskIoReadKB.toLocaleString() : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}> KB/s</span></div>
      </div>
      <div style={{ padding:"10px 16px", borderRight:`1px solid ${c.border}` }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>WRITE</div>
        <div style={{ fontSize:20, fontWeight:700, color:c.text }}>{diskIoWriteKB != null ? diskIoWriteKB.toLocaleString() : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}> KB/s</span></div>
      </div>
      <div style={{ padding:"10px 16px" }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>BUSY</div>
        <div style={{ fontSize:20, fontWeight:700, color: diskIoBusyPct != null && diskIoBusyPct >= 95 ? c.red : diskIoBusyPct != null && diskIoBusyPct >= 80 ? "#E89A2E" : c.text }}>
          {diskIoBusyPct != null ? diskIoBusyPct : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}>%</span>
        </div>
        {diskIoName && <div style={{ fontSize:10, color:c.textDimmer, marginTop:4 }}>{diskIoName}</div>}
      </div>
    </div>
  ) : null;

  // ── Network metrics grid (infra-net, success only) ───────────────────────
  const netMetricsGrid = (monSubLower === "infra-net" && latest.IsSuccess && netSentKB !== null) ? (
    <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", borderTop:`1px solid ${c.border}`, borderBottom:`1px solid ${c.border}` }}>
      <div style={{ padding:"10px 16px", borderRight:`1px solid ${c.border}` }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>SENT</div>
        <div style={{ fontSize:20, fontWeight:700, color:c.text }}>{netSentKB != null ? netSentKB.toLocaleString() : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}> KB/s</span></div>
      </div>
      <div style={{ padding:"10px 16px", borderRight:`1px solid ${c.border}` }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>RECV</div>
        <div style={{ fontSize:20, fontWeight:700, color:c.text }}>{netRecvKB != null ? netRecvKB.toLocaleString() : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}> KB/s</span></div>
      </div>
      <div style={{ padding:"10px 16px" }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>BANDWIDTH</div>
        <div style={{ fontSize:20, fontWeight:700, color:c.text }}>{netBwMbps != null ? netBwMbps.toFixed(1) : "—"}<span style={{ fontSize:12, fontWeight:500, color:c.textDimmer }}> Mbps</span></div>
        {netIface && <div style={{ fontSize:10, color:c.textDimmer, marginTop:4 }}>{netIface}</div>}
      </div>
    </div>
  ) : null;

  // ── Error / detail box ───────────────────────────────────────────────────
  // Always shown when ErrorMessage is present; color driven by severity so
  // infra-log / infra-hw healthy detail strings render in neutral style.
  const errorBox = latest.ErrorMessage ? (
    <div style={{ margin:"10px 14px", background: sev === "failed" || sev === "critical" ? c.redLight : sev === "warning" ? "#FFF8E7" : "#F0F7FB", border:`1px solid ${sev === "failed" || sev === "critical" ? c.red : sev === "warning" ? "#E89A2E" : "#7A9AB8"}40`, borderRadius:6, padding:"10px 14px", fontFamily:"monospace", fontSize:12, color: sev === "failed" || sev === "critical" ? c.red : sev === "warning" ? "#B45309" : "#2C4A6E", wordBreak:"break-all", whiteSpace:"pre-wrap" }}>
      {latest.ErrorMessage}
    </div>
  ) : null;

  // ── Threshold checklist ──────────────────────────────────────────────────
  const checks = [];
  if (["infra-cpu","infra-memory","infra-mem","infra-disk"].includes(monSubLower) && primaryValue != null) {
    const dot = primaryValue >= critThr ? c.red : primaryValue >= warnThr ? "#E89A2E" : c.green;
    checks.push({ dot, label:`${primaryLabel.replace(" UTILIZATION","")} ${primaryValue}% — ${(primarySub||"").toLowerCase()}`, right:`warn at ${warnThr}%` });
  }
  if (monSubLower === "infra-diskio" && diskIoBusyPct !== null) {
    const dot = diskIoBusyPct >= 95 ? c.red : diskIoBusyPct >= 80 ? "#E89A2E" : c.green;
    checks.push({ dot, label:`Disk busy ${diskIoBusyPct}% — ${diskIoBusyPct >= 95 ? "critical" : diskIoBusyPct >= 80 ? "above warning" : "normal"}`, right:`read=${diskIoReadKB}KB/s write=${diskIoWriteKB}KB/s` });
  }
  if (monSubLower === "infra-net" && netSentKB !== null) {
    const totalKBs = (netSentKB || 0) + (netRecvKB || 0);
    const bwCapKBs = netBwMbps ? netBwMbps * 125 : 0;
    const pctUsed  = bwCapKBs > 0 ? Math.round(totalKBs * 100 / bwCapKBs) : 0;
    const dot = pctUsed >= 90 ? c.red : pctUsed >= 70 ? "#E89A2E" : c.green;
    checks.push({ dot, label:`Network ${pctUsed}% utilization — sent=${netSentKB}KB/s recv=${netRecvKB}KB/s`, right:netBwMbps ? `${netBwMbps.toFixed(1)}Mbps link` : "" });
  }
  if (monSubLower === "infra-svc" && latest.ErrorMessage) {
    const isRunning = latest.IsSuccess;
    checks.push({
      dot: isRunning ? c.green : c.red,
      label: latest.ErrorMessage,
      right: isRunning ? "running" : "stopped"
    });
  }
  if (monSubLower === "infra-proc" && latest.ErrorMessage) {
    checks.push({
      dot: latest.IsSuccess ? c.green : c.red,
      label: latest.ErrorMessage,
      right: latest.IsSuccess ? "running" : "not found"
    });
  }
  // ── Website subtype-specific checks ────────────────────────────────────
  if (monSubLower === "web-cm" && latest.ContentMatchResult) {
    const passed = latest.ContentMatchResult.startsWith("Passed");
    checks.push({
      dot: passed ? c.green : c.red,
      label: latest.ContentMatchResult,
      right: passed ? "match found" : "no match"
    });
  }
  if (monSubLower === "web-perf" && latest.TtfbMs != null) {
    const ttfbWarn = parseInt(meta?.typeConfig?.TtfbWarnMs) || 500;
    const flWarn   = parseInt(meta?.typeConfig?.FullLoadWarnMs) || 3000;
    const flCrit   = parseInt(meta?.typeConfig?.FullLoadCritMs) || 6000;
    const ttfbDot  = latest.TtfbMs >= ttfbWarn ? "#E89A2E" : c.green;
    const flDot    = latest.FullLoadMs >= flCrit ? c.red : latest.FullLoadMs >= flWarn ? "#E89A2E" : c.green;
    checks.push({ dot: ttfbDot, label: `TTFB: ${latest.TtfbMs}ms`, right: `warn at ${ttfbWarn}ms` });
    checks.push({ dot: flDot, label: `Full load: ${latest.FullLoadMs}ms`, right: `warn ${flWarn}ms / crit ${flCrit}ms` });
  }
  if (monSubLower === "web-headers" && latest.HeaderName) {
    const found = latest.HeaderFound;
    checks.push({
      dot: found ? c.green : "#E89A2E",
      label: found
        ? `Header found: ${latest.HeaderName}: ${latest.HeaderValue || "(present)"}`
        : `Header not found: ${latest.HeaderName}`,
      right: found ? "header present" : "header missing"
    });
  }
  if (monSubLower === "web-apiresp" && latest.ValidationPassed != null) {
    checks.push({
      dot: latest.ValidationPassed ? c.green : "#E89A2E",
      label: `JSONPath result: ${latest.JsonPathResult || "(not found)"}`,
      right: latest.ValidationPassed ? "validation passed" : "validation failed"
    });
  }
  // General ContentMatchResult fallback — fires for web-http and any website subtype
  // where the backend sets ContentMatchResult but no subtype-specific row handled it yet.
  // web-cm already has a detailed row above; web-dns uses ContentMatchResult for IP checks.
  if (latest.ContentMatchResult === "Passed" || latest.ContentMatchResult === "Failed") {
    const alreadyHandled = monSubLower === "web-cm" || monSubLower === "web-dns";
    if (!alreadyHandled) {
      const cmLabel = monSubLower === "web-headers"  ? "Header check"
                    : monSubLower === "web-apiresp"  ? "API validation"
                    : "Content match";
      const cmPassed = latest.ContentMatchResult === "Passed";
      const cmFailDetail = !cmPassed && latest.ErrorMessage ? ` — ${latest.ErrorMessage}` : "";
      checks.push({
        dot:   cmPassed ? c.green : c.red,
        label: `${cmLabel}: ${latest.ContentMatchResult}${cmFailDetail}`,
        right: cmPassed ? "passed" : "failed"
      });
    }
  }
  if (monSubLower === "web-ssl") {
    const daysM = (latest.ErrorMessage || "").match(/(\d+) days until expiry/);
    const sslDays = daysM ? parseInt(daysM[1]) : (latest.StatusCode != null ? Math.round(latest.StatusCode / 10) : null);
    if (sslDays !== null) {
      const sslWarn = parseInt(meta?.typeConfig?.ExpiryWarnDays) || 30;
      const sslCrit = parseInt(meta?.typeConfig?.ExpiryCritDays) || 7;
      const effWarn = Math.max(sslWarn, sslCrit);
      const effCrit = Math.min(sslWarn, sslCrit);
      const dot = sslDays <= 0 ? c.red : sslDays <= effCrit ? c.red : sslDays <= effWarn ? "#E89A2E" : c.green;
      const desc = sslDays <= 0 ? "expired" : sslDays <= effCrit ? "critical — renew immediately" : sslDays <= effWarn ? "warning — renewal recommended" : "healthy";
      checks.push({ dot, label: `${sslDays} days remaining — ${desc}`, right: `warn ≤${effWarn}d / crit ≤${effCrit}d` });
    }
  }
  if (monSubLower === "infra-log" && latest.ErrorMessage) {
    checks.push({
      dot: latest.IsSuccess ? c.green : c.red,
      label: latest.ErrorMessage,
      right: latest.IsSuccess ? "no events" : "events found"
    });
  }
  if (monSubLower === "infra-hw" && latest.ErrorMessage) {
    checks.push({
      dot: latest.IsSuccess ? c.green : c.red,
      label: latest.ErrorMessage,
      right: latest.IsSuccess ? "healthy" : "alert"
    });
  }
  // ── Database connectivity check rows ────────────────────────────────────
  if (["db-conn"].includes(monSubLower) || proto === "SQL") {
    const dbHostDisplay = meta?.typeConfig?.DbHost || (latest.Target || "").split("/")[0] || "server";
    const dbPortDisplay = meta?.typeConfig?.DbPort || "1433";
    if (latest.IsSuccess) {
      checks.push({ dot: c.green, label: `Connection established — ${dbHostDisplay}:${dbPortDisplay}`, right: "connected" });
      if (latest.ResponseTimeMs != null) {
        checks.push({ dot: c.green, label: `Query executed in ${latest.ResponseTimeMs}ms`, right: meta?.typeConfig?.TestQuery || "SELECT 1" });
      }
    } else {
      checks.push({ dot: c.red, label: `Connection failed — ${latest.ErrorMessage || "unknown error"}`, right: "failed" });
    }
  }
  {
    const rtDot = rtPct > 80 ? c.red : rtPct > 40 ? "#E89A2E" : c.green;
    const rtDesc = rtPct > 80 ? "approaching timeout" : rtPct > 40 ? "moderate latency" : "fast response";
    checks.push({ dot:rtDot, label:`Response time ${rtMs.toLocaleString()}ms — ${rtDesc}`, right:`timeout at ${timeoutMs.toLocaleString()}ms` });
  }
  if (credUser) {
    checks.push({ dot:c.green, label:"Login credentials resolved and decrypted successfully", right:credUser });
  }
  {
    const connLabel = protocolLabel === "WMI/DCOM" ? "WMI connection authenticated"
      : proto === "ICMP" ? "Ping responded"
      : proto === "TCP"  ? "TCP connection established"
      : proto === "DNS"  ? "DNS resolution completed"
      : proto === "SQL"  ? "SQL Server connection established"
      : (proto === "HTTP" || proto === "HTTPS") ? "HTTP connection established"
      : "Check completed";
    checks.push({ dot: latest.IsSuccess ? c.green : c.red, label: latest.IsSuccess ? connLabel : connLabel + " failed", right:"" });
  }
  // Override displayed pass/fail: if any check shows red, the overall result is a failure
  const hasFailedCheck = checks.some(ch => ch.dot === c.red);
  if (hasFailedCheck && sev === "healthy") {
    sevColor = c.red;
    sevIcon  = "❌";
    sevLabel = "Test Failed";
  }
  const checklist = (
    <div style={{ padding:"8px 16px" }}>
      <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>THRESHOLD CHECKS</div>
      {checks.map((ch, i) => (
        <div key={i} style={{ display:"flex", alignItems:"center", gap:8, padding:"2px 0", borderBottom: i < checks.length-1 ? `1px solid ${c.borderLight}` : "none" }}>
          <div style={{ width:8, height:8, borderRadius:"50%", background:ch.dot, flexShrink:0 }} />
          <span style={{ fontSize:11, color:c.text, flex:1 }}>{ch.label}</span>
          {ch.right && <span style={{ fontSize:11, color:c.textDimmer, whiteSpace:"nowrap", fontFamily:"monospace" }}>{ch.right}</span>}
        </div>
      ))}
    </div>
  );

  // ── Sparkline ────────────────────────────────────────────────────────────
  const sparkRows = (meta?.recentLogs || []).slice(0,10).reverse();
  const sparkMaxRt = Math.max(...sparkRows.map(r => r.ResponseTimeMs || 0), 1);
  const sparkHealthy = sparkRows.filter(r => (r.Severity||"").toLowerCase() === "healthy" || r.IsSuccess).length;
  const sparkFailed  = sparkRows.length - sparkHealthy;
  const sparkColor   = r => { const s=(r.Severity||"").toLowerCase(); return s==="healthy"?c.green:s==="warning"?"#E89A2E":(s==="critical"||s==="failed")?c.red:"#9CA3AF"; };
  const sparkline = sparkRows.length > 0 ? (
    <div style={{ padding:"8px 16px", borderTop:`1px solid ${c.border}` }}>
      <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>LAST {sparkRows.length} CHECKS</div>
      <div style={{ display:"flex", alignItems:"flex-end", gap:3, height:20 }}>
        {sparkRows.map((r,i) => {
          const h = r.IsSuccess ? Math.max(4, Math.round(((r.ResponseTimeMs||0)/sparkMaxRt)*16)) : 4;
          return <div key={i} title={`${r.ResponseTimeMs||"?"}ms · ${r.Severity||(r.IsSuccess?"healthy":"failed")}`} style={{ flex:1, height:h, background:sparkColor(r), borderRadius:"2px 2px 0 0", minHeight:4, maxHeight:16 }} />;
        })}
      </div>
      <div style={{ display:"flex", justifyContent:"space-between", fontSize:10, color:c.textDimmer, marginTop:3 }}>
        <span>oldest</span>
        <span>{sparkHealthy} healthy · {sparkFailed} failed</span>
        <span>latest</span>
      </div>
    </div>
  ) : null;

  // ── Query result table ───────────────────────────────────────────────────
  const queryTable = latest.QueryResult && latest.QueryResult.Columns.length > 0 ? (
    <div style={{ margin:"0 14px 12px", borderTop:`1px solid ${c.border}`, paddingTop:10 }}>
      <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:6 }}>QUERY RESULT</div>
      <div style={{ overflowX:"auto" }}>
        <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12, fontFamily:"monospace" }}>
          <thead>
            <tr>{latest.QueryResult.Columns.map((col,i) => <th key={i} style={{ textAlign:"left", padding:"6px 10px", background:c.surfaceAlt, borderBottom:`2px solid ${c.border}`, color:c.textMuted, fontWeight:600, whiteSpace:"nowrap" }}>{col}</th>)}</tr>
          </thead>
          <tbody>
            {latest.QueryResult.Rows.map((row,ri) => (
              <tr key={ri} style={{ borderBottom:`1px solid ${c.borderLight}` }}>
                {row.map((cell,ci) => <td key={ci} style={{ padding:"6px 10px", color:c.text, verticalAlign:"top" }}>{cell}</td>)}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  ) : null;

  // ── Previous runs ────────────────────────────────────────────────────────
  const prevRuns = history.length > 0 ? (
    <div style={{ padding:"8px 14px 4px", borderTop:`1px solid ${c.border}` }}>
      <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:6 }}>PREVIOUS RUNS</div>
      {history.map((r,i) => (
        <div key={i} style={{ display:"flex", gap:12, alignItems:"center", padding:"5px 8px", background:c.surfaceAlt, border:`1px solid ${c.borderLight}`, borderRadius:6, marginBottom:4, fontSize:12 }}>
          <span style={{ color:r.IsSuccess?c.green:c.red, fontWeight:700, minWidth:14 }}>{r.IsSuccess?"✓":"✗"}</span>
          <span style={{ color:c.textDimmer, flex:1 }}>{r.Timestamp ? parseUTC(r.Timestamp)?.toLocaleTimeString() : "—"}</span>
          <span style={{ fontFamily:"monospace", color:c.textMuted }}>{r.ResponseTimeMs!=null?`${r.ResponseTimeMs}ms`:"—"}</span>
          {r.ErrorMessage && <span style={{ color:"#1A1A1A", fontSize:11, flex:2, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{r.ErrorMessage}</span>}
        </div>
      ))}
    </div>
  ) : null;

  // ── Header row ───────────────────────────────────────────────────────────
  return (
    <div style={{ border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden", marginTop:2 }}>
      <div style={{ display:"flex", alignItems:"baseline", gap:16, padding:"10px 14px", borderBottom:`1px solid ${c.border}` }}>
        <span style={{ fontSize:15, fontWeight:700, color:sevColor, lineHeight:1.3 }}>{sevIcon} {sevLabel}</span>
        <span style={{ fontSize:11, color:c.textDimmer, whiteSpace:"nowrap" }}>{ts}</span>
      </div>
      {statBar}
      {latest.IsSuccess ? metricsGrid : errorBox}
      {diskIoMetricsGrid}
      {netMetricsGrid}
      {checklist}
      {sparkline}
      {queryTable}
      {prevRuns}
    </div>
  );
}

// ── Tree data ─────────────────────────────────────────────────────────────────
const MONITOR_TREE = [
  { id:"infra", label:"Infrastructure", color:"#0369a1", bg:"#e0f2fe", groups:[
    { id:"infra-avail", label:"Availability", leaves:[
      {id:"infra-ping", label:"Ping / ICMP"},
      {id:"infra-tcp",  label:"TCP Port Check"},
      {id:"infra-svc",  label:"Service / Daemon"},
    ]},
    { id:"infra-res", label:"Resources", leaves:[
      {id:"infra-cpu",  label:"CPU Utilization"},
      {id:"infra-mem",  label:"Memory Utilization"},
      {id:"infra-disk", label:"Disk Space"},
      {id:"infra-diskio",label:"Disk I/O"},
      {id:"infra-net",  label:"Network Interface"},
    ]},
    { id:"infra-adv", label:"Advanced", leaves:[
      {id:"infra-proc", label:"Process Monitor"},
      {id:"infra-log",  label:"System Log"},
      {id:"agt-ps",     label:"Agent Script"},
    ]},
  ]},
  { id:"app", label:"Application", color:"#6b21a8", bg:"#f3e8ff", groups:[
    { id:"app-avail", label:"Availability", leaves:[
      {id:"app-svc",   label:"Service Availability"},
      {id:"app-api",   label:"API Endpoint"},
      {id:"app-port",  label:"Port Check"},
    ]},
    { id:"app-perf", label:"Performance", leaves:[
      {id:"app-jvm",   label:"JVM / .NET Runtime"},
      {id:"app-thread",label:"Thread / Worker Pool"},
      {id:"app-queue", label:"Message Queue"},
      {id:"app-cache", label:"Cache Monitor"},
    ]},
    { id:"app-biz", label:"Business KPIs", leaves:[
      {id:"app-kpi",    label:"Business KPI Counter"},
      {id:"app-applog", label:"Application Log"},
      {id:"app-perfctr",label:"Performance Counter"},
    ]},
    { id:"app-scripts", label:"Scripts", leaves:[
      {id:"agt-ps", label:"Agent Script"},
    ]},
  ]},
  { id:"db", label:"Database", color:"#854d0e", bg:"#fef9c3", groups:[
    { id:"db-avail", label:"Availability", leaves:[
      {id:"db-conn", label:"Connectivity"},
      {id:"db-sess", label:"Session Count"},
    ]},
    { id:"db-perf", label:"Performance", leaves:[
      {id:"db-block",  label:"Blocking / Locking"},
      {id:"db-wait",   label:"Wait Events"},
      {id:"db-longq",  label:"Long-Running Queries"},
      {id:"db-perfctr",label:"DB Performance Counter"},
    ]},
    { id:"db-storage", label:"Storage & Logs", leaves:[
      {id:"db-ts",    label:"Tablespace / Storage"},
      {id:"db-txlog", label:"Transaction Log"},
    ]},
    { id:"db-custom", label:"Custom SQL", leaves:[
      {id:"db-query",  label:"Query / Row Count"},
      {id:"db-sqlctr", label:"SQL Counter"},
    ]},
  ]},
  { id:"web", label:"Website", color:"#166534", bg:"#dcfce7", groups:[
    { id:"web-avail", label:"Availability", leaves:[
      {id:"web-http",  label:"HTTP / HTTPS"},
      {id:"web-ssl",   label:"SSL Certificate"},
      {id:"web-dns",   label:"DNS Resolution"},
      {id:"web-redir", label:"Redirect / URL Path"},
    ]},
    { id:"web-content", label:"Content & Performance", leaves:[
      {id:"web-cm",      label:"Content Match"},
      {id:"web-perf",    label:"Page Load Time"},
      {id:"web-headers", label:"HTTP Headers"},
      {id:"web-apiresp", label:"API Response Validation"},
    ]},
  ]},
  { id:"syn", label:"Synthetic", color:"#475569", bg:"#f1f5f9", groups:[
    { id:"syn-tx", label:"Transactions", leaves:[
      {id:"syn-login", label:"Login Flow"},
      {id:"syn-web",   label:"Multi-Step Web"},
      {id:"syn-pay",   label:"Payment / Checkout"},
      {id:"syn-wf",    label:"Application Workflow"},
    ]},
    { id:"syn-api", label:"API & Data", leaves:[
      {id:"syn-apiwf", label:"API Workflow"},
      {id:"syn-sql",   label:"SQL Query Check"},
      {id:"syn-ftp",   label:"File Transfer"},
      {id:"syn-email", label:"Email Flow"},
    ]},
  ]},
];

const LEAF_META = (() => {
  const catType = {infra:"Server",app:"Application",db:"Database",web:"Website",syn:"Synthetic"};
  const map = {};
  MONITOR_TREE.forEach(cat => cat.groups.forEach(grp => grp.leaves.forEach(leaf => {
    map[leaf.id] = { catId:cat.id, catColor:cat.color, groupId:grp.id, monitorType:catType[cat.id], label:leaf.label };
  })));
  return map;
})();

// ── Default TypeConfig values applied when a leaf is first selected ───────────
const LEAF_TC_DEFAULTS = {
  "infra-ping":  { RtWarnMs: "200", RtCritMs: "500", PacketLossWarnPct: "10" },
  "infra-cpu":   { CheckMethod: "WMI", CpuWarnPct: "80", CpuCritPct: "95" },
  "infra-mem":   { CheckMethod: "WMI", MemWarnPct: "80", MemCritPct: "95", PagingWarnPct: "50" },
  "infra-disk":   { CheckMethod: "WMI", FreeWarnPct: "20", FreeCritPct: "10" },
  "infra-diskio": { CheckMethod: "WMI", IoWarnKBs: "50000", IoCritKBs: "100000", BusyWarnPct: "80", BusyCritPct: "95" },
  "infra-net":    { CheckMethod: "WMI", ThroughputInWarn: "80", ThroughputOutWarn: "80", ErrorRateWarn: "5", DropRateWarn: "5" },
  "web-http":    { RtWarnMs: "2000", RtCritMs: "5000", ExpectedStatus: "200" },
  "web-content": { RtWarnMs: "2000", RtCritMs: "5000", ExpectedStatus: "200" },
  "db-conn":     { ConnPoolWarnPct: "80", ConnPoolCritPct: "95" },
  "db-query":    { RowCountMinWarn: "1", RowCountMaxWarn: "1000" },
};

// ── MonitorFormPage — 2-column layout ────────────────────────────────────────
function MonitorFormPage({ api, monitor, origin, onClose, onSaved, navInterceptRef, onNavigate, onCopy }) {
  const isEdit = !!monitor && !monitor._addWithGroup;

  const initLeaf = () => {
    if (monitor?.MonitorSubType) return monitor.MonitorSubType;
    const mt = monitor?.MonitorType || "";
    if (mt==="Server") return "infra-ping";
    if (mt==="Application") return "app-api";
    if (mt==="Database") return "db-conn";
    if (mt==="Website") return "web-http";
    if (mt==="Synthetic") return "syn-login";
    return "";
  };

  const [form, setForm] = useState(() => (!monitor || monitor._addWithGroup) ? { ...BLANK_MONITOR, GroupID: monitor?._addWithGroup ? (monitor.GroupID||"") : "", GroupName: monitor?._addWithGroup ? (monitor.GroupName||"") : "" } : {
    MonitorName: monitor.MonitorName||"", MonitorType: monitor.MonitorType||"Server",
    IPAddressOrUrl: monitor.IPAddressOrUrl||monitor.IPAddress||"", Port: monitor.Port > 0 ? monitor.Port : "",
    Protocol: monitor.Protocol||"HTTPS", CheckFrequencySeconds: monitor.CheckFrequencySeconds||300,
    FailureThreshold: monitor.FailureThreshold||3, TimeoutMs: monitor.TimeoutMs||5000,
    ResponseTimeThresholdMs: monitor.ResponseTimeThresholdMs||2000,
    GroupID: monitor.GroupID||"", GroupName: monitor.GroupName||"",
    ParentMonitorID: monitor.ParentMonitorID||"",
    EnableAnomalyDetection: monitor.EnableAnomalyDetection||false,
    AnomalyBaselineDays: monitor.AnomalyBaselineDays||7,
    CheckSSLCertificate: monitor.CheckSSLCertificate||false,
    SSLCertExpiryWarningDays: monitor.SSLCertExpiryWarningDays||30,
    EscalationAfterAlerts: monitor.EscalationAfterAlerts||2,
    EscalationRepeatMinutes: monitor.EscalationRepeatMinutes||60,
    RunbookUrl: monitor.RunbookUrl||"",
    RunbookNotes: monitor.RunbookNotes||"",
  });
  const [tc, setTc] = useState(() => {
    const loaded = (() => {
      if (!monitor?.TypeConfigJson) return {};
      try { return JSON.parse(monitor.TypeConfigJson); } catch { return {}; }
    })();
    // Seed defaults for any fields not already present in saved TypeConfigJson.
    // This ensures threshold fields are pre-filled on edit even when the monitor
    // was saved before defaults were introduced (TypeConfigJson missing those keys).
    const defs = LEAF_TC_DEFAULTS[monitor?.MonitorSubType || ""] || {};
    const merged = { ...defs };
    Object.keys(loaded).forEach(k => { if (loaded[k] !== "" && loaded[k] !== undefined) merged[k] = loaded[k]; });
    return merged;
  });
  const [notifs, setNotifs] = useState(() =>
    (monitor?.Notifications||[]).map(n=>({
      MethodType:n.MethodType||"email", Destination:n.Destination||"",
      OnWarning:n.OnWarning??true, OnCritical:n.OnCritical??true, OnRecovery:n.OnRecovery??true,
    }))
  );
  const [groups, setGroups]     = useState([]);
  const [allMons, setAllMons]   = useState([]);
  const [err, setErr]           = useState("");
  const [saving, setSaving]     = useState(false);
  const [testing, setTesting]   = useState(false);
  const [testRuns, setTestRuns] = useState([]);
  const [isDirty, setIsDirty]       = useState(false);
  const [spinnerFrame, setSpinnerFrame]         = useState(0);
  const [elapsedTenths, setElapsedTenths]       = useState(0);
  const [runNowSpinnerFrame, setRunNowSpinnerFrame]   = useState(0);
  const [runNowElapsedTenths, setRunNowElapsedTenths] = useState(0);
  const [barPct, setBarPct]     = useState(0);
  const [barVisible, setBarVisible] = useState(false);
  const [confirmLeave, setConfirmLeave] = useState(false);
  const [deleteConfirm, setDeleteConfirm] = useState(false);
  const [deleting, setDeleting]     = useState(false);
  const [pendingNavTab, setPendingNavTab] = useState(null);
  const [recentLogs, setRecentLogs] = useState([]);
  const [logsLoading, setLogsLoading] = useState(false);
  const [copying, setCopying] = useState(false);
  const [runNow, setRunNow]   = useState(false);
  const [runNowMsg, setRunNowMsg] = useState("");
  const [isEnabled, setIsEnabled] = useState(() => monitor ? monitor.IsEnabled !== false : true);
  // ── Inline AI Diagnosis pane ──
  const [credentialId, setCredentialId] = useState(() => monitor?.CredentialID || "");
  const [credentials, setCredentials]   = useState([]);
  const [agentHosts, setAgentHosts]     = useState([]);
  const [agentHostId, setAgentHostId]   = useState(() => monitor?.AgentHostId ? String(monitor.AgentHostId) : "");
  const [psScript, setPsScript]         = useState(() => monitor?.AgentScript || "");
  const [psTimeout, setPsTimeout]       = useState(() => monitor?.AgentScriptTimeoutSeconds || 30);
  const [psExamplesOpen, setPsExamplesOpen] = useState(false);
  const [diagOpen,    setDiagOpen]    = useState(false);
  const [diagLoading, setDiagLoading] = useState(false);
  const [diagResult,  setDiagResult]  = useState(null);
  const [diagError,   setDiagError]   = useState(null);
  const [diagTs,      setDiagTs]      = useState(null);
  const [diagName,    setDiagName]    = useState("");
  const [diagSevCol,  setDiagSevCol]  = useState("#D95C5C");
  const [investigationThread, setInvestigationThread] = useState([]);
  const [followUpText, setFollowUpText] = useState('');
  const [followUpLoading, setFollowUpLoading] = useState(false);
  const [copiedScriptId, setCopiedScriptId] = useState(null);
  const [lastDiagnosisContext, setLastDiagnosisContext] = useState('');
  const [currentSessionId, setCurrentSessionId] = useState(null);
  const [investigationHistory, setInvestigationHistory] = useState([]);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [historyPanelOpen, setHistoryPanelOpen] = useState(true);
  const [historyShowAll, setHistoryShowAll] = useState(false);
  const [sessionTitle, setSessionTitle] = useState('');
  const [pendingAttachments, setPendingAttachments] = useState([]);
  const [showPasteModal, setShowPasteModal] = useState(false);
  const [pasteText, setPasteText] = useState('');
  const [attachmentError, setAttachmentError] = useState('');
  const readFileAsText = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = e => resolve(e.target.result);
    reader.onerror = () => reject(new Error('File read failed'));
    reader.readAsText(file);
  });
  const readFileAsBase64 = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = e => resolve(e.target.result.split(',')[1]);
    reader.onerror = () => reject(new Error('File read failed'));
    reader.readAsDataURL(file);
  });
  const estimateTokens = (text) => Math.ceil(text.length / 4);
  const handleFileAttach = async (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    setAttachmentError('');
    const isImage = file.type.startsWith('image/');
    const maxBytes = isImage ? 5 * 1024 * 1024 : Infinity;
    if (file.size > maxBytes) { setAttachmentError('Image exceeds 5MB limit.'); e.target.value = ''; return; }
    if (pendingAttachments.length >= 5) { setAttachmentError('Maximum 5 attachments per message.'); e.target.value = ''; return; }
    try {
      if (isImage) {
        const base64 = await readFileAsBase64(file);
        const thumbnailUrl = URL.createObjectURL(file);
        setPendingAttachments(prev => [...prev, { id: Date.now(), type: 'image', name: file.name, data: base64, mimeType: file.type, fileSizeBytes: file.size, thumbnailUrl, tokenEstimate: Math.ceil(file.size / 750) }]);
      } else {
        let text = await readFileAsText(file);
        const fullSize = text.length;
        const MAX_SEND = 200000;
        const truncated = text.length > MAX_SEND;
        if (truncated) text = text.slice(0, MAX_SEND) + `\n\n[File truncated — ${fullSize.toLocaleString()} chars total, first 200,000 sent to AI.]`;
        setPendingAttachments(prev => [...prev, { id: Date.now(), type: 'file', name: file.name, data: text, mimeType: file.type, fileSizeBytes: file.size, thumbnailUrl: null, tokenEstimate: estimateTokens(text), sentToApi: true }]);
      }
    } catch { setAttachmentError('Could not read file. Please try again.'); }
    e.target.value = '';
  };
  const handlePasteConfirm = () => {
    if (!pasteText.trim()) return;
    if (pendingAttachments.length >= 5) { setAttachmentError('Maximum 5 attachments per message.'); return; }
    setPendingAttachments(prev => [...prev, { id: Date.now(), type: 'paste', name: 'Pasted text', data: pasteText.trim(), mimeType: 'text/plain', fileSizeBytes: pasteText.length, thumbnailUrl: null, tokenEstimate: estimateTokens(pasteText), sentToApi: true }]);
    setPasteText(''); setShowPasteModal(false);
  };
  const removeAttachment = (id) => {
    setPendingAttachments(prev => { const att = prev.find(a => a.id === id); if (att?.thumbnailUrl) URL.revokeObjectURL(att.thumbnailUrl); return prev.filter(a => a.id !== id); });
  };
  const runDiagnosis = (data) => {
    setDiagOpen(true);
    setDiagLoading(true); setDiagResult(null); setDiagError(null); setDiagTs(null);
    setInvestigationThread([]); setFollowUpText(''); setLastDiagnosisContext('');
    setCurrentSessionId(null); setSessionTitle('');
    setPendingAttachments([]); setAttachmentError('');
    setDiagName(data.monitorName || "");
    setDiagSevCol(data.severity === "warning" ? "#E89A2E" : "#D95C5C");
    const systemPrompt = "You are an expert infrastructure monitoring assistant helping users diagnose monitor check failures 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 400 words.";
    const userMessage = `Monitor Name: ${data.monitorName||"Unknown"}\nMonitor Type: ${data.monitorType||"Unknown"} / ${data.monitorSubType||"Unknown"}\nTarget: ${data.target||"Unknown"}\nProtocol: ${data.protocol||"Unknown"}\nHTTP Status Code: ${data.statusCode||"N/A"}\nResponse Time: ${data.responseTime||"N/A"}\nError Message: ${data.errorMessage||"None"}\nSeverity: ${data.severity||"unknown"}\nTimeout Setting: ${data.timeoutMs||5000}ms\n\nPlease diagnose this monitor result and explain what went wrong and how to fix it.`;
    setLastDiagnosisContext(userMessage);
    api("POST", "/diagnose", { systemPrompt, userMessage })
      .then(d => {
        const txt = d?.text || "No response received.";
        setDiagResult(txt); setDiagTs(new Date().toLocaleTimeString());
        setInvestigationThread([{ role: 'assistant', content: txt, timestamp: new Date().toISOString(), id: Date.now() }]);
      })
      .catch(e => setDiagError((e?.message && !e.message.startsWith("HTTP ")) ? e.message : "Diagnosis unavailable — please try again."))
      .finally(() => setDiagLoading(false));
  };
  const renderDiagMd = (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 trimmed = line.trim();
      if (trimmed.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 }}>{trimmed.slice(3)}</div>;
      if (trimmed === "---")
        return <hr key={i} style={{ border:"none", borderTop:"1px solid #C0D8E8", margin:"10px 0" }} />;
      if (trimmed.startsWith("- ") || trimmed.startsWith("• ")) {
        const content = parseBold(trimmed.slice(2));
        return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", marginTop:2 }}>•</span><span>{content}</span></div>;
      }
      if (/^\d+\./.test(trimmed)) {
        const num = trimmed.match(/^\d+/)[0];
        const content = parseBold(trimmed.slice(num.length+1).trim());
        return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", minWidth:14 }}>{num}.</span><span>{content}</span></div>;
      }
      if (trimmed === "") return <div key={i} style={{ height:6 }} />;
      return <div key={i} style={{ marginBottom:3, lineHeight:1.6 }}>{parseBold(line)}</div>;
    });
  };
  const handleDiagFollowUp = () => {
    const q = followUpText.trim();
    if (!q || followUpLoading) return;
    const snapAttachments = [...pendingAttachments];
    const newUserMsg = { role: 'user', content: q, timestamp: new Date().toISOString(), id: Date.now(), attachments: snapAttachments };
    const updatedThread = [...investigationThread, newUserMsg];
    setInvestigationThread(updatedThread);
    setFollowUpText('');
    setPendingAttachments([]);
    setAttachmentError('');
    setFollowUpLoading(true);
    const systemPrompt = "You are an expert infrastructure monitoring assistant helping users investigate monitor 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.";
    const threadHistory = updatedThread.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n\n');
    const contextText = `Initial incident context:\n${lastDiagnosisContext}\n\n---\nConversation so far:\n${threadHistory}\n\n---\nPlease respond to the latest user message.`;
    const now = new Date();
    const fmtDate = now.toLocaleString('en-US', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit', hour12:false });
    const ensureSession = (sidRef) => {
      if (sidRef.id !== null) return Promise.resolve(sidRef.id);
      const title = `${diagName || 'Monitor'} \u00b7 ${fmtDate}`;
      return api("POST", "/investigations", { monitorId: monitor?.MonitorID || null, groupName: null, sessionTitle: title, initialContext: lastDiagnosisContext })
        .then(d => {
          const sid = d?.sessionId;
          sidRef.id = sid;
          setCurrentSessionId(sid);
          setSessionTitle(title);
          if (investigationThread.length > 0) {
            api("POST", `/investigations/${sid}/messages`, { role: 'assistant', content: investigationThread[0].content, stage: 'diagnosis', attachments: [] }).catch(() => {});
          }
          return sid;
        });
    };
    const sidRef = { id: currentSessionId };
    // Build API payload — use messages array if there are image attachments
    let diagnosePayload;
    if (snapAttachments.length > 0) {
      const contentParts = [{ type: 'text', text: contextText }];
      snapAttachments.forEach(att => {
        if (att.type === 'image') contentParts.push({ type: 'image', source: { type: 'base64', media_type: att.mimeType, data: att.data } });
        else contentParts.push({ type: 'text', text: `=== ATTACHED: ${att.name} ===\n${att.data}` });
      });
      diagnosePayload = { systemPrompt, messages: [{ role: 'user', content: contentParts }] };
    } else {
      diagnosePayload = { systemPrompt, userMessage: contextText };
    }
    api("POST", "/diagnose", diagnosePayload)
      .then(d => {
        const txt = d?.text || "No response received.";
        setInvestigationThread(prev => [...prev, { role: 'assistant', content: txt, timestamp: new Date().toISOString(), id: Date.now() + 1, attachments: [] }]);
        ensureSession(sidRef).then(sid => {
          if (!sid) return;
          const attReqs = snapAttachments.map(a => ({ attachmentType: a.type, attachmentName: a.name, attachmentData: a.data, fileSizeBytes: a.fileSizeBytes, sentToApi: true, tokenEstimate: a.tokenEstimate }));
          api("POST", `/investigations/${sid}/messages`, { role: 'user', content: q, stage: 'gathering', attachments: attReqs }).catch(() => {});
          api("POST", `/investigations/${sid}/messages`, { role: 'assistant', content: txt, stage: 'gathering', attachments: [] }).catch(() => {});
        });
      })
      .catch(() => {
        setInvestigationThread(prev => [...prev, { role: 'assistant', content: 'Follow-up unavailable — please try again.', timestamp: new Date().toISOString(), id: Date.now() + 1, attachments: [] }]);
      })
      .finally(() => setFollowUpLoading(false));
  };
  const [enabledConfirm, setEnabledConfirm] = useState(false);
  const [recentAlerts, setRecentAlerts] = useState([]);

  // ── Notification assignments panel ──
  const [monitorAssignments, setMonitorAssignments] = useState([]);
  const [monitorAssignmentsLoading, setMonitorAssignmentsLoading] = useState(false);
  const [availableRecipients, setAvailableRecipients] = useState([]);
  const [notifPanelOpen, setNotifPanelOpen] = useState(true);
  const [showAssignPicker, setShowAssignPicker] = useState(false);
  const [assignPickerRecipient, setAssignPickerRecipient] = useState('');
  const [assignPickerSeverity, setAssignPickerSeverity] = useState('warning');
  const [assignPickerCooldown, setAssignPickerCooldown] = useState(30);

  // ── Runbook panel ──
  const [runbookPanelOpen, setRunbookPanelOpen] = useState(true);
  const [runbookDirty, setRunbookDirty] = useState(false);

  const [selectedLeaf, setSelectedLeaf] = useState(initLeaf);
  const [expandedCats, setExpandedCats] = useState(() => {
    const leaf = initLeaf();
    const meta = LEAF_META[leaf];
    return meta ? new Set([meta.catId]) : new Set();
  });
  const [expandedGroups, setExpandedGroups] = useState(() => {
    const leaf = initLeaf();
    const meta = LEAF_META[leaf];
    return meta ? new Set([meta.groupId]) : new Set();
  });
  const [hovered, setHovered] = useState(null);
  const hasLoadedRef = useRef(false);
  const preserveTcRef = useRef(false);

  // Clear stale TypeConfig when user switches monitor type (not on initial load).
  // When switching within the same monitor type family, preserveTcRef is set to true
  // by selectLeaf to retain CheckMethod across subtype changes.
  useEffect(()=>{
    if (!hasLoadedRef.current) { hasLoadedRef.current = true; return; }
    const defs = LEAF_TC_DEFAULTS[selectedLeaf] || {};
    if (preserveTcRef.current) {
      preserveTcRef.current = false;
      // Same-family switch: preserve CheckMethod, apply defaults only for empty fields.
      setTc(prev => {
        const merged = { ...defs };
        Object.keys(prev).forEach(k => { if (prev[k] !== "" && prev[k] !== undefined) merged[k] = prev[k]; });
        return merged;
      });
    } else {
      setTc(defs);
    }
  }, [selectedLeaf]);

  // Change 4: Smart threshold defaults — new monitors only
  useEffect(() => {
    if (isEdit || !selectedLeaf) return;
    const THRESH = {
      "infra-cpu":   { CpuWarnPct: "80",  CpuCritPct: "95" },
      "infra-mem":   { MemWarnPct: "80",  MemCritPct: "95" },
      "infra-disk":  { FreeWarnPct: "20", FreeCritPct: "10" },
      "infra-ping":  { RtWarnMs: "200",   RtCritMs: "500" },
      "web-http":    { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-cm":      { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-load":    { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-redir":   { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-headers": { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-apiresp": { RtWarnMs: "2000",  RtCritMs: "5000" },
      "web-ssl":     { ExpiryWarnDays: "30", ExpiryCritDays: "7" },
    };
    const defs = THRESH[selectedLeaf];
    if (!defs) return;
    setTc(prev => {
      const updated = { ...prev };
      Object.entries(defs).forEach(([k, v]) => {
        if (updated[k] === undefined || updated[k] === "" || updated[k] === "0" || updated[k] === 0) {
          updated[k] = v;
        }
      });
      return updated;
    });
  }, [selectedLeaf]);

  const loadRecentLogs = useCallback(() => {
    if (!isEdit || !monitor?.MonitorID) return;
    setLogsLoading(true);
    api("GET",`/logs/ping?monitorId=${monitor.MonitorID}&top=100`)
      .then(r=>setRecentLogs(Array.isArray(r)?r:[]))
      .catch(()=>{})
      .finally(()=>setLogsLoading(false));
  },[api, isEdit, monitor?.MonitorID]);

  const loadMonitorAssignments = useCallback(async () => {
    if (!isEdit || !monitor?.MonitorID) return;
    setMonitorAssignmentsLoading(true);
    try {
      const assignments = await api("GET", `/notifications/monitors/${monitor.MonitorID}/assignments`);
      setMonitorAssignments(Array.isArray(assignments) ? assignments : []);
    } catch { setMonitorAssignments([]); }
    finally { setMonitorAssignmentsLoading(false); }
  }, [api, isEdit, monitor?.MonitorID]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    const skId = "op1-skeleton-kf";
    if (!document.getElementById(skId)) {
      const el = document.createElement("style"); el.id = skId;
      el.textContent = "@keyframes op1-skeleton-pulse { 0%,100%{opacity:0.4} 50%{opacity:0.8} }";
      document.head.appendChild(el);
    }
    const denseId = "op1-dense-form-css";
    if (!document.getElementById(denseId)) {
      const el = document.createElement("style"); el.id = denseId;
      el.textContent = [
        ".op1-dense-form input:not([type=checkbox]):not([type=radio]):not([type=file]),",
        ".op1-dense-form select {",
        "  padding: 4px 8px !important;",
        "  height: 28px !important;",
        "  font-size: 12px !important;",
        "  box-sizing: border-box !important;",
        "}",
        ".op1-dense-form label {",
        "  font-size: 10px !important;",
        "  margin-bottom: 2px !important;",
        "}",
        ".op1-dense-form .cs-title {",
        "  font-size: 10px !important;",
        "}",
      ].join("\n");
      document.head.appendChild(el);
    }
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/monitors/groups").then(r=>setGroups(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/monitors").then(r=>setAllMons(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/credentials").then(r=>setCredentials(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/agents").then(r=>setAgentHosts(Array.isArray(r)?r:[])).catch(()=>{});
    // Pre-populate form with saved defaults only for new monitors
    if (!isEdit) {
      api("GET","/config/monitor-defaults").then(d=>{
        if (!d || typeof d !== "object") return;
        const g = k => d[k] ? parseInt(d[k]) : undefined;
        setForm(p=>({
          ...p,
          CheckFrequencySeconds:   g("default_check_frequency_seconds")   || p.CheckFrequencySeconds,
          FailureThreshold:        g("default_failure_threshold")          || p.FailureThreshold,
          TimeoutMs:               g("default_timeout_ms")                 || p.TimeoutMs,
          ResponseTimeThresholdMs: g("default_response_time_warning_ms")   || p.ResponseTimeThresholdMs,
          SSLCertExpiryWarningDays:g("default_ssl_expiry_warning_days")    || p.SSLCertExpiryWarningDays,
          AnomalyBaselineDays:     g("default_anomaly_baseline_days")      || p.AnomalyBaselineDays,
          EscalationRepeatMinutes: g("default_escalation_repeat_minutes")  || p.EscalationRepeatMinutes,
        }));
      }).catch(()=>{});
    }
    loadRecentLogs();
    loadMonitorAssignments();
    api("GET", "/notifications/recipients").then(r => setAvailableRecipients(Array.isArray(r) ? r : [])).catch(() => {});
    if (isEdit && monitor?.MonitorID) {
      setHistoryLoading(true);
      api("GET", `/investigations?monitorId=${monitor.MonitorID}`)
        .then(r => setInvestigationHistory(Array.isArray(r) ? r : []))
        .catch(() => setInvestigationHistory([]))
        .finally(() => setHistoryLoading(false));
    }
  },[api]);

  const f  = (k,v) => { setForm(p=>({...p,[k]:v})); setIsDirty(true); };
  const t  = (k,v) => { setTc(p=>({...p,[k]:v})); setIsDirty(true); };
  const addNotif    = () => { setNotifs(n=>[...n,{MethodType:"email",Destination:"",OnWarning:true,OnCritical:true,OnRecovery:true}]); setIsDirty(true); };
  const removeNotif = i => { setNotifs(n=>n.filter((_,j)=>j!==i)); setIsDirty(true); };
  const updNotif    = (i,k,v) => { setNotifs(n=>n.map((r,j)=>j===i?{...r,[k]:v}:r)); setIsDirty(true); };

  const selectLeaf = (leafId) => {
    const meta = LEAF_META[leafId];
    if (!meta) return;
    const currentMeta = LEAF_META[selectedLeaf];
    const sameFamilySwitch = !!(currentMeta && currentMeta.monitorType === meta.monitorType);
    setSelectedLeaf(leafId);
    setExpandedCats(s => new Set([...s, meta.catId]));
    setExpandedGroups(s => new Set([...s, meta.groupId]));
    if (leafId !== selectedLeaf) {
      // Subtype changed — reset connection/config fields so stale values don't carry over.
      // Preserve: MonitorName, GroupID/Name, CheckFrequencySeconds, FailureThreshold,
      //           TimeoutMs, ResponseTimeThresholdMs, ParentMonitorID, anomaly/escalation settings.
      // Same monitor type family: also preserve IPAddressOrUrl and CheckMethod (via preserveTcRef).
      // credentialId is separate state and always preserved.
      if (sameFamilySwitch) {
        preserveTcRef.current = true;
        setForm(p => ({
          ...p,
          MonitorType: meta.monitorType,
          Port:        "",
          Protocol:    "HTTPS",
          TimeoutMs:   leafId === "infra-hw" ? 15000 : p.TimeoutMs,
        }));
      } else {
        setCredentialId("");
        setForm(p => ({
          ...p,
          MonitorType:    meta.monitorType,
          IPAddressOrUrl: "",
          Port:           "",
          Protocol:       "HTTPS",
          TimeoutMs:      leafId === "infra-hw" ? 15000 : p.TimeoutMs,
        }));
      }
    } else {
      setForm(p => ({ ...p, MonitorType: meta.monitorType }));
    }
    setIsDirty(true);
  };

  const tryClose = () => {
    if (isDirty) { setConfirmLeave(true); } else { onClose(); }
  };

  useEffect(()=>{
    const handler = e => { if (isDirty) { e.preventDefault(); e.returnValue=""; } };
    window.addEventListener("beforeunload", handler);
    return () => window.removeEventListener("beforeunload", handler);
  },[isDirty]);

  useEffect(()=>{
    if (!navInterceptRef) return;
    navInterceptRef.current = (destKey) => {
      if (isDirty) { setPendingNavTab(destKey); setConfirmLeave(true); }
      else { onNavigate(destKey); onClose(); }
    };
    return () => { navInterceptRef.current = null; };
  },[isDirty, onNavigate, onClose, navInterceptRef]);

  const handleToggleEnabled = async () => {
    if (!monitor?.MonitorID) return;
    const newVal = !isEnabled;
    setIsEnabled(newVal);
    try {
      await api("PATCH", `/monitors/${monitor.MonitorID}/enabled`, { enabled: newVal });
      setEnabledConfirm(true);
      setTimeout(() => setEnabledConfirm(false), 2000);
    } catch(ex) {
      setIsEnabled(!newVal);
      setErr(ex.message || "Failed to update.");
    }
  };

  const buildPayload = () => {
    const tcClean = Object.fromEntries(Object.entries(tc).filter(([,v])=>v!=null&&v!==""));
    return {
      ...form,
      Tags: undefined,
      IsEnabled: isEnabled,
      MonitorSubType: selectedLeaf || undefined,
      Port: parseInt(form.Port)||0,
      CheckFrequencySeconds: parseInt(form.CheckFrequencySeconds)||300,
      FailureThreshold: parseInt(form.FailureThreshold)||3,
      TimeoutMs: parseInt(form.TimeoutMs)||5000,
      ResponseTimeThresholdMs: parseInt(form.ResponseTimeThresholdMs)||2000,
      GroupID: form.GroupName ? null : (form.GroupID ? parseInt(form.GroupID) : null),
      GroupName: form.GroupName || undefined,
      ParentMonitorID: form.ParentMonitorID ? parseInt(form.ParentMonitorID) : null,
      SSLCertExpiryWarningDays: parseInt(form.SSLCertExpiryWarningDays)||30,
      EscalationAfterAlerts: parseInt(form.EscalationAfterAlerts)||2,
      EscalationRepeatMinutes: parseInt(form.EscalationRepeatMinutes)||60,
      Notifications: notifs.filter(n=>n.Destination.trim()),
      TypeConfig: Object.keys(tcClean).length ? tcClean : undefined,
      CredentialID: credentialId ? parseInt(credentialId) : null,
      AgentHostId: agentHostId ? parseInt(agentHostId) : null,
      AgentScript: psScript || undefined,
      AgentScriptTimeoutSeconds: selectedLeaf === "agt-ps" ? (parseInt(psTimeout) || 30) : undefined,
    };
  };

  const submit = async () => {
    setErr("");
    if (!form.MonitorName.trim()) { setErr("Monitor Name is required."); return; }
    setSaving(true);
    try {
      const payload = buildPayload();
      if (isEdit) await api("PUT",`/monitors/${monitor.MonitorID}`,payload);
      else        await api("POST","/monitors",payload);
      setIsDirty(false);
      setRunbookDirty(false);
      onSaved();
    } catch(e) { setErr(e.message); }
    finally { setSaving(false); }
  };

  const SPINNER_CHARS = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];

  useEffect(() => {
    if (!testing) { setSpinnerFrame(0); setElapsedTenths(0); return; }
    const t1 = setInterval(() => setSpinnerFrame(f => (f + 1) % 10), 80);
    const t2 = setInterval(() => setElapsedTenths(n => n + 1), 100);
    return () => { clearInterval(t1); clearInterval(t2); };
  }, [testing]);

  useEffect(() => {
    if (!runNow) { setRunNowSpinnerFrame(0); setRunNowElapsedTenths(0); return; }
    const t1 = setInterval(() => setRunNowSpinnerFrame(f => (f + 1) % 10), 80);
    const t2 = setInterval(() => setRunNowElapsedTenths(n => n + 1), 100);
    return () => { clearInterval(t1); clearInterval(t2); };
  }, [runNow]);

  const runTest = async () => {
    // Inline validation for subtypes that require specific config fields
    if (selectedLeaf === "web-cm" && !(tc.ContentMatchString||tc.ContentMatch||"").trim()) {
      setErr("Content Match String is required before running a test."); return;
    }
    if (selectedLeaf === "web-apiresp" && !(tc.PathExpression||"").trim()) {
      setErr("JSONPath / XPath Expression is required before running a test."); return;
    }
    setErr(""); setTesting(true);
    setBarVisible(true); setBarPct(85);
    try {
      const tcClean = Object.fromEntries(Object.entries(tc).filter(([,v])=>v!=null&&v!==""));
      const isDbTest  = form.MonitorType === "Database";
      const isAppSvc  = selectedLeaf === "app-svc";
      const payload = {
        MonitorType:             form.MonitorType,
        MonitorSubType:          selectedLeaf || undefined,
        IPAddressOrUrl:          isDbTest ? (tc.DbHost || "") : form.IPAddressOrUrl.trim(),
        Protocol:                isDbTest ? "SQL" : isAppSvc ? (tc.CheckMethod || "TCP") : form.Protocol,
        Port:                    isDbTest ? (parseInt(tc.DbPort) || 1433) : (parseInt(form.Port)||0),
        TimeoutMs:               parseInt(form.TimeoutMs)||5000,
        ResponseTimeThresholdMs: parseInt(form.ResponseTimeThresholdMs)||2000,
        TypeConfig:              Object.keys(tcClean).length ? tcClean : undefined,
        CredentialID:            credentialId ? parseInt(credentialId) : null,
      };
      const r = await api("POST", "/monitors/test", payload);
      const run = { ...r, MonitorName: form.MonitorName.trim() || form.IPAddressOrUrl.trim() || selectedLeaf };
      setTestRuns(prev => [run, ...prev].slice(0, 3));
      setBarPct(100);
      setTimeout(() => { setBarVisible(false); setBarPct(0); }, 300);
    } catch(e) { setErr(e.message); setBarVisible(false); setBarPct(0); }
    finally { setTesting(false); }
  };

  const copyMonitor = async () => {
    if (!isEdit || !monitor?.MonitorID) return;
    setCopying(true); setErr("");
    try {
      const created = await api("POST", `/monitors/${monitor.MonitorID}/copy`);
      setIsDirty(false);
      onCopy && onCopy(created);
    } catch(e) { setErr(e.message); }
    finally { setCopying(false); }
  };

  const DARK = "#1E2B3C";
  const PANE_H = "calc(100vh - 58px)";

  // ── Shared field blocks ───────────────────────────────────────────────────
  const dbCredOptions = credentials.filter(c=>c.CredentialType==="Database"||c.CredentialType==="Generic");
  const dbConnFields = (
    <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
      {dbCredOptions.length > 0 && (
        <div style={{ gridColumn:"1/-1" }}>
          <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
          <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }}
            style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
            <option value="">— Use inline credentials below —</option>
            {dbCredOptions.map(c=><option key={c.CredentialID} value={c.CredentialID}>{c.Name} ({c.Username})</option>)}
          </select>
        </div>
      )}
      <div style={{ gridColumn:"1/-1" }}>
        <Sel label="Database Type" value={tc.DbType||"SQLServer"} onChange={e=>{
          const p = {SQLServer:1433,PostgreSQL:5432,MySQL:3306,Oracle:1521,MongoDB:27017,Redis:6379};
          t("DbType",e.target.value);
          if(!tc.DbPort) t("DbPort", String(p[e.target.value]||1433));
        }} options={[{value:"SQLServer",label:"SQL Server"},{value:"PostgreSQL",label:"PostgreSQL"},{value:"MySQL",label:"MySQL"},{value:"Oracle",label:"Oracle"},{value:"MongoDB",label:"MongoDB"},{value:"Redis",label:"Redis"}]} />
      </div>
      <Input label="Host" value={tc.DbHost||""} onChange={e=>t("DbHost",e.target.value)} hint="e.g. localhost" />
      <Input label="Port" type="number" value={tc.DbPort||"1433"} onChange={e=>t("DbPort",e.target.value)} />
      <Input label="Database Name" value={tc.DbName||""} onChange={e=>t("DbName",e.target.value)} hint="e.g. mydb" />
      <Input label="Username" value={tc.DbUser||""} onChange={e=>t("DbUser",e.target.value)} />
      <Input label="Password" type="password" value={tc.DbPass||""} onChange={e=>t("DbPass",e.target.value)} />
      <Sel label="SSL/TLS" value={tc.DbSsl||"Preferred"} onChange={e=>t("DbSsl",e.target.value)} options={["Required","Preferred","Disabled"]} />
      <Input label="Test Query (optional)" value={tc.TestQuery||""} onChange={e=>t("TestQuery",e.target.value)} hint="e.g. SELECT 1" />
    </div>
  );

  const infraCredOptions = credentials.filter(c=>c.CredentialType==="Windows"||c.CredentialType==="SSH"||c.CredentialType==="SNMP"||c.CredentialType==="Generic");
  const infraHostFields = (
    <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
      {agentHostId
        ? <div style={{ gridColumn:"1/-1", fontSize:11, color:"#4A4A4A", fontStyle:"italic", marginBottom:4 }}>ℹ️ Agent monitors this host directly — no IP address required.</div>
        : <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
      }
      {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","SNMP","Agent"]} />}
      {!agentHostId && infraCredOptions.length > 0 && (
        <div style={{ gridColumn:"1/-1" }}>
          <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
          <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }}
            style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
            <option value="">— No credential selected —</option>
            {infraCredOptions.map(c=><option key={c.CredentialID} value={c.CredentialID}>{c.Name} ({c.CredentialType} · {c.Username})</option>)}
          </select>
        </div>
      )}
    </div>
  );

  // Synthetic step helpers
  const synthSteps = (() => { try { return tc.Steps ? JSON.parse(tc.Steps) : []; } catch { return []; } })();
  const addStep = () => t("Steps", JSON.stringify([...synthSteps, {name:"",url:"",action:"navigate",selector:"",value:"",waitMs:""}]));
  const delStep = (i) => t("Steps", JSON.stringify(synthSteps.filter((_,j)=>j!==i)));
  const updStep = (i,k,v) => { const s=[...synthSteps]; s[i]={...s[i],[k]:v}; t("Steps",JSON.stringify(s)); };

  // Request header helpers
  const reqHeaders = (() => { try { return tc.RequestHeaders ? JSON.parse(tc.RequestHeaders) : []; } catch { return []; } })();
  const addHdr = () => t("RequestHeaders", JSON.stringify([...reqHeaders, {key:"",value:""}]));
  const delHdr = (i) => t("RequestHeaders", JSON.stringify(reqHeaders.filter((_,j)=>j!==i)));
  const updHdr = (i,k,v) => { const h=[...reqHeaders]; h[i]={...h[i],[k]:v}; t("RequestHeaders",JSON.stringify(h)); };

  const customHdrs = (() => { try { return tc.CustomHeaders ? JSON.parse(tc.CustomHeaders) : []; } catch { return []; } })();
  const addCustomHdr = () => t("CustomHeaders", JSON.stringify([...customHdrs, {key:"",value:""}]));
  const delCustomHdr = (i) => t("CustomHeaders", JSON.stringify(customHdrs.filter((_,j)=>j!==i)));
  const updCustomHdr = (i,k,v) => { const h=[...customHdrs]; h[i]={...h[i],[k]:v}; t("CustomHeaders",JSON.stringify(h)); };

  const customHdrsUI = (
    <CollapsibleSection title="Custom Headers" alwaysOpen={true}>
      {customHdrs.map((h,i) => (
        <div key={i} style={{ display:"flex", gap:8, marginBottom:6 }}>
          <Input label="" value={h.key||""} onChange={e=>updCustomHdr(i,"key",e.target.value)} placeholder="Header name" />
          <Input label="" value={h.value||""} onChange={e=>updCustomHdr(i,"value",e.target.value)} placeholder="Value" />
          <button onClick={()=>delCustomHdr(i)} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#D95C5C",cursor:"pointer",padding:"0 10px",fontSize:14,whiteSpace:"nowrap" }}>✕</button>
        </div>
      ))}
      <button onClick={addCustomHdr} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#006D8C",cursor:"pointer",padding:"6px 12px",fontSize:12,marginTop:4 }}>+ Add Header</button>
    </CollapsibleSection>
  );

  // API workflow step helpers
  const apiSteps = (() => { try { return tc.ApiSteps ? JSON.parse(tc.ApiSteps) : []; } catch { return []; } })();
  const addApiStep = () => t("ApiSteps", JSON.stringify([...apiSteps, {name:"",url:"",method:"GET",expectedStatus:"200",assertion:"",storeAs:""}]));
  const delApiStep = (i) => t("ApiSteps", JSON.stringify(apiSteps.filter((_,j)=>j!==i)));
  const updApiStep = (i,k,v) => { const s=[...apiSteps]; s[i]={...s[i],[k]:v}; t("ApiSteps",JSON.stringify(s)); };

  // ── Tree Pane ─────────────────────────────────────────────────────────────
  const treePane = (
    <div style={{ width:220, flexShrink:0, overflowY:"auto", background:"#FFFFFF", borderRight:"1px solid #C0D8E8" }}>
      <div style={{ padding:"8px 12px", background:"#F8FBFD", borderBottom:"1px solid #C0D8E8", fontSize:10, fontWeight:700, color:"#4A4A4A", letterSpacing:"0.08em", textTransform:"uppercase" }}>Monitor Type</div>
      {MONITOR_TREE.map(cat => {
        const catOpen = expandedCats.has(cat.id);
        const isSyn = cat.id === "syn";
        const isAgt = cat.id === "agt" && !agentHostId;
        const isDisabled = isSyn || isAgt;
        const disabledTitle = isSyn ? "Still working on this" : isAgt ? "Requires an agent host" : undefined;
        return (
          <div key={cat.id}>
            <div
              title={disabledTitle}
              style={{ display:"flex", alignItems:"center", gap:8, padding:"10px 12px", cursor: isDisabled ? "not-allowed" : "pointer", fontSize:12, fontWeight:700, color: isDisabled ? "#B0C4D4" : "#1A1A1A", background: (!isDisabled && hovered===cat.id) ? "#F0F7FB" : "transparent" }}
              onClick={() => { if(isDisabled) return; setExpandedCats(s => { const n=new Set(s); if(n.has(cat.id)) n.delete(cat.id); else n.add(cat.id); return n; }); }}
              onMouseEnter={() => { if(!isDisabled) setHovered(cat.id); }}
              onMouseLeave={() => setHovered(null)}
            >
              <span style={{ width:10, height:10, borderRadius:2, background: isDisabled ? "#B0C4D4" : cat.color, flexShrink:0, display:"inline-block" }} />
              <span>{cat.label}</span>
              <span style={{ marginLeft:"auto", fontSize:10, color: isDisabled ? "#B0C4D4" : "#4A4A4A" }}>{catOpen ? "▾" : "▸"}</span>
            </div>
            {catOpen && cat.groups.map(grp => {
              const grpOpen = expandedGroups.has(grp.id);
              return (
                <div key={grp.id}>
                  <div
                    title={disabledTitle}
                    style={{ display:"flex", alignItems:"center", gap:6, padding:"7px 12px 7px 26px", cursor: isDisabled ? "not-allowed" : "pointer", fontSize:11.5, fontWeight:600, color: isDisabled ? "#B0C4D4" : "#1A1A1A", background: (!isDisabled && (grpOpen || hovered===grp.id)) ? "#F0F7FB" : "transparent" }}
                    onClick={() => { if(isDisabled) return; setExpandedGroups(s => { const n=new Set(s); if(n.has(grp.id)) n.delete(grp.id); else n.add(grp.id); return n; }); }}
                    onMouseEnter={() => { if(!isDisabled) setHovered(grp.id); }}
                    onMouseLeave={() => setHovered(null)}
                  >
                    <span style={{ flex:1 }}>{grp.label}</span>
                    <span style={{ fontSize:10, color: isDisabled ? "#B0C4D4" : "#4A4A4A" }}>{grpOpen ? "▾" : "▸"}</span>
                  </div>
                  {grpOpen && grp.leaves.map(leaf => {
                    const isSelected = selectedLeaf === leaf.id;
                    const isLeafDisabled = isDisabled || (leaf.id === "agt-ps" && !agentHostId);
                    const leafTitle = isLeafDisabled && !isDisabled ? "Requires an agent host" : disabledTitle;
                    return (
                      <div
                        key={leaf.id}
                        title={leafTitle}
                        style={{
                          display:"flex", alignItems:"center", gap:6,
                          padding:"6px 12px 6px 40px",
                          cursor: isLeafDisabled ? "not-allowed" : "pointer", fontSize:11,
                          color: isLeafDisabled ? "#B0C4D4" : (isSelected ? cat.color : "#2C2C2C"),
                          fontWeight: isSelected ? 700 : 500,
                          background: isLeafDisabled ? "transparent" : (isSelected ? cat.bg : (hovered===leaf.id ? "#F0F7FB" : "transparent")),
                        }}
                        onClick={() => { if(isLeafDisabled) return; selectLeaf(leaf.id); }}
                        onMouseEnter={() => { if(!isLeafDisabled) setHovered(leaf.id); }}
                        onMouseLeave={() => setHovered(null)}
                      >
                        <span style={{ width:4, height:4, borderRadius:"50%", background: isLeafDisabled ? "#B0C4D4" : (isSelected ? cat.color : "#9AAAB8"), flexShrink:0, display:"inline-block" }} />
                        <span>{leaf.label}</span>
                      </div>
                    );
                  })}
                </div>
              );
            })}
          </div>
        );
      })}
    </div>
  );

  // ── Field Pane ────────────────────────────────────────────────────────────
  const leafMeta = selectedLeaf ? LEAF_META[selectedLeaf] : null;
  const catObj   = leafMeta ? MONITOR_TREE.find(c=>c.id===leafMeta.catId) : null;
  const grpObj   = catObj ? catObj.groups.find(g=>g.id===leafMeta.groupId) : null;
  const leafObj  = grpObj ? grpObj.leaves.find(l=>l.id===selectedLeaf) : null;

  const g2 = { display:"grid", gridTemplateColumns:"1fr 1fr", gap:8, marginBottom:8 };
  const g3 = { display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:8, marginBottom:8 };
  const g1 = { display:"grid", gridTemplateColumns:"1fr", gap:8, marginBottom:8 };

  const basicSettings = (
    <div style={{ marginBottom:8 }}>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8, marginBottom:8 }}>
        <Input label="Monitor Name *" value={form.MonitorName} onChange={e=>f("MonitorName",e.target.value)} hint="e.g. prod-web-01" tabIndex={1} />
        <GroupAutocomplete
          value={form.GroupName}
          groups={groups}
          onChange={(name) => f("GroupName", name)}
          tabIndex={2}
        />
      </div>
      <div style={{ marginBottom:8 }}>
        <label style={{ fontSize:11, color:c.textMuted, fontWeight:600, letterSpacing:"0.05em", textTransform:"uppercase", display:"block", marginBottom:4 }}>Agent Host</label>
        <select value={agentHostId} onChange={e=>{ setAgentHostId(e.target.value); setIsDirty(true); }} tabIndex={3}
          style={{ background:c.surface, border:`1px solid ${c.border}`, borderRadius:6, padding:"8px 12px", color:c.text, fontSize:13, width:"100%", outline:"none" }}>
          <option value="">(None — VPS monitors directly)</option>
          {agentHosts.map(a => {
            const online = a.LastSeenAt && (Date.now() - new Date(a.LastSeenAt + (!a.LastSeenAt.endsWith('Z') ? 'Z' : '')).getTime()) < 5*60*1000;
            return <option key={a.AgentHostId} value={String(a.AgentHostId)}>{online ? "● " : "○ "}{a.Name} ({online ? "online" : "offline"})</option>;
          })}
        </select>
      </div>
      {!agentHostId && ["infra-cpu","infra-mem","infra-disk","infra-diskio","infra-net"].includes(selectedLeaf) && (
        <div style={{ borderLeft:'3px solid #E89A2E', background:'#FDF3E0', padding:'10px 12px', borderRadius:6, marginTop:8, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div><strong style={{color:'#E89A2E'}}>⚠ Direct mode</strong> — the portal server runs this check directly over the network.</div>
          <div><strong>Requirements for the target machine:</strong></div>
          <div>• Network-reachable from the server running OP1</div>
          <div>• WMI remoting enabled (TCP 135 + dynamic RPC ports open in firewall)</div>
          <div>• Valid Windows credentials configured in the Credential Vault</div>
          <div>• Not suitable for machines behind NAT or a firewall you don't control</div>
          <div><span style={{color:'#006D8C'}}>→ For firewalled or remote machines, select an Agent Host above instead.</span></div>
        </div>
      )}
      {!agentHostId && !["infra-cpu","infra-mem","infra-disk","infra-diskio","infra-net"].includes(selectedLeaf) && (
        <div style={{ borderLeft:'3px solid #006D8C', background:'#E0F2F7', padding:'10px 12px', borderRadius:6, marginTop:8, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div><strong style={{color:'#006D8C'}}>ⓘ Direct mode</strong> — the portal server runs this check over the network.</div>
          <div>• Works for any host reachable from the server running OP1</div>
          <div>• For hosts on private or firewalled networks, use an Agent Host instead</div>
        </div>
      )}
      {!!agentHostId && (
        <div style={{ borderLeft:'3px solid #008C6F', background:'#DCF2EA', padding:'10px 12px', borderRadius:6, marginTop:8, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div><strong style={{color:'#008C6F'}}>✓ Agent mode</strong> — checks run locally on the agent machine and report back.</div>
          <div>• Works through firewalls — no inbound network access required</div>
          <div>• The agent machine must be online and checking in (see Agents screen)</div>
          <div>• WMI resource monitors query the agent's local machine only</div>
        </div>
      )}
      <div style={{ marginBottom:8 }}>
        <Sel label="Parent Monitor" value={form.ParentMonitorID||""} onChange={e=>f("ParentMonitorID",e.target.value)} tabIndex={4}
          options={[{value:"",label:"— None —"},...allMons.filter(m=>!monitor||m.MonitorID!==monitor.MonitorID).map(m=>({value:m.MonitorID,label:m.MonitorName}))]} />
      </div>
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:8, marginBottom:8 }}>
        <Sel label="Check Frequency" value={form.CheckFrequencySeconds} onChange={e=>f("CheckFrequencySeconds",e.target.value)} options={FREQ_OPTS} tabIndex={5} />
        <Input label="Failure Threshold" type="number" value={form.FailureThreshold} onChange={e=>f("FailureThreshold",e.target.value)} tabIndex={6} />
        <Input label="Timeout (ms)" type="number" value={form.TimeoutMs} onChange={e=>f("TimeoutMs",e.target.value)} tabIndex={7} />
      </div>
      <div style={{ marginBottom:8 }}>
        <Toggle label="Enable Anomaly Detection" checked={form.EnableAnomalyDetection} onChange={v=>f("EnableAnomalyDetection",v)} />
      </div>
      {form.EnableAnomalyDetection && (
        <div style={{ marginBottom:8 }}>
          <Input label="Baseline Days" type="number" value={form.AnomalyBaselineDays||7} onChange={e=>f("AnomalyBaselineDays",e.target.value)} tabIndex={8} />
        </div>
      )}
    </div>
  );

  // Type-specific field sets
  let typeFields = null;

  if (selectedLeaf === "infra-ping") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hint="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hint="e.g. 500" />
            <Input label="Packet Loss Warn (%)" type="number" value={tc.PacketLossWarnPct||""} onChange={e=>t("PacketLossWarnPct",e.target.value)} hint="e.g. 10" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-tcp") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hint="e.g. 80" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hint="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hint="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-svc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            {agentHostId
              ? <div style={{ gridColumn:"1/-1", fontSize:11, color:"#4A4A4A", fontStyle:"italic", marginBottom:4 }}>ℹ️ Agent monitors this host directly — no IP address required.</div>
              : <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            }
            {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"Agent"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","SNMP","Agent"]} />}
            {!agentHostId && infraCredOptions.length > 0 && (
              <div style={{ gridColumn:"1/-1" }}>
                <label style={{ fontSize:11, color:c.textMuted, fontWeight:600, letterSpacing:"0.05em", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
                <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }} style={{ background:c.surface, border:`1px solid ${c.border}`, borderRadius:6, padding:"8px 12px", color:c.text, fontSize:13, width:"100%", outline:"none" }}>
                  <option value="">— No credential selected —</option>
                  {infraCredOptions.map(cr=><option key={cr.CredentialID} value={cr.CredentialID}>{cr.Name} ({cr.Username})</option>)}
                </select>
              </div>
            )}
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <WmiCombo label="Service Name / Pattern" value={tc.ServiceName||""} onChange={v=>t("ServiceName",v)} hint="e.g. Spooler, MSSQLSERVER" discoveryType="services" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Restart Count Warn (per hour)" type="number" value={tc.RestartCountWarn||""} onChange={e=>t("RestartCountWarn",e.target.value)} hint="e.g. 3" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-cpu") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="CPU Warn (%)" type="number" value={tc.CpuWarnPct||""} onChange={e=>t("CpuWarnPct",e.target.value)} hint="e.g. 80" />
            <Input label="CPU Crit (%)" type="number" value={tc.CpuCritPct||""} onChange={e=>t("CpuCritPct",e.target.value)} hint="e.g. 95" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Options" alwaysOpen={true}>
          <Toggle label="Per-core monitoring" checked={tc.PerCoreMonitoring==="true"} onChange={v=>t("PerCoreMonitoring",v?"true":"")} />
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-mem") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Memory Warn (%)" type="number" value={tc.MemWarnPct||""} onChange={e=>t("MemWarnPct",e.target.value)} hint="e.g. 80" />
            <Input label="Memory Crit (%)" type="number" value={tc.MemCritPct||""} onChange={e=>t("MemCritPct",e.target.value)} hint="e.g. 95" />
            <Input label="Paging Warn (%)" type="number" value={tc.PagingWarnPct||""} onChange={e=>t("PagingWarnPct",e.target.value)} hint="e.g. 50" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-disk") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <WmiCombo label="Drive / Mount Point" value={tc.DrivePath||""} onChange={v=>t("DrivePath",v)} hint="e.g. C: or D:" discoveryType="drives" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Free Space Warn (%)" type="number" value={tc.FreeWarnPct||""} onChange={e=>t("FreeWarnPct",e.target.value)} hint="e.g. 20" />
            <Input label="Free Space Crit (%)" type="number" value={tc.FreeCritPct||""} onChange={e=>t("FreeCritPct",e.target.value)} hint="e.g. 10" />
            <div style={{ gridColumn:"1/-1" }}>
              <Toggle label="Growth trend alert" checked={tc.GrowthTrendAlert==="true"} onChange={v=>t("GrowthTrendAlert",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-diskio") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Reads/sec Warn" type="number" value={tc.ReadsWarn||""} onChange={e=>t("ReadsWarn",e.target.value)} hint="e.g. 500" />
            <Input label="Writes/sec Warn" type="number" value={tc.WritesWarn||""} onChange={e=>t("WritesWarn",e.target.value)} hint="e.g. 500" />
            <Input label="Queue Depth Warn" type="number" value={tc.QueueDepthWarn||""} onChange={e=>t("QueueDepthWarn",e.target.value)} hint="e.g. 10" />
            <Input label="Latency Warn (ms)" type="number" value={tc.LatencyWarnMs||""} onChange={e=>t("LatencyWarnMs",e.target.value)} hint="e.g. 20" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-net") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            {agentHostId
              ? <div style={{ gridColumn:"1/-1", fontSize:11, color:"#4A4A4A", fontStyle:"italic", marginBottom:4 }}>ℹ️ Agent monitors this host directly — no IP address required.</div>
              : <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            }
            {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","SNMP","Agent"]} />}
            {!agentHostId && infraCredOptions.length > 0 && (
              <div style={{ gridColumn:"1/-1" }}>
                <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
                <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }}
                  style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
                  <option value="">— No credential selected —</option>
                  {infraCredOptions.map(c=><option key={c.CredentialID} value={c.CredentialID}>{c.Name} ({c.CredentialType} · {c.Username})</option>)}
                </select>
              </div>
            )}
            <div style={{ gridColumn:"1/-1" }}>
              <WmiCombo label="Interface Name (optional)" value={tc.InterfaceName||""} onChange={v=>t("InterfaceName",v)} hint="Leave blank to use first adapter" discoveryType="interfaces" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Throughput In Warn (Mbps)" type="number" value={tc.ThroughputInWarn||""} onChange={e=>t("ThroughputInWarn",e.target.value)} hint="e.g. 900" />
            <Input label="Throughput Out Warn (Mbps)" type="number" value={tc.ThroughputOutWarn||""} onChange={e=>t("ThroughputOutWarn",e.target.value)} hint="e.g. 900" />
            <Input label="Error Rate Warn (%)" type="number" value={tc.ErrorRateWarn||""} onChange={e=>t("ErrorRateWarn",e.target.value)} hint="e.g. 1" />
            <Input label="Drop Rate Warn (%)" type="number" value={tc.DropRateWarn||""} onChange={e=>t("DropRateWarn",e.target.value)} hint="e.g. 1" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-proc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <WmiCombo label="Process Name / Pattern" value={tc.ProcessName||""} onChange={v=>t("ProcessName",v)} hint="e.g. nginx, svchost, java" discoveryType="processes" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="CPU Warn (%)" type="number" value={tc.ProcCpuWarn||""} onChange={e=>t("ProcCpuWarn",e.target.value)} hint="e.g. 80" />
            <Input label="Memory Warn (MB)" type="number" value={tc.ProcMemWarnMb||""} onChange={e=>t("ProcMemWarnMb",e.target.value)} hint="e.g. 512" />
            <Input label="Instance Count Min" type="number" value={tc.InstanceMin||""} onChange={e=>t("InstanceMin",e.target.value)} hint="e.g. 1" />
            <Input label="Instance Count Max" type="number" value={tc.InstanceMax||""} onChange={e=>t("InstanceMax",e.target.value)} hint="e.g. 10" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-log") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
            {agentHostId
              ? <div style={{ gridColumn:"1/-1", fontSize:11, color:"#4A4A4A", fontStyle:"italic", marginBottom:4 }}>ℹ️ Agent monitors this host directly — no IP address required.</div>
              : <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            }
            {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","Agent"]} />}
            {!agentHostId && infraCredOptions.length > 0 && (
              <div style={{ gridColumn:"1/-1" }}>
                <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
                <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }}
                  style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
                  <option value="">— No credential selected —</option>
                  {infraCredOptions.map(c=><option key={c.CredentialID} value={c.CredentialID}>{c.Name} ({c.CredentialType} · {c.Username})</option>)}
                </select>
              </div>
            )}
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g2}>
            <Sel label="Log Source" value={tc.LogSource||"Application"} onChange={e=>t("LogSource",e.target.value)} options={["Application","System","Security"]} />
            <Sel label="Severity Filter" value={tc.SeverityFilter||"Error"} onChange={e=>t("SeverityFilter",e.target.value)} options={["Error","Warning","Any"]} />
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Pattern to Match (optional)" value={tc.MatchPattern||""} onChange={e=>t("MatchPattern",e.target.value)} hint="e.g. disk, failed, access denied" />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Warn threshold (events)" type="number" value={tc.WarnThreshold||"1"} onChange={e=>t("WarnThreshold",e.target.value)} hint="e.g. 1" />
            <Input label="Crit threshold (events)" type="number" value={tc.CritThreshold||"5"} onChange={e=>t("CritThreshold",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-hw") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
            {agentHostId
              ? <div style={{ gridColumn:"1/-1", fontSize:11, color:"#4A4A4A", fontStyle:"italic", marginBottom:4 }}>ℹ️ Agent monitors this host directly — no IP address required.</div>
              : <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            }
            {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","Agent"]} />}
            {!agentHostId && infraCredOptions.length > 0 && (
              <div style={{ gridColumn:"1/-1" }}>
                <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Credential Vault (optional)</label>
                <select value={credentialId} onChange={e=>{ setCredentialId(e.target.value); setIsDirty(true); }}
                  style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
                  <option value="">— No credential selected —</option>
                  {infraCredOptions.map(c=><option key={c.CredentialID} value={c.CredentialID}>{c.Name} ({c.CredentialType} · {c.Username})</option>)}
                </select>
              </div>
            )}
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={{ display:"flex", flexDirection:"column", gap:10, marginBottom:12 }}>
            <Toggle label="Check Temperature (MSAcpi_ThermalZoneTemperature)" checked={tc.CheckTemperature==="true"} onChange={v=>t("CheckTemperature",v?"true":"")} />
            <Toggle label="Check RAID / Disk Status (Win32_DiskDrive)" checked={tc.CheckRaidStatus==="true"} onChange={v=>t("CheckRaidStatus",v?"true":"")} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Temp Warn (°C)" type="number" value={tc.TempWarn||"70"} onChange={e=>t("TempWarn",e.target.value)} hint="e.g. 70" />
            <Input label="Temp Crit (°C)" type="number" value={tc.TempCrit||"85"} onChange={e=>t("TempCrit",e.target.value)} hint="e.g. 85" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-svc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hint="e.g. 8080" />
            <Sel label="Check Method" value={tc.CheckMethod||"TCP"} onChange={e=>t("CheckMethod",e.target.value)} options={["TCP","HTTP","Process"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hint="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hint="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-api") {
    const httpMethod = tc.HttpMethod || "GET";
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Endpoint URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. https://api.example.com/health" />
            </div>
            <Sel label="HTTP Method" value={httpMethod} onChange={e=>t("HttpMethod",e.target.value)} options={["GET","POST","PUT","DELETE","PATCH","HEAD"]} />
            <Input label="Expected Status Code" type="number" value={tc.ExpectedStatus||"200"} onChange={e=>t("ExpectedStatus",e.target.value)} hint="e.g. 200" />
          </div>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:6 }}>Request Headers</div>
            {reqHeaders.map((h,i) => (
              <div key={i} style={{ display:"flex", gap:8, marginBottom:6 }}>
                <Input label="" value={h.key||""} onChange={e=>updHdr(i,"key",e.target.value)} placeholder="Header name" />
                <Input label="" value={h.value||""} onChange={e=>updHdr(i,"value",e.target.value)} placeholder="Value" />
                <button onClick={()=>delHdr(i)} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#D95C5C",cursor:"pointer",padding:"0 10px",fontSize:14,whiteSpace:"nowrap" }}>✕</button>
              </div>
            ))}
            <button onClick={addHdr} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#006D8C",cursor:"pointer",padding:"6px 12px",fontSize:12,marginTop:4 }}>+ Add Header</button>
          </div>
          {(httpMethod==="POST"||httpMethod==="PUT"||httpMethod==="PATCH") && (
            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4 }}>Request Body</div>
              <textarea style={{ width:"100%", minHeight:80, background:"#FFFFFF", border:"1px solid #C0D8E8", borderRadius:6, padding:"8px 12px", color:"#1A1A1A", fontSize:12, fontFamily:"monospace", resize:"vertical", boxSizing:"border-box" }} value={tc.RequestBody||""} onChange={e=>t("RequestBody",e.target.value)} />
              <div style={{ fontSize: 11, color: c.textDimmer, marginTop: 1 }}>e.g. {`{"key":"value"}`}</div>
            </div>
          )}
          <div style={g1}>
            <Input label="Expected Content Match (optional)" value={tc.ContentMatch||""} onChange={e=>t("ContentMatch",e.target.value)} hint="e.g. ok" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="RT p95 Warn (ms)" type="number" value={tc.RtP95WarnMs||""} onChange={e=>t("RtP95WarnMs",e.target.value)} placeholder="1000" />
            <Input label="RT p95 Crit (ms)" type="number" value={tc.RtP95CritMs||""} onChange={e=>t("RtP95CritMs",e.target.value)} placeholder="3000" />
            <Input label="Error Rate Warn (%)" type="number" value={tc.ErrorRateWarn||""} onChange={e=>t("ErrorRateWarn",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-port") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hint="e.g. 8080" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hint="e.g. 200" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-jvm") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            <Input label="Agent Endpoint URL" value={tc.AgentEndpoint||""} onChange={e=>t("AgentEndpoint",e.target.value)} placeholder="http://host:9090/metrics" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Heap Warn (%)" type="number" value={tc.HeapWarnPct||""} onChange={e=>t("HeapWarnPct",e.target.value)} placeholder="80" />
            <Input label="Heap Crit (%)" type="number" value={tc.HeapCritPct||""} onChange={e=>t("HeapCritPct",e.target.value)} placeholder="95" />
            <Input label="GC Pause Warn (ms)" type="number" value={tc.GcPauseWarnMs||""} onChange={e=>t("GcPauseWarnMs",e.target.value)} hint="e.g. 500" />
            <Input label="Thread Count Warn" type="number" value={tc.ThreadCountWarn||""} onChange={e=>t("ThreadCountWarn",e.target.value)} hint="e.g. 200" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-thread") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Agent Endpoint URL" value={tc.AgentEndpoint||""} onChange={e=>t("AgentEndpoint",e.target.value)} placeholder="http://host:9090/metrics" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Active Threads Warn" type="number" value={tc.ActiveThreadsWarn||""} onChange={e=>t("ActiveThreadsWarn",e.target.value)} placeholder="50" />
            <Input label="Queue Depth Warn" type="number" value={tc.QueueDepthWarn||""} onChange={e=>t("QueueDepthWarn",e.target.value)} placeholder="100" />
            <Input label="Queue Depth Crit" type="number" value={tc.QueueDepthCrit||""} onChange={e=>t("QueueDepthCrit",e.target.value)} hint="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-queue") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Queue Endpoint / Host" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="amqp://host:5672" />
            <Input label="Queue Name" value={tc.QueueName||""} onChange={e=>t("QueueName",e.target.value)} placeholder="my-queue" />
            <div style={{ gridColumn:"1/-1" }}>
              <Sel label="Queue Type" value={tc.QueueType||"RabbitMQ"} onChange={e=>t("QueueType",e.target.value)} options={["RabbitMQ","Kafka","SQS","Azure Service Bus"]} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Queue Depth Warn" type="number" value={tc.QueueDepthWarn||""} onChange={e=>t("QueueDepthWarn",e.target.value)} placeholder="1000" />
            <Input label="Queue Depth Crit" type="number" value={tc.QueueDepthCrit||""} onChange={e=>t("QueueDepthCrit",e.target.value)} placeholder="5000" />
            <Input label="Consumer Lag Warn" type="number" value={tc.ConsumerLagWarn||""} onChange={e=>t("ConsumerLagWarn",e.target.value)} hint="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-cache") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Host" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="localhost" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} placeholder="6379" />
            <Sel label="Cache Type" value={tc.CacheType||"Redis"} onChange={e=>t("CacheType",e.target.value)} options={["Redis","Memcached"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Hit Ratio Warn (%)" type="number" value={tc.HitRatioWarn||""} onChange={e=>t("HitRatioWarn",e.target.value)} placeholder="80" />
            <Input label="Eviction Rate Warn" type="number" value={tc.EvictionRateWarn||""} onChange={e=>t("EvictionRateWarn",e.target.value)} placeholder="100" />
            <Input label="Memory Usage Warn (%)" type="number" value={tc.MemUsageWarnPct||""} onChange={e=>t("MemUsageWarnPct",e.target.value)} placeholder="80" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-kpi") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Agent Endpoint / API Endpoint" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://api.example.com/metrics" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Metric Name" value={tc.MetricName||""} onChange={e=>t("MetricName",e.target.value)} placeholder="orders_per_minute" />
            <Sel label="Aggregation" value={tc.Aggregation||"sum"} onChange={e=>t("Aggregation",e.target.value)} options={["sum","count","avg"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Value Min Warn" type="number" value={tc.ValueMinWarn||""} onChange={e=>t("ValueMinWarn",e.target.value)} />
            <Input label="Value Max Warn" type="number" value={tc.ValueMaxWarn||""} onChange={e=>t("ValueMaxWarn",e.target.value)} />
            <Input label="Value Min Crit" type="number" value={tc.ValueMinCrit||""} onChange={e=>t("ValueMinCrit",e.target.value)} />
            <Input label="Value Max Crit" type="number" value={tc.ValueMaxCrit||""} onChange={e=>t("ValueMaxCrit",e.target.value)} />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-applog") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Agent Endpoint / Log Path" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="/var/log/app.log" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Pattern to Match" value={tc.MatchPattern||""} onChange={e=>t("MatchPattern",e.target.value)} placeholder="ERROR|EXCEPTION" />
            <Sel label="Severity Filter" value={tc.SeverityFilter||"Error"} onChange={e=>t("SeverityFilter",e.target.value)} options={["Error","Warning","Any"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Occurrences Warn (per interval)" type="number" value={tc.OccurrencesWarn||""} onChange={e=>t("OccurrencesWarn",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-perfctr") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={agentHostId ? g2 : g3}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hint="e.g. 192.168.1.1" />
            {!agentHostId && <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>t("CheckMethod",e.target.value)} options={["WMI","Agent"]} />}
            <Input label="Counter Path" value={tc.CounterPath||""} onChange={e=>t("CounterPath",e.target.value)} placeholder="\Processor(_Total)\% Processor Time" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Value Warn" type="number" value={tc.ValueWarn||""} onChange={e=>t("ValueWarn",e.target.value)} />
            <Input label="Value Crit" type="number" value={tc.ValueCrit||""} onChange={e=>t("ValueCrit",e.target.value)} />
            <div style={{ gridColumn:"1/-1" }}>
              <Toggle label="Delta / Rate" checked={tc.DeltaRate==="true"} onChange={v=>t("DeltaRate",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-conn") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Connection Pool Warn (%)" type="number" value={tc.ConnPoolWarnPct||""} onChange={e=>t("ConnPoolWarnPct",e.target.value)} placeholder="80" />
            <Input label="Connection Pool Crit (%)" type="number" value={tc.ConnPoolCritPct||""} onChange={e=>t("ConnPoolCritPct",e.target.value)} placeholder="95" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-sess") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Active Sessions Warn" type="number" value={tc.ActiveSessWarn||""} onChange={e=>t("ActiveSessWarn",e.target.value)} placeholder="100" />
            <Input label="Active Sessions Crit" type="number" value={tc.ActiveSessCrit||""} onChange={e=>t("ActiveSessCrit",e.target.value)} hint="e.g. 200" />
            <Input label="Blocked Sessions Warn" type="number" value={tc.BlockedSessWarn||""} onChange={e=>t("BlockedSessWarn",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-block") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Blocking Chain Length Warn" type="number" value={tc.BlockingChainWarn||""} onChange={e=>t("BlockingChainWarn",e.target.value)} hint="e.g. 5" />
            <Input label="Lock Wait Time Warn (ms)" type="number" value={tc.LockWaitWarnMs||""} onChange={e=>t("LockWaitWarnMs",e.target.value)} placeholder="5000" />
            <Input label="Deadlock Count Warn (per hr)" type="number" value={tc.DeadlockWarn||""} onChange={e=>t("DeadlockWarn",e.target.value)} hint="e.g. 1" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-wait") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Wait Event Category Filter (optional)" value={tc.WaitCategory||""} onChange={e=>t("WaitCategory",e.target.value)} placeholder="LOCK, NETWORK_IO, etc." />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Total Wait Time Warn (ms)" type="number" value={tc.WaitTimeWarnMs||""} onChange={e=>t("WaitTimeWarnMs",e.target.value)} placeholder="10000" />
            <Input label="Wait Count Warn (per interval)" type="number" value={tc.WaitCountWarn||""} onChange={e=>t("WaitCountWarn",e.target.value)} placeholder="50" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-longq") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Query Duration Warn (sec)" type="number" value={tc.QueryDurWarnSec||""} onChange={e=>t("QueryDurWarnSec",e.target.value)} placeholder="30" />
            <Input label="Query Duration Crit (sec)" type="number" value={tc.QueryDurCritSec||""} onChange={e=>t("QueryDurCritSec",e.target.value)} placeholder="120" />
            <Input label="Long Query Count Warn" type="number" value={tc.LongQueryCountWarn||""} onChange={e=>t("LongQueryCountWarn",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-perfctr") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Counter Name" value={tc.CounterName||""} onChange={e=>t("CounterName",e.target.value)} placeholder="Buffer cache hit ratio" />
            <div style={{ display:"flex", alignItems:"flex-end", paddingBottom:6 }}>
              <Toggle label="Delta / Rate" checked={tc.DeltaRate==="true"} onChange={v=>t("DeltaRate",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Value Warn" type="number" value={tc.ValueWarn||""} onChange={e=>t("ValueWarn",e.target.value)} />
            <Input label="Value Crit" type="number" value={tc.ValueCrit||""} onChange={e=>t("ValueCrit",e.target.value)} />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-ts") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Tablespace / Filegroup Name (blank = all)" value={tc.TablespaceName||""} onChange={e=>t("TablespaceName",e.target.value)} placeholder="PRIMARY" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Free Space Warn (%)" type="number" value={tc.FreeWarnPct||""} onChange={e=>t("FreeWarnPct",e.target.value)} hint="e.g. 20" />
            <Input label="Free Space Crit (%)" type="number" value={tc.FreeCritPct||""} onChange={e=>t("FreeCritPct",e.target.value)} hint="e.g. 10" />
            <Input label="Growth Rate Warn (MB/day)" type="number" value={tc.GrowthRateWarnMbDay||""} onChange={e=>t("GrowthRateWarnMbDay",e.target.value)} placeholder="1000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-txlog") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Log Usage Warn (%)" type="number" value={tc.LogUsageWarnPct||""} onChange={e=>t("LogUsageWarnPct",e.target.value)} placeholder="70" />
            <Input label="Log Usage Crit (%)" type="number" value={tc.LogUsageCritPct||""} onChange={e=>t("LogUsageCritPct",e.target.value)} placeholder="90" />
            <Input label="Write Rate Warn (MB/sec)" type="number" value={tc.WriteRateWarnMbSec||""} onChange={e=>t("WriteRateWarnMbSec",e.target.value)} placeholder="100" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-query") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4 }}>SQL Query</div>
            <textarea style={{ width:"100%", minHeight:80, background:"#FFFFFF", border:"1px solid #C0D8E8", borderRadius:6, padding:"8px 12px", color:"#1A1A1A", fontSize:12, fontFamily:"monospace", resize:"vertical", boxSizing:"border-box" }} value={tc.SqlQuery||""} onChange={e=>t("SqlQuery",e.target.value)} placeholder="SELECT COUNT(*) FROM orders WHERE status='pending'" />
          </div>
          <div style={g1}>
            <Sel label="Result Type" value={tc.ResultType||"Row count"} onChange={e=>t("ResultType",e.target.value)} options={[
              {value:"Row count",    label:"Row Count - warn if outside min/max range"},
              {value:"Single value", label:"Single Value - warn if value exceeds threshold"},
              {value:"Content match",label:"Content Match - warn if value does not match string"},
            ]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          {(tc.ResultType||"Row count") === "Row count" && (
            <div>
              <div style={{ fontSize:11, color:"#6B6B6B", marginBottom:10 }}>Warn if query returns fewer or more rows than the expected range. Leave blank to skip.</div>
              <div style={g2}>
                <Input label="Minimum expected rows" type="number" value={tc.RowCountMinWarn||""} onChange={e=>t("RowCountMinWarn",e.target.value)} placeholder="(no minimum)" />
                <Input label="Maximum expected rows" type="number" value={tc.RowCountMaxWarn||""} onChange={e=>t("RowCountMaxWarn",e.target.value)} placeholder="(no maximum)" />
              </div>
            </div>
          )}
          {(tc.ResultType||"") === "Single value" && (
            <div>
              <div style={{ fontSize:11, color:"#6B6B6B", marginBottom:10 }}>Query must return a single numeric value (e.g. SELECT count(*) or SELECT avg(duration))</div>
              <div style={g2}>
                <Input label="Warning threshold" type="number" value={tc.ValueWarn||""} onChange={e=>t("ValueWarn",e.target.value)} placeholder="Warn if value exceeds this" />
                <Input label="Critical threshold" type="number" value={tc.ValueCrit||""} onChange={e=>t("ValueCrit",e.target.value)} placeholder="Alert critical if value exceeds this" />
              </div>
            </div>
          )}
          {(tc.ResultType||"") === "Content match" && (
            <div>
              <div style={{ fontSize:11, color:"#6B6B6B", marginBottom:10 }}>Query must return a single text value - monitor fails if the value does not match.</div>
              <div style={g1}>
                <Input label="Expected value" value={tc.ContentMatchStr||""} onChange={e=>t("ContentMatchStr",e.target.value)} placeholder="expected result string" />
              </div>
            </div>
          )}
        </CollapsibleSection>
        <CollapsibleSection title="Options" alwaysOpen={true}>
          <Toggle label="Delta / Rate" checked={tc.DeltaRate==="true"} onChange={v=>t("DeltaRate",v?"true":"")} />
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-sqlctr") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4 }}>SQL Query</div>
            <textarea style={{ width:"100%", minHeight:80, background:"#FFFFFF", border:"1px solid #C0D8E8", borderRadius:6, padding:"8px 12px", color:"#1A1A1A", fontSize:12, fontFamily:"monospace", resize:"vertical", boxSizing:"border-box" }} value={tc.SqlQuery||""} onChange={e=>t("SqlQuery",e.target.value)} placeholder="SELECT COUNT(*) FROM orders" />
          </div>
          <Toggle label="Delta / Rate" checked={tc.DeltaRate==="true"} onChange={v=>t("DeltaRate",v?"true":"")} />
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={{ fontSize:11, color:"#6B6B6B", marginBottom:10 }}>Query must return a single numeric value (e.g. SELECT count(*) or SELECT avg(duration)…)</div>
          <div style={g2}>
            <Input label="Warning threshold" type="number" value={tc.ValueWarn||""} onChange={e=>t("ValueWarn",e.target.value)} placeholder="Warn if value exceeds this" />
            <Input label="Critical threshold" type="number" value={tc.ValueCrit||""} onChange={e=>t("ValueCrit",e.target.value)} placeholder="Alert critical if value exceeds this" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-http") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://example.com" />
            </div>
            <Input label="Expected Status Code" type="number" value={tc.ExpectedStatus||"200"} onChange={e=>t("ExpectedStatus",e.target.value)} hint="e.g. 200" />
            <div style={{ display:"flex", alignItems:"flex-end", paddingBottom:6 }}>
              <Toggle label="Follow Redirects" checked={tc.FollowRedirects!=="false"} onChange={v=>t("FollowRedirects",v?"true":"false")} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} placeholder="1000" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} placeholder="3000" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="SSL" alwaysOpen={true}>
          <div style={{ marginBottom:10 }}>
            <Toggle label="SSL Check" checked={tc.SslCheck==="true"} onChange={v=>t("SslCheck",v?"true":"")} />
          </div>
          {tc.SslCheck==="true" && (
            <div style={{ maxWidth:220 }}>
              <Input label="Expiry Warning (days)" type="number" value={tc.SslExpiryWarnDays||"30"} onChange={e=>t("SslExpiryWarnDays",e.target.value)} placeholder="30" />
            </div>
          )}
        </CollapsibleSection>
        {customHdrsUI}
      </div>
    );
  } else if (selectedLeaf === "web-ssl") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Hostname (no protocol)" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="example.com" />
            <Input label="Port" type="number" value={form.Port||"443"} onChange={e=>f("Port",e.target.value)} placeholder="443" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Expiry Warn (days)" type="number" value={tc.ExpiryWarnDays||"30"} onChange={e=>t("ExpiryWarnDays",e.target.value)} placeholder="30" />
            <Input label="Expiry Crit (days)" type="number" value={tc.ExpiryCritDays||"7"} onChange={e=>t("ExpiryCritDays",e.target.value)} placeholder="7" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Options" alwaysOpen={true}>
          <Toggle label="Check Full Chain Validity" checked={tc.CheckChain==="true"} onChange={v=>t("CheckChain",v?"true":"")} />
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-dns") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Hostname to Resolve" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="example.com" />
            <Input label="DNS Server (optional)" value={tc.DnsServer||""} onChange={e=>t("DnsServer",e.target.value)} placeholder="Optional — leave blank to use system resolver" />
            <Input label="Expected IP (optional)" value={tc.ExpectedIp||""} onChange={e=>t("ExpectedIp",e.target.value)} placeholder="Optional — leave blank to skip IP validation" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Resolution Time Warn (ms)" type="number" value={tc.ResTimeWarnMs||""} onChange={e=>t("ResTimeWarnMs",e.target.value)} hint="e.g. 500" />
            <Input label="Resolution Time Crit (ms)" type="number" value={tc.ResTimeCritMs||""} onChange={e=>t("ResTimeCritMs",e.target.value)} placeholder="2000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-redir") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Start URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="http://example.com" />
            <Input label="Expected Final URL" value={tc.ExpectedFinalUrl||""} onChange={e=>t("ExpectedFinalUrl",e.target.value)} placeholder="https://www.example.com/" />
            <Input label="Max Redirect Hops" type="number" value={tc.MaxRedirectHops||"5"} onChange={e=>t("MaxRedirectHops",e.target.value)} hint="e.g. 5" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Total Redirect Time Warn (ms)" type="number" value={tc.RedirectTimeWarnMs||""} onChange={e=>t("RedirectTimeWarnMs",e.target.value)} placeholder="2000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-cm") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://example.com" />
            </div>
            <Input label="Expected Status Code" type="number" value={tc.ExpectedStatus||"200"} onChange={e=>t("ExpectedStatus",e.target.value)} hint="e.g. 200" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g1}>
            <Input label="Content Match String *" value={tc.ContentMatchString||""} onChange={e=>t("ContentMatchString",e.target.value)} placeholder="Welcome to Example" />
            <div style={{ display:"flex", gap:24 }}>
              <Toggle label="Case Sensitive" checked={tc.CaseSensitive==="true"} onChange={v=>t("CaseSensitive",v?"true":"")} />
              <Toggle label="Regex" checked={tc.UseRegex==="true"} onChange={v=>t("UseRegex",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} placeholder="2000" />
          </div>
        </CollapsibleSection>
        {customHdrsUI}
      </div>
    );
  } else if (selectedLeaf === "web-perf") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g1}>
            <Input label="URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://example.com" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="TTFB Warn (ms)" type="number" value={tc.TtfbWarnMs||""} onChange={e=>t("TtfbWarnMs",e.target.value)} hint="e.g. 200" />
            <Input label="DOM Load Warn (ms)" type="number" value={tc.DomLoadWarnMs||""} onChange={e=>t("DomLoadWarnMs",e.target.value)} placeholder="1500" />
            <Input label="Full Load Warn (ms)" type="number" value={tc.FullLoadWarnMs||""} onChange={e=>t("FullLoadWarnMs",e.target.value)} placeholder="3000" />
            <Input label="Full Load Crit (ms)" type="number" value={tc.FullLoadCritMs||""} onChange={e=>t("FullLoadCritMs",e.target.value)} placeholder="6000" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Core Web Vitals" alwaysOpen={true}>
          <div style={g3}>
            <Input label="LCP Warn (ms)" type="number" value={tc.LcpWarnMs||""} onChange={e=>t("LcpWarnMs",e.target.value)} placeholder="2500" />
            <Input label="CLS Score Warn" type="number" value={tc.ClsWarn||""} onChange={e=>t("ClsWarn",e.target.value)} placeholder="0.1" />
            <Input label="FID Warn (ms)" type="number" value={tc.FidWarnMs||""} onChange={e=>t("FidWarnMs",e.target.value)} placeholder="100" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-headers") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://example.com" />
            <Sel label="HTTP Method" value={tc.HttpMethod||"GET"} onChange={e=>t("HttpMethod",e.target.value)} options={["GET","HEAD","POST"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Header Name to Check" value={tc.HeaderName||""} onChange={e=>t("HeaderName",e.target.value)} placeholder="Content-Security-Policy" />
            <Input label="Expected Value / Pattern" value={tc.HeaderExpected||""} onChange={e=>t("HeaderExpected",e.target.value)} placeholder="default-src 'self'" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g1}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} placeholder="1000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "web-apiresp") {
    const apiHttpMethod = tc.HttpMethod || "GET";
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="API URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://api.example.com/endpoint" />
            </div>
            <Sel label="HTTP Method" value={apiHttpMethod} onChange={e=>t("HttpMethod",e.target.value)} options={["GET","POST","PUT","DELETE","PATCH"]} />
            <Input label="Expected Status Code" type="number" value={tc.ExpectedStatus||"200"} onChange={e=>t("ExpectedStatus",e.target.value)} hint="e.g. 200" />
          </div>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:6 }}>Request Headers</div>
            {reqHeaders.map((h,i) => (
              <div key={i} style={{ display:"flex", gap:8, marginBottom:6 }}>
                <Input label="" value={h.key||""} onChange={e=>updHdr(i,"key",e.target.value)} placeholder="Header name" />
                <Input label="" value={h.value||""} onChange={e=>updHdr(i,"value",e.target.value)} placeholder="Value" />
                <button onClick={()=>delHdr(i)} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#D95C5C",cursor:"pointer",padding:"0 10px",fontSize:14,whiteSpace:"nowrap" }}>✕</button>
              </div>
            ))}
            <button onClick={addHdr} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#006D8C",cursor:"pointer",padding:"6px 12px",fontSize:12,marginTop:4 }}>+ Add Header</button>
          </div>
          {(apiHttpMethod==="POST"||apiHttpMethod==="PUT"||apiHttpMethod==="PATCH") && (
            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4 }}>Request Body</div>
              <textarea style={{ width:"100%", minHeight:80, background:"#FFFFFF", border:"1px solid #C0D8E8", borderRadius:6, padding:"8px 12px", color:"#1A1A1A", fontSize:12, fontFamily:"monospace", resize:"vertical", boxSizing:"border-box" }} value={tc.RequestBody||""} onChange={e=>t("RequestBody",e.target.value)} />
              <div style={{ fontSize: 11, color: c.textDimmer, marginTop: 1 }}>e.g. {`{"key":"value"}`}</div>
            </div>
          )}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={g3}>
            <Sel label="Response Format" value={tc.ResponseFormat||"JSON"} onChange={e=>t("ResponseFormat",e.target.value)} options={["JSON","XML"]} />
            <Input label="JSONPath / XPath Expression" value={tc.PathExpression||""} onChange={e=>t("PathExpression",e.target.value)} placeholder="$.status" />
            <Input label="Expected Value" value={tc.ExpectedValue||""} onChange={e=>t("ExpectedValue",e.target.value)} placeholder="ok" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} placeholder="1000" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} placeholder="3000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-login") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Login URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://app.example.com/login" />
            </div>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Check Locations (comma-separated)" value={tc.Locations||""} onChange={e=>t("Locations",e.target.value)} placeholder="us-east,eu-west" />
            </div>
            <div style={{ gridColumn:"1/-1" }}>
              <Toggle label="Screenshot on Failure" checked={tc.ScreenshotOnFailure==="true"} onChange={v=>t("ScreenshotOnFailure",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Steps" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Username Selector" value={tc.UserSelector||""} onChange={e=>t("UserSelector",e.target.value)} placeholder="#username" />
            <Input label="Test Username" value={tc.TestUsername||""} onChange={e=>t("TestUsername",e.target.value)} placeholder="monitor@example.com" />
            <Input label="Password Selector" value={tc.PassSelector||""} onChange={e=>t("PassSelector",e.target.value)} placeholder="#password" />
            <Input label="Test Password" type="password" value={tc.TestPassword||""} onChange={e=>t("TestPassword",e.target.value)} />
            <Input label="Submit Selector" value={tc.SubmitSelector||""} onChange={e=>t("SubmitSelector",e.target.value)} placeholder="#login-btn" />
            <Input label="Success Indicator" value={tc.SuccessIndicator||""} onChange={e=>t("SuccessIndicator",e.target.value)} placeholder=".dashboard-header" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Total Flow Time Warn (ms)" type="number" value={tc.FlowTimeWarnMs||""} onChange={e=>t("FlowTimeWarnMs",e.target.value)} placeholder="5000" />
            <Input label="Total Flow Time Crit (ms)" type="number" value={tc.FlowTimeCritMs||""} onChange={e=>t("FlowTimeCritMs",e.target.value)} placeholder="15000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-web" || selectedLeaf === "syn-pay" || selectedLeaf === "syn-wf") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Base URL (optional)" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://app.example.com" />
            </div>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Check Locations (comma-separated)" value={tc.Locations||""} onChange={e=>t("Locations",e.target.value)} placeholder="us-east,eu-west" />
            </div>
            <Toggle label="Screenshot on Failure" checked={tc.ScreenshotOnFailure==="true"} onChange={v=>t("ScreenshotOnFailure",v?"true":"")} />
            {selectedLeaf === "syn-web" && (
              <Toggle label="Session Replay" checked={tc.SessionReplay==="true"} onChange={v=>t("SessionReplay",v?"true":"")} />
            )}
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Steps" alwaysOpen={true}>
          <div style={{ marginBottom:8 }}>
            {synthSteps.map((step, i) => (
              <div key={i} style={{ background:"#F0F7FB", border:"1px solid #C0D8E8", borderRadius:8, padding:"12px 14px", marginBottom:10 }}>
                <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:8 }}>
                  <span style={{ fontSize:11, fontWeight:700, color:"#4A4A4A" }}>Step {i+1}</span>
                  <button onClick={()=>delStep(i)} style={{ background:"none",border:"none",color:"#D95C5C",cursor:"pointer",fontSize:13 }}>✕ Remove</button>
                </div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
                  <Input label="Step Name" value={step.name||""} onChange={e=>updStep(i,"name",e.target.value)} placeholder="Click login" />
                  <Sel label="Action" value={step.action||"navigate"} onChange={e=>updStep(i,"action",e.target.value)} options={["navigate","click","type","assert"]} />
                  <Input label="URL" value={step.url||""} onChange={e=>updStep(i,"url",e.target.value)} placeholder="https://..." />
                  <Input label="Selector" value={step.selector||""} onChange={e=>updStep(i,"selector",e.target.value)} placeholder="#submit" />
                  <Input label="Value" value={step.value||""} onChange={e=>updStep(i,"value",e.target.value)} placeholder="Text to type or assert" />
                  <Input label="Wait (ms)" type="number" value={step.waitMs||""} onChange={e=>updStep(i,"waitMs",e.target.value)} hint="e.g. 500" />
                </div>
              </div>
            ))}
            <button onClick={addStep} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#006D8C",cursor:"pointer",padding:"8px 16px",fontSize:12,width:"100%" }}>+ Add Step</button>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Total Flow Time Warn (ms)" type="number" value={tc.FlowTimeWarnMs||""} onChange={e=>t("FlowTimeWarnMs",e.target.value)} placeholder="10000" />
            {(selectedLeaf==="syn-pay" || selectedLeaf==="syn-web") && (
              <Input label="Total Flow Time Crit (ms)" type="number" value={tc.FlowTimeCritMs||""} onChange={e=>t("FlowTimeCritMs",e.target.value)} placeholder="30000" />
            )}
            {selectedLeaf==="syn-web" && (
              <Input label="Any Step Time Warn (ms)" type="number" value={tc.StepTimeWarnMs||""} onChange={e=>t("StepTimeWarnMs",e.target.value)} placeholder="5000" />
            )}
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-apiwf") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Base URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="https://api.example.com" />
            </div>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Check Locations (comma-separated)" value={tc.Locations||""} onChange={e=>t("Locations",e.target.value)} placeholder="us-east,eu-west" />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Steps" alwaysOpen={true}>
          <div style={{ marginBottom:8 }}>
            {apiSteps.map((step, i) => (
              <div key={i} style={{ background:"#F0F7FB", border:"1px solid #C0D8E8", borderRadius:8, padding:"12px 14px", marginBottom:10 }}>
                <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:8 }}>
                  <span style={{ fontSize:11, fontWeight:700, color:"#4A4A4A" }}>API Step {i+1}</span>
                  <button onClick={()=>delApiStep(i)} style={{ background:"none",border:"none",color:"#D95C5C",cursor:"pointer",fontSize:13 }}>✕ Remove</button>
                </div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
                  <Input label="Step Name" value={step.name||""} onChange={e=>updApiStep(i,"name",e.target.value)} placeholder="Get token" />
                  <Sel label="Method" value={step.method||"GET"} onChange={e=>updApiStep(i,"method",e.target.value)} options={["GET","POST","PUT","DELETE","PATCH"]} />
                  <div style={{ gridColumn:"1/-1" }}>
                    <Input label="URL" value={step.url||""} onChange={e=>updApiStep(i,"url",e.target.value)} placeholder="/v1/auth/token" />
                  </div>
                  <Input label="Expected Status" value={step.expectedStatus||"200"} onChange={e=>updApiStep(i,"expectedStatus",e.target.value)} hint="e.g. 200" />
                  <Input label="Store As (variable)" value={step.storeAs||""} onChange={e=>updApiStep(i,"storeAs",e.target.value)} placeholder="token" />
                  <div style={{ gridColumn:"1/-1" }}>
                    <Input label="JSONPath Assertion" value={step.assertion||""} onChange={e=>updApiStep(i,"assertion",e.target.value)} placeholder="$.status == 'ok'" />
                  </div>
                </div>
              </div>
            ))}
            <button onClick={addApiStep} style={{ background:"none",border:`1px solid #C0D8E8`,borderRadius:6,color:"#006D8C",cursor:"pointer",padding:"8px 16px",fontSize:12,width:"100%" }}>+ Add API Step</button>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Total Workflow Time Warn (ms)" type="number" value={tc.WorkflowTimeWarnMs||""} onChange={e=>t("WorkflowTimeWarnMs",e.target.value)} placeholder="10000" />
            <Input label="Any Step Time Warn (ms)" type="number" value={tc.StepTimeWarnMs||""} onChange={e=>t("StepTimeWarnMs",e.target.value)} placeholder="3000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-sql") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true}>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4 }}>SQL Query</div>
            <textarea style={{ width:"100%", minHeight:80, background:"#FFFFFF", border:"1px solid #C0D8E8", borderRadius:6, padding:"8px 12px", color:"#1A1A1A", fontSize:12, fontFamily:"monospace", resize:"vertical", boxSizing:"border-box" }} value={tc.SqlQuery||""} onChange={e=>t("SqlQuery",e.target.value)} placeholder="SELECT status FROM health_check WHERE id=1" />
          </div>
          <div style={g1}>
            <Sel label="Expected Result Type" value={tc.ResultType||"Row count"} onChange={e=>t("ResultType",e.target.value)} options={["Row count","Value","Content match"]} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g3}>
            <Input label="Query Time Warn (ms)" type="number" value={tc.QueryTimeWarnMs||""} onChange={e=>t("QueryTimeWarnMs",e.target.value)} placeholder="5000" />
            <Input label="Value Warn" type="number" value={tc.ValueWarn||""} onChange={e=>t("ValueWarn",e.target.value)} />
            <Input label="Value Crit" type="number" value={tc.ValueCrit||""} onChange={e=>t("ValueCrit",e.target.value)} />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-ftp") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Host" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="files.example.com" />
            <Input label="Port" type="number" value={form.Port||"22"} onChange={e=>f("Port",e.target.value)} placeholder="22" />
            <Sel label="Protocol" value={tc.FtpProtocol||"SFTP"} onChange={e=>t("FtpProtocol",e.target.value)} options={["SFTP","FTP","FTPS"]} />
            <Input label="Username" value={tc.FtpUser||""} onChange={e=>t("FtpUser",e.target.value)} />
            <Input label="Password" type="password" value={tc.FtpPass||""} onChange={e=>t("FtpPass",e.target.value)} />
            <Input label="Remote Path" value={tc.RemotePath||""} onChange={e=>t("RemotePath",e.target.value)} placeholder="/uploads/test.txt" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Transfer Time Warn (ms)" type="number" value={tc.TransferTimeWarnMs||""} onChange={e=>t("TransferTimeWarnMs",e.target.value)} placeholder="5000" />
            <Input label="Transfer Time Crit (ms)" type="number" value={tc.TransferTimeCritMs||""} onChange={e=>t("TransferTimeCritMs",e.target.value)} placeholder="15000" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "syn-email") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true}>
          <div style={g2}>
            <Input label="SMTP Host" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} placeholder="smtp.example.com" />
            <Input label="SMTP Port" type="number" value={form.Port||"587"} onChange={e=>f("Port",e.target.value)} placeholder="587" />
            <Input label="Username" value={tc.SmtpUser||""} onChange={e=>t("SmtpUser",e.target.value)} placeholder="monitor@example.com" />
            <Input label="Password" type="password" value={tc.SmtpPass||""} onChange={e=>t("SmtpPass",e.target.value)} />
            <Input label="From Address" value={tc.FromAddress||""} onChange={e=>t("FromAddress",e.target.value)} placeholder="monitor@example.com" />
            <Input label="To Address" value={tc.ToAddress||""} onChange={e=>t("ToAddress",e.target.value)} placeholder="test@example.com" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="IMAP Check" alwaysOpen={true}>
          <div style={g2}>
            <Input label="IMAP Host" value={tc.ImapHost||""} onChange={e=>t("ImapHost",e.target.value)} placeholder="imap.example.com" />
            <Input label="IMAP Port" type="number" value={tc.ImapPort||"993"} onChange={e=>t("ImapPort",e.target.value)} placeholder="993" />
            <div style={{ gridColumn:"1/-1" }}>
              <Toggle label="Check Inbox for Reply" checked={tc.CheckInboxReply==="true"} onChange={v=>t("CheckInboxReply",v?"true":"")} />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true}>
          <div style={g2}>
            <Input label="Send Time Warn (ms)" type="number" value={tc.SendTimeWarnMs||""} onChange={e=>t("SendTimeWarnMs",e.target.value)} placeholder="3000" />
            {tc.CheckInboxReply==="true" && (
              <Input label="Round-Trip Time Warn (ms)" type="number" value={tc.RoundTripWarnMs||""} onChange={e=>t("RoundTripWarnMs",e.target.value)} placeholder="30000" />
            )}
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "agt-ps") {
    const PS_EXAMPLES = [
      { title:"Service Check", script:`# Returns JSON: { "IsSuccess": true/false, "Message": "...", "ResponseTimeMs": 123 }
$svc = Get-Service -Name "Spooler" -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -eq "Running") {
  @{ IsSuccess=$true; Message="Service is running"; ResponseTimeMs=0 } | ConvertTo-Json
} else {
  @{ IsSuccess=$false; Message="Service not running or not found" } | ConvertTo-Json
}` },
      { title:"Disk Space Check", script:`$drive = Get-PSDrive C
$freeGb = [Math]::Round($drive.Free / 1GB, 2)
$usedGb = [Math]::Round($drive.Used / 1GB, 2)
$totalGb = $freeGb + $usedGb
$pctFree = [Math]::Round(($freeGb / $totalGb) * 100, 1)
$ok = $pctFree -gt 10
@{ IsSuccess=$ok; Message="C: $freeGb GB free of $totalGb GB ($pctFree% free)"; ResponseTimeMs=0 } | ConvertTo-Json` },
      { title:"Process Memory Check", script:`$proc = Get-Process -Name "chrome" -ErrorAction SilentlyContinue | Sort-Object WorkingSet -Desc | Select-Object -First 1
if (!$proc) { @{ IsSuccess=$false; Message="Process not found" } | ConvertTo-Json; exit }
$mb = [Math]::Round($proc.WorkingSet64 / 1MB, 1)
$ok = $mb -lt 2048
@{ IsSuccess=$ok; Message="chrome using $mb MB RAM"; ResponseTimeMs=0 } | ConvertTo-Json` },
    ];
    typeFields = (
      <div>
        <CollapsibleSection title="PowerShell Script" alwaysOpen={true}>
          <div style={{ marginBottom:12 }}>
            <div style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:6 }}>POWERSHELL SCRIPT</div>
            <textarea
              value={psScript}
              onChange={e=>{ setPsScript(e.target.value); setIsDirty(true); }}
              rows={10}
              spellCheck={false}
              placeholder={"# Script must output JSON on the last line:\n# { \"IsSuccess\": true, \"Message\": \"All good\", \"ResponseTimeMs\": 42 }\n\n$result = Test-Connection -ComputerName localhost -Count 1 -Quiet\n@{ IsSuccess=$result; Message=if($result){\"Reachable\"}else{\"Unreachable\"}; ResponseTimeMs=0 } | ConvertTo-Json"}
              style={{ width:"100%", fontFamily:"'DM Mono', 'Cascadia Code', 'Consolas', monospace", fontSize:12, lineHeight:1.5, padding:"10px 12px", background:"#F5F0E8", border:"1px solid #C0D8E8", borderRadius:6, outline:"none", resize:"vertical", color:"#1A1A1A" }}
            />
          </div>
          <div style={{ display:"grid", gridTemplateColumns:"120px 1fr", gap:12, alignItems:"start" }}>
            <div>
              <div style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:6 }}>TIMEOUT (SECONDS)</div>
              <input type="number" min={5} max={300} value={psTimeout}
                onChange={e=>{ setPsTimeout(e.target.value); setIsDirty(true); }}
                style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #C0D8E8", borderRadius:4, outline:"none", background:"#fff" }}
              />
            </div>
            <div style={{ paddingTop:2 }}>
              <div style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:6 }}>SCRIPT OUTPUT FORMAT</div>
              <div style={{ fontSize:11, color:"#4A4A4A", lineHeight:1.5 }}>Script must write a JSON object to stdout on the final line with <code style={{ background:"#F0F7FB", padding:"1px 4px", borderRadius:3 }}>IsSuccess</code> (bool), <code style={{ background:"#F0F7FB", padding:"1px 4px", borderRadius:3 }}>Message</code> (string), and optionally <code style={{ background:"#F0F7FB", padding:"1px 4px", borderRadius:3 }}>ResponseTimeMs</code> (int).</div>
            </div>
          </div>
        </CollapsibleSection>
        <div style={{ borderTop:"1px solid #E8EDF2", marginTop:4 }}>
          <div
            style={{ display:"flex", alignItems:"center", gap:8, padding:"10px 0", cursor:"pointer", userSelect:"none" }}
            onClick={() => setPsExamplesOpen(v=>!v)}
          >
            <span style={{ fontSize:10, fontWeight:700, color:"#006D8C", textTransform:"uppercase", letterSpacing:"0.06em" }}>Example Scripts</span>
            <span style={{ fontSize:10, color:"#4A4A4A", marginLeft:"auto" }}>{psExamplesOpen ? "▾" : "▸"}</span>
          </div>
          {psExamplesOpen && (
            <div style={{ display:"flex", flexDirection:"column", gap:12, paddingBottom:16 }}>
              {PS_EXAMPLES.map((ex,i) => (
                <div key={i} style={{ background:"#F8FBFD", border:"1px solid #C0D8E8", borderRadius:6, overflow:"hidden" }}>
                  <div style={{ display:"flex", alignItems:"center", padding:"6px 12px", background:"#EAF3F8", borderBottom:"1px solid #C0D8E8" }}>
                    <span style={{ fontSize:11, fontWeight:600, color:"#1A1A1A", flex:1 }}>{ex.title}</span>
                    <button
                      type="button"
                      onClick={() => { setPsScript(ex.script); setIsDirty(true); }}
                      style={{ fontSize:10, padding:"3px 10px", background:"#006D8C", color:"#fff", border:"none", borderRadius:4, cursor:"pointer", fontWeight:600 }}
                    >Use</button>
                  </div>
                  <pre style={{ margin:0, padding:"10px 12px", fontSize:11, fontFamily:"'DM Mono','Cascadia Code','Consolas',monospace", color:"#1A1A1A", whiteSpace:"pre-wrap", wordBreak:"break-word", background:"#F5F0E8" }}>{ex.script}</pre>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    );
  }

  const CAT_BG = { infra:"#e0f2fe", app:"#f3e8ff", db:"#fef9c3", web:"#dcfce7", syn:"#f1f5f9", agt:"#e0f7fa" };

  const fieldPane = (
    <div className="dp op1-dense-form" style={{ flex:1, overflowY:"auto", padding:"20px 24px", background:"#FFFFFF" }}>
      {!selectedLeaf ? (
        <div style={{ fontSize:12, color:"#7A9AB8", marginBottom:16 }}>Select a monitor type from the tree on the left</div>
      ) : catObj && grpObj && leafObj ? (
        <div>
          <div style={{ borderLeft:`4px solid ${catObj.color}`, background:CAT_BG[leafMeta.catId] || "#F8FBFD", borderRadius:"0 6px 6px 0", padding:"8px 12px", marginBottom:16 }}>
            <div style={{ fontSize:11, fontWeight:600, color:"#1A1A1A", marginBottom:3 }}>
              <span>{catObj.label}</span>
              <span style={{ color:"#7A9AB8", margin:"0 5px" }}>›</span>
              <span>{grpObj.label}</span>
            </div>
            <div style={{ fontSize:15, fontWeight:700, color:"#1A1A1A" }}>{leafObj.label}</div>
          </div>
          {basicSettings}
          {typeFields}
        </div>
      ) : null}
    </div>
  );

  // ── Notification Pane ─────────────────────────────────────────────────────
  const notifPane = (
    <div className="dp op1-dense-form" style={{ background:"#FFFFFF", borderTop:"1px solid #C0D8E8", flexShrink:0 }}>
      {/* ── Notifications panel ── */}
      <div style={{ borderBottom:"0.5px solid #f1f5f9" }}>
        <div style={{ width:"100%", display:"flex", alignItems:"center", padding:"10px 20px" }}>
          <div style={{ display:"flex", alignItems:"center", gap:8 }}>
            <span style={{ fontSize:11, fontWeight:700, color:c.blue, letterSpacing:"0.08em", textTransform:"uppercase" }}>Notifications</span>
            {monitorAssignments.length > 0
              ? <span style={{ fontSize:10, borderRadius:10, padding:"1px 8px", background:c.blueLight, color:c.blue, fontWeight:600 }}>{monitorAssignments.length} recipient{monitorAssignments.length!==1?'s':''}</span>
              : <span style={{ fontSize:10, color:c.textMuted }}>None</span>
            }
          </div>
        </div>
        <div style={{ padding:"0 20px 14px" }}>
            {monitorAssignmentsLoading ? (
              <div style={{ fontSize:12, color:c.textMuted, padding:"8px 0" }}>Loading…</div>
            ) : monitorAssignments.length === 0 && !showAssignPicker ? (
              <div style={{ fontSize:12, color:c.textMuted, padding:"8px 0" }}>No recipients assigned. Add one below or manage all notifications in Config → Notifications.</div>
            ) : (
              <div style={{ display:"flex", flexDirection:"column", gap:6, marginBottom:8 }}>
                {monitorAssignments.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, whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" }}>{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>
                    <div style={{ display:"flex", alignItems:"center", gap:4, flexShrink:0 }}>
                      <label style={{ fontSize:10, color:c.textMuted }}>Min:</label>
                      <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}); loadMonitorAssignments(); }}>
                        <option value="warning">Warning+</option>
                        <option value="critical">Critical only</option>
                      </select>
                    </div>
                    <div style={{ display:"flex", alignItems:"center", gap:2, flexShrink:0 }}>
                      <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}); loadMonitorAssignments(); }}
                        onChange={e=>{ setMonitorAssignments(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>
                    <div onClick={async()=>{ await api("PUT",`/notifications/assignments/${a.AssignmentID||a.ID}`,{MinSeverity:a.MinSeverity,CooldownMinutes:a.CooldownMinutes,IsEnabled:!a.IsEnabled}); loadMonitorAssignments(); }}
                      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={async()=>{ if(!window.confirm('Remove this recipient from monitor?')) return; await api("DELETE",`/notifications/assignments/${a.AssignmentID||a.ID}`); loadMonitorAssignments(); }}
                      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>
            )}
            <div style={{ display:"flex", alignItems:"center", gap:10, marginTop:4 }}>
              <button onClick={()=>{ setShowAssignPicker(p=>!p); setAssignPickerRecipient(''); }}
                style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${c.blue}`, borderRadius:5, background:"#fff", color:c.blue, cursor:"pointer" }}>
                + Assign recipient
              </button>
              <span style={{ fontSize:11, color:c.textMuted }}>Recipients managed in <button onClick={()=>{}} style={{ background:"none", border:"none", color:c.blue, cursor:"pointer", fontSize:11, padding:0 }}>Config → Notifications ↗</button></span>
            </div>
            {showAssignPicker && (()=>{
              const unassigned = availableRecipients.filter(r=>!monitorAssignments.some(a=>a.RecipientID===r.RecipientID));
              return (
                <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 }}>{availableRecipients.length === 0 ? "No recipients configured. Add recipients in Config → Notifications." : "All recipients already assigned."}</span>
                  ) : (<>
                    <select value={assignPickerRecipient} onChange={e=>setAssignPickerRecipient(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={assignPickerSeverity} onChange={e=>setAssignPickerSeverity(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={assignPickerCooldown} min={0} onChange={e=>setAssignPickerCooldown(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={!assignPickerRecipient} onClick={async()=>{ await api("POST","/notifications/assignments",{MonitorID:monitor?.MonitorID,RecipientID:parseInt(assignPickerRecipient),MinSeverity:assignPickerSeverity,CooldownMinutes:assignPickerCooldown}); setShowAssignPicker(false); setAssignPickerRecipient(''); loadMonitorAssignments(); }}
                      style={{ fontSize:11, padding:"3px 10px", border:"none", borderRadius:4, background:assignPickerRecipient?c.blue:"#ccc", color:"#fff", cursor:assignPickerRecipient?"pointer":"not-allowed" }}>Add</button>
                    <button onClick={()=>setShowAssignPicker(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>
      </div>
      {/* ── Runbook panel ── */}
      <div>
        <div style={{ width:"100%", display:"flex", alignItems:"center", padding:"10px 20px" }}>
          <div style={{ display:"flex", alignItems:"center", gap:8 }}>
            <span style={{ fontSize:11, fontWeight:700, color:c.blue, letterSpacing:"0.08em", textTransform:"uppercase" }}>Runbook</span>
            {(form.RunbookUrl||form.RunbookNotes)
              ? <span style={{ fontSize:10, borderRadius:10, padding:"1px 8px", background:c.greenLight, color:c.green, fontWeight:600 }}>Configured{runbookDirty?" · unsaved":""}</span>
              : <span style={{ fontSize:10, color:c.textMuted }}>Optional</span>
            }
          </div>
        </div>
        <div style={{ padding:"0 20px 14px", display:"flex", flexDirection:"column", gap:10 }}>
            <div>
              <label style={{ display:"block", fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:3 }}>Support document URL</label>
              <input type="text" value={form.RunbookUrl||''} placeholder="https://wiki.internal/runbooks/monitor-name"
                onChange={e=>{ f("RunbookUrl", e.target.value); setRunbookDirty(true); }}
                style={{ width:"100%", padding:"5px 8px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, boxSizing:"border-box" }} />
              <div style={{ fontSize:10, color:c.textMuted, marginTop:3 }}>Link to internal wiki, Confluence, SharePoint, or any URL. Included in alert emails.</div>
            </div>
            <div>
              <label style={{ display:"block", fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:3 }}>Known instructions</label>
              <textarea rows={4} value={form.RunbookNotes||''} placeholder={"1. Check X first\n2. If Y then Z\n3. Escalate to on-call if unresolved after 10 min"}
                onChange={e=>{ f("RunbookNotes", e.target.value); setRunbookDirty(true); }}
                style={{ width:"100%", padding:"5px 8px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, resize:"vertical", boxSizing:"border-box", fontFamily:"inherit" }} />
              <div style={{ fontSize:10, color:c.textMuted, marginTop:3 }}>Supports basic markdown (bold, bullets, numbered lists). Shown in the 'What to do' section of alert emails.</div>
            </div>
          </div>
      </div>
      {/* ── Investigation History panel ── */}
      {isEdit && (
        <div>
          <div style={{ width:"100%", display:"flex", alignItems:"center", padding:"10px 20px" }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <span style={{ fontSize:11, fontWeight:700, color:c.blue, letterSpacing:"0.08em", textTransform:"uppercase" }}>Investigation History</span>
              {investigationHistory.length > 0 && (
                <span style={{ fontSize:10, borderRadius:10, padding:"1px 8px", background:"#E8F4F8", color:c.blue, fontWeight:600 }}>{investigationHistory.length}</span>
              )}
            </div>
          </div>
            <div style={{ padding:"0 20px 14px" }}>
              {historyLoading ? (
                <div style={{ fontSize:12, color:c.textMuted, padding:"8px 0" }}>Loading…</div>
              ) : investigationHistory.length === 0 ? (
                <div style={{ fontSize:12, color:c.textMuted, lineHeight:1.6 }}>No investigation sessions yet. Sessions are saved automatically when you use AI Diagnose and ask follow-up questions.</div>
              ) : (
                <div style={{ display:"flex", flexDirection:"column", gap:6 }}>
                  {(historyShowAll ? investigationHistory : investigationHistory.slice(0,5)).map(s => (
                    <div key={s.SessionId} style={{ border:`1px solid ${c.border}`, borderRadius:8, padding:"8px 12px", background:"#fff", display:"flex", alignItems:"center", gap:10 }}>
                      <span style={{ width:8, height:8, borderRadius:"50%", flexShrink:0, background: s.IsResolved ? "#008C6F" : "#E89A2E", display:"inline-block" }} />
                      <div style={{ flex:1, minWidth:0 }}>
                        <div style={{ fontSize:12, fontWeight:700, color:"#1A1A1A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" }}>{s.SessionTitle}</div>
                        <div style={{ fontSize:10, color:c.textMuted, marginTop:1 }}>
                          {new Date(s.CreatedAt).toLocaleString('en-US', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit', hour12:false })}
                          {" · "}
                          <span style={{ color: s.IsResolved ? "#008C6F" : "#E89A2E", fontWeight:600 }}>{s.IsResolved ? "Resolved" : "Open"}</span>
                        </div>
                      </div>
                      <button onClick={() => {
                        api("GET", `/investigations/${s.SessionId}`)
                          .then(full => {
                            if (!full || !full.Messages) return;
                            const thread = full.Messages.map((m, i) => ({ role: m.Role, content: m.Content, timestamp: m.SentAt, id: i }));
                            setInvestigationThread(thread);
                            setCurrentSessionId(s.SessionId);
                            setSessionTitle(s.SessionTitle);
                            setLastDiagnosisContext(full.InitialContext || '');
                            setDiagOpen(true);
                            setDiagResult(thread.length > 0 ? thread[0].content : null);
                            setDiagName(s.SessionTitle.split(' \u00b7')[0]);
                          })
                          .catch(() => {});
                      }} style={{ fontSize:11, padding:"3px 9px", borderRadius:5, border:`1px solid ${c.border}`, background:"#f8fafc", color:"#374151", cursor:"pointer", flexShrink:0, fontWeight:500 }}>
                        Resume
                      </button>
                    </div>
                  ))}
                  {investigationHistory.length > 5 && (
                    <button onClick={() => setHistoryShowAll(o => !o)}
                      style={{ fontSize:11, color:c.blue, background:"none", border:"none", cursor:"pointer", textAlign:"left", padding:"2px 0" }}>
                      {historyShowAll ? "Show less" : `View all (${investigationHistory.length})`}
                    </button>
                  )}
                </div>
              )}
            </div>
        </div>
      )}
    </div>
  );

  // ── Recent Monitor Results Panel ──────────────────────────────────────────
  const recentLogsPanel = isEdit ? (() => {
    // Summary metrics computed from up to 100 results
    const subtype   = selectedLeaf || monitor?.MonitorSubType || "";
    const pm        = primaryMetric(subtype);
    const isAgent   = !!agentHostId;
    const withRt    = recentLogs.filter(l => l.ResponseTimeMs != null);
    // DB query/counter monitors: use StatusCode as the numeric query value
    const isDbQuery = monitor.MonitorType === "Database" &&
      (monitor.MonitorSubType === "db-query" || monitor.MonitorSubType === "db-sqlctr");
    // WMI resource monitors: StatusCode stores value×10; sparkline uses StatusCode/10
    // web-ssl also stores daysRemaining×10 in StatusCode — same display path
    const isWmiResource = ["infra-cpu","infra-mem","infra-disk","infra-diskio","infra-net"].includes(subtype);
    const isSSLMonitor  = subtype === "web-ssl";

    const avgRt        = withRt.length ? Math.round(withRt.reduce((s,l)=>s+l.ResponseTimeMs,0)/withRt.length) : null;
    const withVal      = isDbQuery ? recentLogs.filter(l=>l.StatusCode!=null) : [];
    const avgVal       = withVal.length ? Math.round(withVal.reduce((s,l)=>s+l.StatusCode,0)/withVal.length) : null;
    // Agent and WMI monitors: StatusCode stores value×10; compute avg for KPI card
    const withScVal   = !isDbQuery ? recentLogs.filter(l=>l.StatusCode!=null) : [];
    const avgAgentVal = withScVal.length ? Math.round(withScVal.reduce((s,l)=>s+l.StatusCode,0)/withScVal.length) : null;
    const alertCnt  = recentLogs.filter(l=>{ const s=l.Severity||(l.IsSuccess?"healthy":"failed"); return s==="warning"||s==="critical"; }).length;
    const avail     = recentLogs.length ? Math.round(recentLogs.filter(l=>l.IsSuccess).length/recentLogs.length*100) : null;

    // Trend: compare avg RT of most recent 10 vs previous 10
    let trend = "Stable";
    if (withRt.length >= 20) {
      const recent10 = withRt.slice(0,10).reduce((s,l)=>s+l.ResponseTimeMs,0)/10;
      const prev10   = withRt.slice(10,20).reduce((s,l)=>s+l.ResponseTimeMs,0)/10;
      const delta    = recent10 - prev10;
      if (delta > prev10 * 0.1) trend = "Degrading";
      else if (delta < -prev10 * 0.1) trend = "Improving";
    }
    const trendColor = trend==="Degrading" ? "#D95C5C" : trend==="Improving" ? "#008C6F" : "#7A9AB8";

    // Sparkline — SVG, 300×80 viewBox, oldest left
    // DB query + WMI resource monitors + web-ssl use StatusCode; others use ResponseTimeMs
    const sparkPts = (isDbQuery || isWmiResource || isSSLMonitor)
      ? [...recentLogs].reverse().filter(l=>l.StatusCode!=null)
      : [...recentLogs].reverse().filter(l=>l.ResponseTimeMs!=null);
    const sparkInfo = { warnThresh: null, critThresh: null, unit: pm.unit };
    const sparkline = sparkPts.length >= 2 ? (() => {
      const VW=300, VH=80, pad=4, lx_label=4;
      if (isDbQuery) {
        // Line 1: query value (StatusCode) — primary, #006D8C bold
        const vals1 = sparkPts.map(l=>l.StatusCode);
        const max1  = Math.max(...vals1), min1 = Math.min(...vals1), rng1 = max1 - min1 || 1;
        const toX   = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
        const toY1  = v => pad + (1-(v-min1)/rng1)*(VH-pad*2);
        const pts1  = sparkPts.map((l,i)=>`${toX(i).toFixed(1)},${toY1(l.StatusCode).toFixed(1)}`).join(" ");
        const lx1 = toX(sparkPts.length-1), ly1 = toY1(sparkPts[sparkPts.length-1].StatusCode);
        // Threshold lines against Line 1 y-scale
        let warnThresh=null, critThresh=null;
        try { const cfg=JSON.parse(monitor.TypeConfigJson||"{}"); warnThresh=parseFloat(cfg.ValueWarn)||null; critThresh=parseFloat(cfg.ValueCrit)||null; } catch {}
        sparkInfo.warnThresh=warnThresh; sparkInfo.critThresh=critThresh;
        const warnY = warnThresh!=null ? toY1(Math.min(warnThresh,max1)) : null;
        const critY = critThresh!=null ? toY1(Math.min(critThresh,max1)) : null;
        // Line 2: response time — secondary, #9CA3AF thin
        const rtPts = sparkPts.filter(l=>l.ResponseTimeMs!=null);
        let pts2 = null;
        if (rtPts.length >= 2) {
          const vals2 = rtPts.map(l=>l.ResponseTimeMs);
          const max2  = Math.max(...vals2), min2 = Math.min(...vals2), rng2 = max2 - min2 || 1;
          // Map rt points to their original indices in sparkPts for x alignment
          const idxMap = rtPts.map(l => sparkPts.indexOf(l));
          const toY2  = v => pad + (1-(v-min2)/rng2)*(VH-pad*2);
          pts2 = idxMap.map((si,j)=>`${toX(si).toFixed(1)},${toY2(rtPts[j].ResponseTimeMs).toFixed(1)}`).join(" ");
        }
        return (
          <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#F8FBFD", overflow:"visible" }}>
            {warnY!=null && <line x1={pad} y1={warnY} x2={VW-pad} y2={warnY} stroke="#E89A2E" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
            {critY!=null && <line x1={pad} y1={critY} x2={VW-pad} y2={critY} stroke="#D95C5C" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
            {pts2 && <polyline points={pts2} fill="none" stroke="#9CA3AF" strokeWidth="1" strokeLinejoin="round" opacity="0.6" />}
            <polyline points={pts1} fill="none" stroke="#006D8C" strokeWidth="2" strokeLinejoin="round" />
            <circle cx={lx1} cy={ly1} r="3" fill="#006D8C" />
          </svg>
        );
      }
      if (isWmiResource || isSSLMonitor) {
        // WMI resource monitors + web-ssl: plot StatusCode/10 (actual metric value), thresholds from TypeConfigJson
        const vals = sparkPts.map(l=>l.StatusCode/10);
        const maxV = Math.max(...vals)||1, rng = maxV || 1;
        const toX  = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
        const toY  = v => pad + (1-Math.min(v,maxV)/maxV)*(VH-pad*2);
        const polyPts = sparkPts.map((l,i)=>`${toX(i).toFixed(1)},${toY(l.StatusCode/10).toFixed(1)}`).join(" ");
        const lx = toX(sparkPts.length-1), ly = toY(sparkPts[sparkPts.length-1].StatusCode/10);
        let warnThresh=null, critThresh=null;
        try {
          const cfg = JSON.parse(monitor.TypeConfigJson||"{}");
          if (subtype==="infra-net") {
            warnThresh = parseFloat(cfg.ThroughputInWarn)||null;
          } else if (subtype==="infra-cpu") {
            warnThresh = parseFloat(cfg.CpuWarn)||null; critThresh = parseFloat(cfg.CpuCrit)||null;
          } else if (subtype==="infra-mem") {
            warnThresh = parseFloat(cfg.MemWarn)||null; critThresh = parseFloat(cfg.MemCrit)||null;
          } else if (subtype==="infra-disk") {
            const fw = parseFloat(cfg.FreeWarnPct); if (!isNaN(fw) && fw>0) warnThresh = 100-fw;
            const fc = parseFloat(cfg.FreeCritPct); if (!isNaN(fc) && fc>0) critThresh = 100-fc;
          } else if (subtype==="web-ssl") {
            // For SSL, warn/crit are days-remaining thresholds — normalize so crit < warn
            const rw = parseFloat(cfg.ExpiryWarnDays)||30;
            const rc = parseFloat(cfg.ExpiryCritDays)||7;
            warnThresh = Math.max(rw, rc);
            critThresh = Math.min(rw, rc);
          }
        } catch {}
        const unit = pm.unit;
        sparkInfo.warnThresh=warnThresh; sparkInfo.critThresh=critThresh; sparkInfo.unit=unit;
        const warnY = warnThresh!=null ? toY(Math.min(warnThresh,maxV)) : null;
        const critY = critThresh!=null ? toY(Math.min(critThresh,maxV)) : null;
        return (
          <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#F8FBFD", overflow:"visible" }}>
            {warnY!=null && <line x1={pad} y1={warnY} x2={VW-pad} y2={warnY} stroke="#E89A2E" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
            {critY!=null && <line x1={pad} y1={critY} x2={VW-pad} y2={critY} stroke="#D95C5C" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
            <polyline points={polyPts} fill="none" stroke="#006D8C" strokeWidth="1.5" strokeLinejoin="round" />
            <circle cx={lx} cy={ly} r="3" fill="#006D8C" />
          </svg>
        );
      }
      // Standard (non-DB, non-WMI) sparkline — single line, response time
      const vals = sparkPts.map(l=>l.ResponseTimeMs);
      const maxV = Math.max(...vals), rng = maxV || 1;
      const toX  = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
      const toY  = v => pad + (1-v/rng)*(VH-pad*2);
      const polyPts = sparkPts.map((l,i)=>`${toX(i).toFixed(1)},${toY(l.ResponseTimeMs).toFixed(1)}`).join(" ");
      const lx = toX(sparkPts.length-1), ly = toY(sparkPts[sparkPts.length-1].ResponseTimeMs);
      let warnThresh=null, critThresh=null;
      try { const cfg=JSON.parse(monitor.TypeConfigJson||"{}"); warnThresh=parseInt(cfg.RtWarnMs)||null; critThresh=parseInt(cfg.RtCritMs)||null; } catch {}
      if (!warnThresh && monitor.ResponseTimeThresholdMs) warnThresh=monitor.ResponseTimeThresholdMs;
      if (!critThresh && warnThresh) critThresh = warnThresh * 2;
      const unit = pm.unit;
      sparkInfo.warnThresh=warnThresh; sparkInfo.critThresh=critThresh; sparkInfo.unit=unit;
      const warnY = warnThresh!=null ? toY(Math.min(warnThresh,maxV)) : null;
      const critY = critThresh!=null ? toY(Math.min(critThresh,maxV)) : null;
      return (
        <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#F8FBFD", overflow:"visible" }}>
          {warnY!=null && <line x1={pad} y1={warnY} x2={VW-pad} y2={warnY} stroke="#E89A2E" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
          {critY!=null && <line x1={pad} y1={critY} x2={VW-pad} y2={critY} stroke="#D95C5C" strokeWidth="1" strokeDasharray="4,2" opacity="0.8" />}
          <polyline points={polyPts} fill="none" stroke="#006D8C" strokeWidth="1.5" strokeLinejoin="round" />
          <circle cx={lx} cy={ly} r="3" fill="#006D8C" />
        </svg>
      );
    })() : null;

    return (
      <div className="dp" style={{ padding:"14px 20px 0", background:"#FFFFFF", borderTop:"1px solid #C0D8E8", flex:1, minHeight:0, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:8, flexShrink:0 }}>
          <span style={{ fontSize:11, fontWeight:700, color:"#4A4A4A", letterSpacing:"0.08em", textTransform:"uppercase" }}>Recent Monitor Results</span>
          <button onClick={loadRecentLogs} style={{ background:"none", border:"none", color:"#006D8C", cursor:"pointer", fontSize:11, padding:0 }}>⟳ Refresh</button>
        </div>
        {logsLoading ? <div style={{ color:"#7A9AB8", fontSize:12 }}>Loading…</div> : recentLogs.length === 0 ? (
          <div style={{ color:"#7A9AB8", fontSize:12, textAlign:"center", padding:"16px 0" }}>No results yet — logs will appear after the first scheduled check.</div>
        ) : (
          <>
            {/* Summary metrics */}
            <div style={{ display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:6, marginBottom:8, flexShrink:0 }}>
              {[
                isDbQuery
                  ? { label:"Avg Value", value: avgVal!=null ? `${avgVal}` : "—", color:"#1A1A1A" }
                  : avgAgentVal!=null
                    // Agent and WMI: StatusCode/10 is the metric value
                    ? { label:pm.label, value: `${(avgAgentVal/10).toFixed(1)}${pm.unit}`, color:"#1A1A1A" }
                    // Ping/TCP/HTTP etc: ResponseTimeMs is the metric value
                    : { label:pm.label, value: avgRt!=null ? `${avgRt}${pm.unit}` : "—", color:"#1A1A1A" },
                { label:"Alerts", value: alertCnt, color: alertCnt>0?"#D95C5C":"#1A1A1A" },
                { label:"Availability", value: avail!=null ? `${avail}%` : "—", color: avail!=null&&avail<95?"#D95C5C":avail!=null&&avail<99?"#E89A2E":"#008C6F" },
                { label:"Trend", value: trend, color: trendColor },
              ].map(m=>(
                <div key={m.label} style={{ background:"#F8FBFD", borderRadius:6, padding:"6px 8px", border:"1px solid #E8F0F5" }}>
                  <div style={{ fontSize:8, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:2 }}>{m.label}</div>
                  <div style={{ fontSize:12, fontWeight:700, color:m.color }}>{m.value}</div>
                </div>
              ))}
            </div>
            {/* Sparkline */}
            {sparkline && <div style={{ marginBottom:2, flexShrink:0, borderRadius:4, overflow:"hidden", border:"1px solid #E8F0F5" }}>{sparkline}</div>}
            {/* Threshold legend — only when at least one threshold line is drawn */}
            {sparkline && (sparkInfo.warnThresh!=null || sparkInfo.critThresh!=null) && (
              <div style={{ fontSize:10, color:"#4A4A4A", marginBottom:6, flexShrink:0, display:"flex", gap:12 }}>
                {sparkInfo.warnThresh!=null && <span><span style={{ color:"#E89A2E", letterSpacing:1 }}>— —</span> <span>Warn threshold</span></span>}
                {sparkInfo.critThresh!=null && <span><span style={{ color:"#D95C5C", letterSpacing:1 }}>· · ·</span> <span>Crit threshold</span></span>}
              </div>
            )}
            {/* Table — scrollable, ~10 rows visible */}
            <div style={{ flex:1, minHeight:0, overflowY:"auto", overflowX:"auto" }}>
              <table style={{ width:"100%", borderCollapse:"collapse", fontSize:11 }}>
                <thead><tr>
                  {(isDbQuery
                    ? ["Timestamp","Status","Response","Value","Severity","Error"]
                    : ["Timestamp","Status","Value","Severity","Message"]
                  ).map(h=>(
                    <th key={h} style={{ padding:"5px 8px", textAlign:"left", fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", borderBottom:"1px solid #C0D8E8", position:"sticky", top:0, background:"#FFFFFF" }}>{h}</th>
                  ))}
                </tr></thead>
                <tbody>
                  {recentLogs.map((l,i)=>{
                    const sev = l.Severity || (l.IsSuccess ? "healthy" : "failed");
                    const sc  = STATUS_COLOR[sev] || "#9CA3AF";
                    if (isDbQuery) {
                      return (
                        <tr key={i} style={{ borderBottom:"1px solid #F0F7FB" }}>
                          <td style={{ padding:"5px 8px", fontFamily:"mono", color:"#7A9AB8", whiteSpace:"nowrap" }}>{l.CheckTimestamp ? parseUTC(l.CheckTimestamp)?.toLocaleString() : "—"}</td>
                          <td style={{ padding:"5px 8px" }}><span style={{ width:8, height:8, borderRadius:"50%", background:sc, display:"inline-block" }} /></td>
                          <td style={{ padding:"5px 8px", fontFamily:"mono", color:"#1A1A1A" }}>{l.ResponseTimeMs!=null ? `${l.ResponseTimeMs}ms` : "—"}</td>
                          <td style={{ padding:"5px 8px", fontFamily:"mono", color:"#1A1A1A" }}>{l.StatusCode!=null ? `${l.StatusCode}` : "—"}</td>
                          <td style={{ padding:"5px 8px", color:sc, fontWeight:600 }}>{sev}</td>
                          <td style={{ padding:"5px 8px", color:"#7A9AB8", fontSize:10, maxWidth:160, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{l.ErrorMessage||""}</td>
                        </tr>
                      );
                    }
                    // Unified row for agent and non-agent monitors
                    let valDisplay = "—";
                    if (l.StatusCode != null) {
                      // Agent monitors AND WMI monitors both encode Value×10 in StatusCode
                      valDisplay = `${(l.StatusCode/10).toFixed(1)}${pm.unit}`;
                    } else if (l.ResponseTimeMs != null && pm.unit === 'ms') {
                      valDisplay = `${l.ResponseTimeMs}ms`;
                    }
                    const msg = l.ErrorMessage || "—";
                    return (
                      <tr key={i} style={{ borderBottom:"1px solid #F0F7FB" }}>
                        <td style={{ padding:"5px 8px", fontFamily:"mono", color:"#7A9AB8", whiteSpace:"nowrap" }}>{l.CheckTimestamp ? parseUTC(l.CheckTimestamp)?.toLocaleString() : "—"}</td>
                        <td style={{ padding:"5px 8px" }}><span style={{ width:8, height:8, borderRadius:"50%", background:sc, display:"inline-block" }} /></td>
                        <td style={{ padding:"5px 8px", fontFamily:"monospace", color:"#1A1A1A" }}>{valDisplay}</td>
                        <td style={{ padding:"5px 8px", color:sc, fontWeight:600 }}>{sev}</td>
                        <td style={{ padding:"5px 8px", color:"#4A4A4A", fontSize:10, maxWidth:200, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{msg.length > 60 ? msg.slice(0,60)+"…" : msg}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </>
        )}
      </div>
    );
  })() : null;

  const parseScriptBlocks = (text) => {
    const segments = [];
    const re = /```powershell\n([\s\S]*?)```/g;
    let last = 0, m;
    while ((m = re.exec(text)) !== null) {
      if (m.index > last) segments.push({ type: 'text', content: text.slice(last, m.index) });
      segments.push({ type: 'script', content: m[1].trim(), id: `ps-${m.index}` });
      last = m.index + m[0].length;
    }
    if (last < text.length) segments.push({ type: 'text', content: text.slice(last) });
    return segments;
  };

  // Paste modal
  const pasteModal = showPasteModal ? (
    <div style={{ position:"fixed", inset:0, background:"rgba(0,0,0,0.75)", zIndex:9999, display:"flex", alignItems:"center", justifyContent:"center" }}
      onClick={e => { if (e.target === e.currentTarget) setShowPasteModal(false); }}>
      <div style={{ background:"#fff", borderRadius:12, padding:24, width:480, maxWidth:"90vw", boxShadow:"0 20px 60px rgba(0,0,0,0.4)" }}>
        <div style={{ fontSize:14, fontWeight:700, color:"#1A1A1A", marginBottom:4 }}>Paste context or data</div>
        <div style={{ fontSize:12, color:"#6B7280", marginBottom:12 }}>Paste event logs, error output, config snippets, or any text for AI analysis.</div>
        <textarea value={pasteText} onChange={e => setPasteText(e.target.value)}
          placeholder="Paste text here..."
          rows={10} style={{ width:"100%", fontSize:12, padding:"8px 10px", border:"1px solid #C0D8E8", borderRadius:6, resize:"vertical", fontFamily:"'IBM Plex Mono',monospace", boxSizing:"border-box", color:"#1A1A1A" }} autoFocus />
        <div style={{ fontSize:10, color:"#9CA3AF", marginTop:4, marginBottom:12 }}>{(pasteText.length).toLocaleString()} characters</div>
        <div style={{ display:"flex", gap:8, justifyContent:"flex-end" }}>
          <button onClick={() => { setShowPasteModal(false); setPasteText(''); }}
            style={{ padding:"6px 14px", borderRadius:6, border:"1px solid #D1D5DB", background:"#fff", fontSize:12, cursor:"pointer", color:"#374151" }}>Cancel</button>
          <button onClick={handlePasteConfirm} disabled={!pasteText.trim()}
            style={{ padding:"6px 14px", borderRadius:6, border:"none", background:"#006D8C", fontSize:12, fontWeight:600, cursor: pasteText.trim() ? "pointer" : "not-allowed", color:"#fff", opacity: pasteText.trim() ? 1 : 0.5 }}>Add to message</button>
        </div>
      </div>
    </div>
  ) : null;

  // Attachment toolbar + chips (shared by both thread panel states)
  const attachmentUI = (
    <div>
      {pendingAttachments.length > 0 && (
        <div style={{ display:"flex", flexWrap:"wrap", gap:6, marginBottom:6 }}>
          {pendingAttachments.map(att => (
            <div key={att.id} style={{ display:"flex", alignItems:"center", gap:5, background:"#E0F2F7", borderRadius:6, padding:"3px 8px", border:"1px solid #B0D8E8" }}>
              {att.type === 'image' && att.thumbnailUrl
                ? <img src={att.thumbnailUrl} alt="" style={{ width:28, height:28, objectFit:"cover", borderRadius:3, flexShrink:0 }} />
                : <span style={{ fontSize:13 }}>{att.type === 'paste' ? '\uD83D\uDCCB' : '\uD83D\uDCC4'}</span>
              }
              <span style={{ fontSize:11, color:"#006D8C", maxWidth:120, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{att.name}</span>
              {att.fileSizeBytes != null && <span style={{ fontSize:10, color:"#5B9DB8" }}>{att.fileSizeBytes > 1048576 ? `${(att.fileSizeBytes/1048576).toFixed(1)}MB` : `${Math.round(att.fileSizeBytes/1024)}KB`}</span>}
              <button onClick={() => removeAttachment(att.id)} style={{ background:"none", border:"none", color:"#5B9DB8", cursor:"pointer", padding:"0 2px", fontSize:12, lineHeight:1 }}>&#215;</button>
            </div>
          ))}
        </div>
      )}
      {pendingAttachments.length > 0 && (
        <div style={{ fontSize:10, color:"#4A4A4A", marginBottom:6 }}>~{pendingAttachments.reduce((s,a) => s+(a.tokenEstimate||0),0).toLocaleString()} tokens in attachments</div>
      )}
      <input type="file" id="inv-any-input" accept=".txt,.csv,.log,.json,.xml,.md,.png,.jpg,.jpeg,.gif,.webp" style={{ display:"none" }} onChange={handleFileAttach} />
      <input type="file" id="inv-img-input" accept="image/*" style={{ display:"none" }} onChange={handleFileAttach} />
      <div style={{ display:"flex", gap:5, marginBottom:6 }}>
        <button title="Attach file or image" onClick={() => document.getElementById('inv-any-input').click()}
          style={{ background:"#F5F9FC", border:"1px solid #C0D8E8", borderRadius:5, padding:"3px 8px", fontSize:13, cursor:"pointer" }}>&#128206;</button>
        <button title="Attach image" onClick={() => document.getElementById('inv-img-input').click()}
          style={{ background:"#F5F9FC", border:"1px solid #C0D8E8", borderRadius:5, padding:"3px 8px", fontSize:13, cursor:"pointer" }}>&#128444;</button>
        <button title="Paste text / logs" onClick={() => setShowPasteModal(true)}
          style={{ background:"#F5F9FC", border:"1px solid #C0D8E8", borderRadius:5, padding:"3px 8px", fontSize:13, cursor:"pointer" }}>&#128203;</button>
      </div>
      {attachmentError && <div style={{ fontSize:11, color:"#D95C5C", marginBottom:4 }}>{attachmentError}</div>}
    </div>
  );

  const investigationThreadPanel = investigationThread.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>
      {investigationThread.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={{ paddingLeft:8 }}>
              <div style={{ fontSize:12, color:"#1A1A1A", lineHeight:1.5 }}>{msg.content}</div>
              {(msg.attachments||[]).length > 0 && (
                <div style={{ display:"flex", flexWrap:"wrap", gap:5, marginTop:5 }}>
                  {(msg.attachments||[]).map((att, ai) => (
                    <div key={ai} style={{ display:"flex", alignItems:"center", gap:4, background:"#E0F2F7", borderRadius:5, padding:"2px 7px", border:"1px solid #B0D8E8" }}>
                      {att.type === 'image' && att.thumbnailUrl
                        ? <img src={att.thumbnailUrl} alt={att.name} style={{ width:40, height:40, objectFit:"cover", borderRadius:4, border:"1px solid #D8CCBA" }} />
                        : <span style={{ fontSize:12 }}>{att.type === 'paste' ? '\uD83D\uDCCB' : '\uD83D\uDCC4'}</span>
                      }
                      {att.type !== 'image' && <span style={{ fontSize:11, color:"#006D8C" }}>{att.name}</span>}
                      {att.fileSizeBytes != null && <span style={{ fontSize:10, color:"#5B9DB8" }}>{att.fileSizeBytes > 1048576 ? `${(att.fileSizeBytes/1048576).toFixed(1)}MB` : `${Math.round(att.fileSizeBytes/1024)}KB`}</span>}
                    </div>
                  ))}
                </div>
              )}
            </div>
          ) : (
            <div style={{ fontSize:12, paddingLeft:8 }}>
              {parseScriptBlocks(msg.content).map((seg, si) => seg.type === 'text' ? (
                <div key={si}>{renderDiagMd(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); setCopiedScriptId(seg.id); setTimeout(() => setCopiedScriptId(null), 2000); }}
                    style={{ position:"absolute", top:6, right:6, background: copiedScriptId === seg.id ? "#008C6F" : "#2D3F52", color:"#fff", border:"none", borderRadius:4, fontSize:10, padding:"2px 7px", cursor:"pointer" }}>
                    {copiedScriptId === seg.id ? "Copied!" : "Copy"}
                  </button>
                </div>
              ))}
            </div>
          )}
        </div>
      ))}
      {currentSessionId && (
        <div style={{ display:"flex", alignItems:"center", gap:6, marginTop:10, marginBottom:4, padding:"5px 8px", background:"#F0FAF7", borderRadius:6, border:"1px solid #C0E8D8" }}>
          <span style={{ fontSize:11, color:"#008C6F", flexShrink:0 }}>&#128190; Saving to:</span>
          <input value={sessionTitle} onChange={e => setSessionTitle(e.target.value)}
            onBlur={e => { if (e.target.value.trim()) api("PATCH", `/investigations/${currentSessionId}/title`, { sessionTitle: e.target.value.trim() }).catch(() => {}); }}
            style={{ flex:1, border:"none", background:"transparent", fontSize:12, color:"#006D8C", fontWeight:600, outline:"none", cursor:"text", fontFamily:"inherit" }} />
        </div>
      )}
      {attachmentUI}
      <div style={{ display:"flex", gap:6 }}>
        <textarea value={followUpText} onChange={e => setFollowUpText(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleDiagFollowUp(); } }}
          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={handleDiagFollowUp} disabled={(!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading}
          style={{ alignSelf:"flex-end", background:"#006D8C", color:"#fff", border:"none", borderRadius:6, padding:"6px 12px", fontSize:12, fontWeight:600, cursor: ((!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading) ? "not-allowed" : "pointer", opacity: ((!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading) ? 0.5 : 1 }}>
          {followUpLoading ? "…" : "Send"}
        </button>
      </div>
      {pasteModal}
    </div>
  ) : diagResult ? (
    <div style={{ marginTop:12, borderTop:"1px solid #C0D8E8", paddingTop:12 }}>
      {attachmentUI}
      <div style={{ display:"flex", gap:6 }}>
        <textarea value={followUpText} onChange={e => setFollowUpText(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleDiagFollowUp(); } }}
          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={handleDiagFollowUp} disabled={(!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading}
          style={{ alignSelf:"flex-end", background:"#006D8C", color:"#fff", border:"none", borderRadius:6, padding:"6px 12px", fontSize:12, fontWeight:600, cursor: ((!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading) ? "not-allowed" : "pointer", opacity: ((!followUpText.trim() && pendingAttachments.length === 0) || followUpLoading) ? 0.5 : 1 }}>
          {followUpLoading ? "…" : "Send"}
        </button>
      </div>
      {pasteModal}
    </div>
  ) : null;

  return (
    <div style={{ height:"calc(100vh - 20px)", background:"#ffffff", color:"#1A1A1A", fontFamily:"'IBM Plex Sans',system-ui,sans-serif", display:"flex", flexDirection:"column", overflow:"hidden" }}>

      {/* ── Branding header ── */}
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", background:"#ffffff", borderBottom:"0.5px solid #f1f5f9", padding:"10px 16px", flexShrink:0, height:58 }}>
        <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={{ display:"flex", alignItems:"center", gap:6 }}>
              <span style={{ fontSize:10, color:"#94a3b8" }}>Enterprise Monitoring · v1.0.236</span>
              {isDirty && <span style={{ fontSize:9, fontWeight:600, borderRadius:4, padding:"1px 5px", background:"#fef9c3", color:"#854d0e" }}>unsaved</span>}
            </div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:8 }}>
          {origin && (
            <button onClick={() => { const key = origin.toLowerCase(); if (navInterceptRef?.current) navInterceptRef.current(key); else { onNavigate(key); onClose(); } }} style={{ background:"none", border:"none", cursor:"pointer", padding:0, color:"#006D8C", fontSize:12, whiteSpace:"nowrap" }}>
              ← Back to {origin}
            </button>
          )}
          {origin && <div style={{ width:"0.5px", height:18, background:"#e2e5ea", flexShrink:0 }} />}
          <button onClick={tryClose} style={{ background:"none", border:"none", cursor:"pointer", padding:0, color:"#94a3b8", fontSize:11, whiteSpace:"nowrap" }}>
            ← Monitors / {isEdit ? `Edit — ${monitor.MonitorName}` : "Add Monitor"}
          </button>
          <div style={{ width:"0.5px", height:18, background:"#e2e5ea", flexShrink:0 }} />
          {isEdit && (
            <div style={{ display:"flex", alignItems:"center", gap:6 }}>
              <div onClick={handleToggleEnabled} style={{ width:28, height:15, borderRadius:8, background:isEnabled?"#008C6F":"#D95C5C", position:"relative", transition:"background 0.2s", cursor:"pointer", flexShrink:0 }}>
                <div style={{ position:"absolute", top:2, left:isEnabled?13:2, width:11, height:11, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
              </div>
              <span style={{ fontSize:11, fontWeight:600, color:isEnabled?"#008C6F":"#D95C5C" }}>{isEnabled ? "Enabled" : "Disabled"}</span>
              {enabledConfirm && <span style={{ fontSize:10, color:"#008C6F" }}>✓</span>}
            </div>
          )}
          {isEdit && <button onClick={()=>setDeleteConfirm(true)} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #fca5a5", borderRadius:5, background:"#fff5f5", color:"#991b1b", cursor:"pointer", whiteSpace:"nowrap" }}>Delete</button>}
          {isEdit && <button disabled={copying} onClick={copyMonitor} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#ffffff", color:"#006D8C", cursor:"pointer", whiteSpace:"nowrap" }}>{copying?"Copying…":"Copy"}</button>}
          <button onClick={tryClose} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#ffffff", color:"#374151", cursor:"pointer" }}>Cancel</button>
          <button disabled={saving} onClick={submit} style={{ fontSize:11, padding:"3px 12px", border:"none", borderRadius:5, background:"#006D8C", color:"#ffffff", cursor:"pointer", fontWeight:600, whiteSpace:"nowrap" }}>
            {saving ? "Saving…" : (isEdit ? "Save Changes" : "Create Monitor")}
          </button>
        </div>
      </div>

      {err && <div style={{ background:`${c.red}22`,border:`1px solid ${c.red}40`,borderRadius:0,padding:"8px 24px",color:c.red,fontSize:12 }}>{err}</div>}

      {/* 3-column body (diagnosis pane animates in as third column) */}
      <div style={{ display:"flex", flex:1, minHeight:0 }}>

        {/* LEFT column — config + notifications */}
        <div style={{ flex: diagOpen ? 3 : 4, transition:"flex 250ms ease", minWidth:0, display:"flex", flexDirection:"column", borderRight:"1px solid #C0D8E8", overflow:"hidden", background:"#FFFFFF" }}>
          {/* Tree + fields */}
          <div style={{ display:"flex", flex:1, minHeight:0, overflow:"hidden" }}>
            {treePane}
            <div style={{ flex:1, overflowY:"auto", background:"#FFFFFF" }}>{fieldPane}</div>
          </div>
          {/* Notifications */}
          {notifPane}
        </div>

        {/* MIDDLE column — test results + recent logs */}
        <div style={{ flex: diagOpen ? 3 : 5, transition:"flex 250ms ease", minWidth:0, height:"100%", display:"flex", flexDirection:"column", overflow:"hidden", background:"#FFFFFF" }}>
          {/* Test results */}
          <div className="dp" style={{ padding:"16px 20px", background:"#FFFFFF", overflowY:"auto", flex:"0 0 auto", maxHeight:"55%", borderBottom:"1px solid #C0D8E8" }}>
            {(agentHostId && !credentialId) ? (() => {
              // ── Agent-managed monitor panel ──
              const agentHost = agentHosts.find(a => a.AgentHostId === parseInt(agentHostId));
              const latest = recentLogs[0];
              const latestSev = latest ? (latest.Severity || (latest.IsSuccess ? "healthy" : "failed")).toLowerCase() : null;
              const sevColor = latestSev === "healthy" ? "#008C6F" : latestSev === "warning" ? "#E89A2E" : latestSev === "critical" ? "#D95C5C" : "#7A9AB8";
              const canDiagnoseAgent = !!latest && latestSev && latestSev !== "healthy";
              const diagColorAgent = latestSev === "warning" ? "#E89A2E" : "#D95C5C";

              const relTime = (ts) => {
                if (!ts) return "Never";
                const d = parseUTC(ts);
                if (!d) return "Never";
                const secs = Math.floor((Date.now() - d.getTime()) / 1000);
                if (secs < 60) return `${secs}s ago`;
                if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
                return `${Math.floor(secs / 3600)}h ago`;
              };

              const agentPm = primaryMetric(selectedLeaf || form.MonitorSubType || "");
              const fmtValue = (statusCode) => {
                if (statusCode == null) return null;
                return `${(statusCode / 10).toFixed(1)}${agentPm.unit}`;
              };

              return (
                <div>
                  {/* Agent header */}
                  <div style={{ borderLeft:"3px solid #006D8C", paddingLeft:10, marginBottom:12 }}>
                    <div style={{ fontSize:13, fontWeight:700, color:"#006D8C", marginBottom:3 }}>● Monitored by Agent</div>
                    <div style={{ fontSize:11, color:"#4A4A4A" }}>
                      Host: <span style={{ fontWeight:600 }}>{agentHost?.Name || `Agent #${agentHostId}`}</span>
                      {agentHost?.LastSeenAt && <> · Last check-in: <span style={{ color: (Date.now() - parseUTC(agentHost.LastSeenAt)?.getTime()) < 5*60*1000 ? "#008C6F" : "#D95C5C" }}>{relTime(agentHost.LastSeenAt)}</span></>}
                    </div>
                  </div>

                  {/* Last result */}
                  <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:8 }}>
                    <span style={{ fontSize:11, fontWeight:700, color:"#4A4A4A", letterSpacing:"0.08em", textTransform:"uppercase" }}>Last Result</span>
                    <div style={{ display:"flex", alignItems:"center", gap:6 }}>
                      {canDiagnoseAgent && (
                        <button
                          onClick={() => runDiagnosis({
                            monitorName:    form.MonitorName || monitor?.MonitorName || "",
                            monitorType:    form.MonitorType || "",
                            monitorSubType: selectedLeaf || form.MonitorSubType || "",
                            target:         latest.Target || "",
                            protocol:       "Agent",
                            statusCode:     latest.StatusCode != null ? String(latest.StatusCode) : "N/A",
                            responseTime:   latest.ResponseTimeMs != null ? `${latest.ResponseTimeMs}ms` : "N/A",
                            errorMessage:   latest.ErrorMessage || "",
                            severity:       latestSev,
                            timeoutMs:      parseInt(form.TimeoutMs) || 5000,
                          })}
                          style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${diagColorAgent}`, borderRadius:4, background:"#fff", color:diagColorAgent, cursor:"pointer", fontWeight:600 }}
                        >🔍 Diagnose</button>
                      )}
                      <button onClick={loadRecentLogs} style={{ fontSize:11, padding:"4px 10px", border:"1px solid #C0D8E8", borderRadius:4, background:"#fff", color:"#006D8C", cursor:"pointer" }}>⟳ Refresh</button>
                    </div>
                  </div>

                  {logsLoading ? (
                    <div style={{ color:"#7A9AB8", fontSize:12 }}>Loading…</div>
                  ) : !latest ? (
                    <div style={{ color:"#7A9AB8", fontSize:12, textAlign:"center", padding:"12px 0" }}>No results yet — the agent will report within its next check interval.</div>
                  ) : (
                    <div style={{ border:`1px solid ${sevColor}40`, borderLeft:`3px solid ${sevColor}`, borderRadius:"0 6px 6px 0", padding:"10px 12px", background:`${sevColor}08` }}>
                      <div style={{ display:"grid", gridTemplateColumns:"auto 1fr", gap:"4px 12px", fontSize:12 }}>
                        <span style={{ color:"#7A9AB8", fontWeight:600 }}>Status</span>
                        <span style={{ color:sevColor, fontWeight:700, textTransform:"capitalize" }}>{latestSev}</span>
                        {fmtValue(latest.StatusCode) && <>
                          <span style={{ color:"#7A9AB8", fontWeight:600 }}>Value</span>
                          <span style={{ fontFamily:"monospace", color:"#1A1A1A" }}>{fmtValue(latest.StatusCode)}</span>
                        </>}
                        {latest.ErrorMessage && <>
                          <span style={{ color:"#7A9AB8", fontWeight:600 }}>Message</span>
                          <span style={{ color:"#1A1A1A" }}>{latest.ErrorMessage}</span>
                        </>}
                        <span style={{ color:"#7A9AB8", fontWeight:600 }}>Checked</span>
                        <span style={{ color:"#4A4A4A" }}>{latest.CheckTimestamp ? parseUTC(latest.CheckTimestamp)?.toLocaleString() : "—"}</span>
                      </div>
                    </div>
                  )}
                </div>
              );
            })() : (() => {
              const testDisabled = !isEdit && !selectedLeaf;
              const latestRun = testRuns[0];
              const latestSev = latestRun ? (latestRun.Severity || (latestRun.IsSuccess ? "healthy" : "failed")).toLowerCase() : "healthy";
              const latestHasFailedCheck = !!latestRun && (
                !latestRun.IsSuccess ||
                (latestRun.ContentMatchResult && !latestRun.ContentMatchResult.startsWith("Passed") && latestRun.ContentMatchResult !== "Not configured")
              );
              const canDiagnose = !!latestRun && (latestSev !== "healthy" || latestHasFailedCheck);
              const diagColor   = latestSev === "warning" ? "#E89A2E" : "#D95C5C";
              return (
                <div style={{ marginBottom:10 }}>
                  {/* Button row: Test Monitor + Run Now side by side */}
                  <div style={{ display:"flex", gap:8, marginBottom: testDisabled || runNowMsg ? 6 : 8 }}>
                    <button
                      onClick={testDisabled ? undefined : runTest}
                      disabled={testing || testDisabled}
                      style={{
                        flex:2, padding:10, borderRadius:6, fontSize:12, fontWeight:700,
                        border:"none", cursor: testDisabled ? "not-allowed" : "pointer",
                        background: testDisabled ? "#C0D8E8" : "#006D8C",
                        color: testDisabled ? "#7A9AB8" : "#FFFFFF",
                        opacity: testDisabled ? 0.4 : (testing ? 0.85 : 1),
                        pointerEvents: testDisabled ? "none" : undefined,
                        fontFamily:"monospace",
                      }}
                    >{testing ? `${SPINNER_CHARS[spinnerFrame]} Running… ${(elapsedTenths/10).toFixed(1)}s` : "▶ Test Monitor"}</button>
                    {isEdit && (
                      <button
                        disabled={runNow}
                        onClick={async () => {
                          setRunNow(true); setRunNowMsg("");
                          try {
                            await api("POST", `/monitors/${monitor.MonitorID}/check`);
                            loadRecentLogs();
                            setRunNowMsg("Check complete — results refreshed.");
                            setTimeout(() => setRunNowMsg(""), 3000);
                          } catch(e) {
                            setRunNowMsg("Run failed: " + (e?.message || "unknown error"));
                            setTimeout(() => setRunNowMsg(""), 4000);
                          } finally { setRunNow(false); }
                        }}
                        style={{
                          flex:1, padding:10, borderRadius:6, fontSize:12, fontWeight:700,
                          border:"1px solid #006D8C", cursor: runNow ? "not-allowed" : "pointer",
                          background:"#ffffff", color:"#006D8C",
                          opacity: runNow ? 0.85 : 1,
                          fontFamily:"monospace",
                        }}
                      >{runNow ? `${SPINNER_CHARS[runNowSpinnerFrame]} Running… ${(runNowElapsedTenths/10).toFixed(1)}s` : "▶ Run Now"}</button>
                    )}
                  </div>
                  {testDisabled && <div style={{ textAlign:"center", fontSize:11, color:"#7A9AB8", marginBottom:6 }}>Select a monitor type to enable testing</div>}
                  {runNowMsg && <div style={{ textAlign:"center", fontSize:11, color: runNowMsg.startsWith("Run failed") ? "#D95C5C" : "#008C6F", marginBottom:6 }}>{runNowMsg}</div>}
                  {/* Test Results label row with inline Diagnose button */}
                  <div style={{ position:"relative", display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:8 }}>
                    {barVisible && (
                      <div style={{ position:"absolute", top:0, left:0, right:0, height:3, background:"#E5E7EB", overflow:"hidden", borderRadius:"2px 2px 0 0" }}>
                        <div style={{
                          height:"100%",
                          width: barPct + "%",
                          background:"#006D8C",
                          transition: barPct === 85 ? "width 3s ease-out" : barPct === 100 ? "width 0.05s linear" : "none",
                        }} />
                      </div>
                    )}
                    <span style={{ fontSize:11, fontWeight:700, color:"#4A4A4A", letterSpacing:"0.08em", textTransform:"uppercase" }}>Test Results</span>
                    <div style={{ display:"flex", alignItems:"center", gap:6 }}>
                      {canDiagnose && (
                        <button
                          onClick={() => runDiagnosis({
                            monitorName:    form.MonitorName || monitor?.MonitorName || "",
                            monitorType:    form.MonitorType || "",
                            monitorSubType: selectedLeaf || form.MonitorSubType || "",
                            target:         latestRun.Target || "",
                            protocol:       latestRun.Protocol || "",
                            statusCode:     latestRun.StatusCode != null ? String(latestRun.StatusCode) : "N/A",
                            responseTime:   latestRun.ResponseTimeMs != null ? `${latestRun.ResponseTimeMs}ms` : "N/A",
                            errorMessage:   latestRun.ErrorMessage || "",
                            severity:       latestSev,
                            timeoutMs:      parseInt(form.TimeoutMs) || 5000,
                          })}
                          style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${diagColor}`, borderRadius:4, background:"#fff", color:diagColor, cursor:"pointer", fontWeight:600 }}
                        >🔍 Diagnose</button>
                      )}
                    </div>
                  </div>
                </div>
              );
            })()}
            {testing ? (
              <div style={{ padding:"12px 0" }}>
                {[0,1,2].map(i => (
                  <div key={i} style={{
                    height:14, borderRadius:6,
                    background:"rgba(216,204,186,0.5)",
                    margin:"0 0 10px 0",
                    animation:"op1-skeleton-pulse 1.2s ease-in-out infinite",
                    animationDelay: i * 0.2 + "s",
                    width: i===0?"90%":i===1?"70%":"50%",
                  }} />
                ))}
              </div>
            ) : testRuns.length === 0 ? (
              <div style={{ color:"#7A9AB8", fontSize:12, textAlign:"center", padding:"16px 0" }}>
                Click <strong style={{ color:"#1A1A1A" }}>▶ Test Monitor</strong> to run a check. Results appear here.
              </div>
            ) : (
              <TestResultPane runs={testRuns} meta={{ monitorName: form.MonitorName || monitor?.MonitorName || "", monitorType: form.MonitorType || "", monitorSubType: selectedLeaf || form.MonitorSubType || "", timeoutMs: parseInt(form.TimeoutMs) || 5000, typeConfig: tc, recentLogs: recentLogs, credentialUsername: credentialId ? (credentials.find(cr => cr.CredentialID === parseInt(credentialId))?.Username || null) : null, credentialName: credentialId ? (credentials.find(cr => cr.CredentialID === parseInt(credentialId))?.Name || null) : null }} />
            )}
          </div>
          {/* Recent logs (edit only) */}
          <div style={{ flex:1, minHeight:0, display:"flex", flexDirection:"column", overflow:"hidden", background:"#FFFFFF" }}>{recentLogsPanel}</div>
        </div>

        {/* RIGHT column — AI Diagnosis pane (always rendered; collapses to 0 when closed) */}
        <div style={{ flex: diagOpen ? 4 : 0, transition:"flex 250ms ease", minWidth:0, overflow:"hidden", display:"flex", flexDirection:"column" }}>
          <div style={{ width:"100%", height:"100%", display:"flex", flexDirection:"column", background:"#FFFFFF", borderLeft:"1px solid #C0D8E8", overflow:"hidden" }}>
            {/* Top accent bar */}
            <div style={{ height:3, background:"#006D8C", flexShrink:0 }} />
            {/* Header */}
            <div style={{ background:"#1E2B3C", color:"#fff", padding:"12px 16px", flexShrink:0 }}>
              <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between" }}>
                <div>
                  <div style={{ fontSize:13, fontWeight:700 }}>🔍 AI Diagnosis</div>
                  <div style={{ fontSize:11, color:"rgba(255,255,255,0.55)", marginTop:2, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", maxWidth:180 }}>{diagName || "Monitor"}</div>
                </div>
                <button onClick={() => setDiagOpen(false)} style={{ background:"none", border:"none", color:"rgba(255,255,255,0.7)", cursor:"pointer", fontSize:18, padding:0, lineHeight:1, flexShrink:0 }}>✕</button>
              </div>
            </div>
            {/* Body */}
            <div style={{ flex:1, overflowY:"auto", padding:16, fontSize:13, lineHeight:1.6, color:"#1A1A1A" }}>
              {diagLoading ? (
                <div style={{ display:"flex", alignItems:"center", gap:10, color:"#6B7280", padding:"24px 0" }}>
                  <Spinner /><span>Analysing…</span>
                </div>
              ) : diagError ? (
                <div style={{ background:"#FAEAEA", border:"1px solid #D95C5C40", borderRadius:8, padding:"12px 14px", color:"#D95C5C", fontSize:12 }}>{diagError}</div>
              ) : diagResult ? (
                <div style={{ fontSize:12 }}>
                  {parseScriptBlocks(diagResult).map((seg, si) => seg.type === 'text' ? (
                    <div key={si}>{renderDiagMd(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); setCopiedScriptId(seg.id); setTimeout(() => setCopiedScriptId(null), 2000); }}
                        style={{ position:"absolute", top:6, right:6, background: copiedScriptId === seg.id ? "#008C6F" : "#2D3F52", color:"#fff", border:"none", borderRadius:4, fontSize:10, padding:"2px 7px", cursor:"pointer" }}>
                        {copiedScriptId === seg.id ? "Copied!" : "Copy"}
                      </button>
                    </div>
                  ))}
                </div>
              ) : null}
              {investigationThreadPanel}
            </div>
            {/* Footer */}
            {(diagResult || diagError) && (
              <div style={{ padding:"8px 16px", borderTop:"1px solid #C0D8E8", fontSize:11, color:"#9CA3AF", flexShrink:0 }}>
                Powered by Claude · {diagTs || "—"}
              </div>
            )}
          </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 35s 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>

      {/* Delete confirm dialog */}
      {deleteConfirm && (
        <div style={{ position:"fixed",inset:0,background:"rgba(0,0,0,0.75)",zIndex:3000,display:"flex",alignItems:"center",justifyContent:"center" }}>
          <Card style={{ maxWidth:400,width:"100%",margin:16 }}>
            <div style={{ fontSize:14,fontWeight:600,marginBottom:8 }}>Delete {monitor?.MonitorName}?</div>
            <div style={{ fontSize:12,color:c.textMuted,marginBottom:6 }}>This will permanently remove the monitor and all its check history. This cannot be undone.</div>
            <div style={{ display:"flex",gap:10,justifyContent:"flex-end",marginTop:20 }}>
              <Btn variant="secondary" onClick={()=>setDeleteConfirm(false)}>Cancel</Btn>
              <Btn variant="danger" loading={deleting} onClick={async()=>{
                setDeleting(true);
                try { await api("DELETE",`/monitors/${monitor.MonitorID}`); setDeleteConfirm(false); onClose(); }
                catch(e) { setErr(e.message); setDeleteConfirm(false); }
                finally { setDeleting(false); }
              }}>Delete</Btn>
            </div>
          </Card>
        </div>
      )}

      {/* Dirty-state confirm dialog */}
      {confirmLeave && (
        <div style={{ position:"fixed",inset:0,background:"rgba(0,0,0,0.75)",zIndex:3000,display:"flex",alignItems:"center",justifyContent:"center" }}>
          <Card style={{ maxWidth:360,width:"100%",margin:16 }}>
            <div style={{ fontSize:14,fontWeight:600,marginBottom:8 }}>Unsaved changes</div>
            <div style={{ fontSize:12,color:c.textMuted,marginBottom:20 }}>You have unsaved changes. Discard them?</div>
            <div style={{ display:"flex",gap:10,justifyContent:"flex-end" }}>
              <Btn variant="secondary" onClick={()=>setConfirmLeave(false)}>Keep Editing</Btn>
              <Btn variant="danger"    onClick={()=>{ setIsDirty(false); setConfirmLeave(false); if (pendingNavTab) { onNavigate(pendingNavTab); setPendingNavTab(null); } onClose(); }}>Discard</Btn>
            </div>
          </Card>
        </div>
      )}
    </div>
  );
}



// ── SmallToggle — top-level so all views can use it ──
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>
);

// ═══ MONITORS ═══
function MonitorsView({ api, canManage, isAdmin, initialNav, onNavConsumed, navInterceptRef, onNavigate, machines, currentMachine, setHelpKey, monitoringControl, onNavigateSynthetic }) {
  const [monitors, setMonitors]               = useState([]);
  const [groups, setGroups]                   = useState([]);
  const [loading, setLoading]                 = useState(true);
  const [err, setErr]                         = useState("");
  const [subView, setSubView]                 = useState("list");
  const [editMonitor, setEditMonitor]         = useState(null);
  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 [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);

  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('');
    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");
        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.";
        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");
        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.";
        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.";
    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?.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));
    }
  },[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 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);
        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" && String(m.GroupID||"") !== groupFilter) 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);
    }
  }
  const namedGroups = Object.values(groupMap).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 = 10;
  const MONITOR_COL_GRID = "70px 58px 1fr 130px 160px 50px 80px 80px 56px 140px";

  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) => {
    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;
    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:"#EDE8DF",
          borderTop:"1px solid #D8CCBA", borderBottom:"1px solid #D8CCBA",
          borderLeft:`3px solid ${healthBorderColor}`,
          padding:"6px 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 }} />
        <span style={{ fontWeight:600, fontSize:13, color: isUngrouped ? "#9CA3AF" : "#1A1A1A" }}>{groupName}</span>
        <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 style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:8 }} onClick={e=>e.stopPropagation()}>
          {!isUngrouped && canManage && (
            <button
              onClick={(e) => { e.stopPropagation(); openAddForGroup(groupId, groupName); }}
              style={{ fontSize:11, padding:"3px 10px", lineHeight:1, border:"1px solid #006D8C", borderRadius:6, background:"#ffffff", color:"#006D8C", cursor:"pointer", fontWeight:500 }}
              title={`Add monitor to ${groupName}`}
            >+ Add</button>
          )}
          {!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:"3px 10px", lineHeight:1, border:"0.5px solid #006D8C", borderRadius:4,
                background:"#ffffff", color:"#006D8C", cursor: runningGroupId !== null ? "not-allowed" : "pointer",
                opacity: runningGroupId !== null ? 0.5 : 1, fontWeight:600,
              }}
              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:"3px 10px", lineHeight:1, border:"1px solid #006D8C", background:"#E0F2F7", color:"#006D8C", borderRadius:6, fontWeight:600, cursor:"pointer" }}
              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:"3px 10px", lineHeight:1, border:`1px solid ${active?c.blue:c.border}`, borderRadius:5, background:active?c.blueLight:"#fff", color:active?c.blue:c.textMuted, cursor:"pointer", fontWeight:active?600:400 }}>
                <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>
    );
  };

  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:"pointer" };
    const cell = { fontSize:12, fontWeight:400, color:"#374151", padding:"5px 10px" };
    return (
      <div key={m.MonitorID} className="mon-row"
        onClick={()=>openEdit(m)}
        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 }}>
          {m.MonitorName}
        </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>;
          // Latency-based monitors: use LastResponseTimeMs
          if (['infra-ping','web-http','web-ssl','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 && <SmallToggle checked={isEnabled} onClick={(e)=>toggleEnabled(m,e)} />}
        </div>
        <div style={{ ...cell, padding:"5px 6px", whiteSpace:"nowrap" }} onClick={e=>e.stopPropagation()}>
          {canManage && (
            <div style={{ display:"flex", alignItems:"center", gap:3 }}>
              <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>
              <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>
              <Btn variant="secondary" onClick={()=>openEdit(m)} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>
              <Btn variant="danger"    onClick={()=>setDeleteTarget(m)} style={{ padding:"3px 6px", fontSize:11 }}>Del</Btn>
            </div>
          )}
        </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 && <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, padding:"5px 6px", whiteSpace:"nowrap" }} onClick={e => e.stopPropagation()}>
          {canManage && (
            <div style={{ display:"flex", alignItems:"center", gap:3 }}>
              <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>
              <Btn variant="secondary" onClick={e => { e.stopPropagation(); onNavigateSynthetic && onNavigateSynthetic(f.FlowId); }} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>
              <Btn variant="danger" onClick={async e => { e.stopPropagation(); if (!confirm("Delete this flow?")) return; try { await api("DELETE", `/synthetic/flows/${f.FlowId}`); setSyntheticFlows(prev => prev.filter(x => x.FlowId !== f.FlowId)); } catch(_) {} }} style={{ padding:"3px 6px", fontSize:11, marginLeft:12 }}>Del</Btn>
            </div>
          )}
        </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 indexed = dpts.map((d,i)=>({...d,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,4].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>
      </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); }}
        onSaved={onSaved}
        navInterceptRef={navInterceptRef}
        onNavigate={onNavigate}
        onCopy={m => openEdit(m)}
      />
    );
  }

  // 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;

  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.0.236</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>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"8px 0" }}>
        <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" }}>Enable</div>
            <div style={{ ...hCell, textAlign:"left", whiteSpace:"nowrap" }}>Alerts / Actions</div>
          </div>
        );
        return (
          <div style={{ background:"#FFFFFF", border:"1px solid #E5E7EB", borderRadius:10, overflow:"hidden" }}>
            <div style={{ overflowX:"auto" }}>
              {namedGroups.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);
                return (
                  <React.Fragment key={g.id}>
                    {renderGroupHeader(g.id, g.name, g.monitors, grpData?.Color || null, false)}
                    {isExpanded && subHeaderRow}
                    {isExpanded && rows.map((row, i) => renderMonitorRow(row, i))}
                    {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>
      )}
      </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={async()=>{ if(!window.confirm('Remove this recipient from group?')) return; 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>
        );
      })()}

      {/* ── 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 35s 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()} \u00b7 {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>
  );
}

// ═══ TOPOLOGY ═══
const isAncestorOrDescendant = (node, groupFilter, byId) => {
  let cur = node;
  while (cur.ParentMonitorID && byId[cur.ParentMonitorID]) {
    cur = byId[cur.ParentMonitorID];
    if (cur.GroupName === groupFilter) return true;
  }
  const hasDescInGroup = (n) => {
    if (n.GroupName === groupFilter) return true;
    return (n.children||[]).some(ch => hasDescInGroup(ch));
  };
  return hasDescInGroup(node);
};

function TopologyView({ api, monitoringControl, onNavigate }) {
  const [monitors, setMonitors]         = useState([]);
  const [err, setErr]                   = useState("");
  const [loading, setLoading]           = useState(true);
  const [recentAlerts, setRecentAlerts] = useState([]);
  const [topoGroupFilter, setTopoGroupFilter]             = useState("all");
  const [topoTransform, setTopoTransform]                 = useState({ x:0, y:0, scale:1 });
  const [topoInitialTransform, setTopoInitialTransform]   = useState({ x:0, y:0, scale:1 });
  const [topoPanning, setTopoPanning]   = useState(false);
  const [topoPanStart, setTopoPanStart] = useState({ x:0, y:0 });
  const [topoLinkMode, setTopoLinkMode] = useState(false);
  const [topoLinkSource, setTopoLinkSource] = useState(null);
  const [topoReloadKey, setTopoReloadKey] = useState(0);
  const topoContainerRef = useRef(null);

  const { isPaused:monIsPaused, canManageConfig, pausePopover, setPausePopover,
          pauseReason, setPauseReason, pauseBusy, togglePause,
          currentCtrl, currentMachine } = monitoringControl || {};

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from{transform:translateX(0)} to{transform:translateX(-50%)} }";
      document.head.appendChild(el);
    }
    setLoading(true);
    api("GET","/monitors").then(r=>{ setMonitors(Array.isArray(r)?r:[]); setLoading(false); }).catch(e=>{ setErr(e.message); setLoading(false); });
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api, topoReloadKey]);

  /* ── Build tree ── */
  const topoById = {};
  monitors.forEach(m => { topoById[m.MonitorID] = { ...m, children:[] }; });
  const topoRoots = [];
  monitors.forEach(m => {
    if (m.ParentMonitorID && topoById[m.ParentMonitorID])
      topoById[m.ParentMonitorID].children.push(topoById[m.MonitorID]);
    else
      topoRoots.push(topoById[m.MonitorID]);
  });

  /* ── Layout ── */
  const NODE_W = 150, NODE_H = 130;
  const assignTopoPos = (node, x, y) => {
    node.y = y;
    if (!node.children.length) { node.x = x + NODE_W/2; return NODE_W; }
    let cx = x;
    node.children.forEach(child => { cx += assignTopoPos(child, cx, y + NODE_H); });
    node.x = x + (cx - x) / 2;
    return cx - x;
  };
  const topoTreeRoots = topoRoots.filter(r => r.children.length > 0);
  const topoIsoRoots  = topoRoots.filter(r => r.children.length === 0);
  let tX = 0;
  topoTreeRoots.forEach(r => { tX += assignTopoPos(r, tX, 60); });
  const placedTopo = Object.values(topoById).filter(n => n.x !== undefined);
  const isoY = placedTopo.length ? Math.max(...placedTopo.map(n=>n.y)) + NODE_H + 40 : 60;
  topoIsoRoots.forEach((n,i) => { n.x = i * NODE_W + NODE_W/2; n.y = isoY; });

  /* ── Bounding box ── */
  const allTopo = Object.values(topoById);
  const topoMinX = allTopo.length ? Math.min(...allTopo.map(n=>n.x||0)) : 0;
  const topoMaxX = allTopo.length ? Math.max(...allTopo.map(n=>n.x||0)) : 0;
  const topoMinY = allTopo.length ? Math.min(...allTopo.map(n=>n.y||0)) : 0;
  const topoMaxY = allTopo.length ? Math.max(...allTopo.map(n=>n.y||0)) : 0;
  const topoContentW = topoMaxX - topoMinX + 100;
  const topoContentH = topoMaxY - topoMinY + 100;

  /* ── Auto-fit on load ── */
  useEffect(()=>{
    if (!loading && monitors.length > 0 && topoContainerRef.current) {
      const vpW = topoContainerRef.current.clientWidth  || 800;
      const vpH = topoContainerRef.current.clientHeight || 500;
      const scale = Math.min((vpW-80)/topoContentW, (vpH-80)/topoContentH, 1.0);
      const fit = {
        x: (vpW - topoContentW*scale)/2 - topoMinX*scale,
        y: (vpH - topoContentH*scale)/2 - topoMinY*scale,
        scale
      };
      setTopoTransform(fit);
      setTopoInitialTransform(fit);
    }
  },[loading, monitors.length]);

  /* ── Visibility set (group filter) ── */
  const topoStatusColor = s => s==='healthy'?'#008C6F':s==='warning'?'#E89A2E':s==='critical'?'#D95C5C':'#4A4A4A';
  const visibleTopoIds = new Set(
    Object.values(topoById)
      .filter(node => topoGroupFilter==='all' || node.GroupName===topoGroupFilter || isAncestorOrDescendant(node, topoGroupFilter, topoById))
      .map(n => n.MonitorID)
  );
  const nHealthy  = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='healthy').length;
  const nWarning  = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='warning').length;
  const nCritical = monitors.filter(m=>m.IsEnabled!==false&&m.EffectiveStatus==='critical').length;
  const nDisabled = monitors.filter(m=>m.IsEnabled===false).length;

  const topoHandleNodeClick = (node) => {
    if (!topoLinkMode) { onNavigate && onNavigate(node.MonitorID); return; }
    if (topoLinkSource === null) { setTopoLinkSource(node.MonitorID); return; }
    if (topoLinkSource === node.MonitorID) { setTopoLinkSource(null); return; }
    const srcNode = topoById[topoLinkSource];
    const srcName = srcNode ? srcNode.MonitorName : String(topoLinkSource);
    const dstName = node.MonitorName;
    if (window.confirm(`Set "${srcName}" as child of "${dstName}"?\n\nThis will update the ParentMonitorID of "${srcName}".`)) {
      api("PATCH", `/monitors/${topoLinkSource}/parent`, { ParentMonitorID: node.MonitorID })
        .then(() => { setTopoReloadKey(k=>k+1); setTopoLinkSource(null); setTopoLinkMode(false); })
        .catch(() => alert("Failed to save relationship. Please try again."));
    }
  };

  const topoHandleNodeRightClick = (e, node) => {
    e.preventDefault();
    if (!node.ParentMonitorID) return;
    if (window.confirm(`Remove parent relationship from "${node.MonitorName}"?\n\nThis will clear its ParentMonitorID.`)) {
      api("PATCH", `/monitors/${node.MonitorID}/parent`, { ParentMonitorID: null })
        .then(() => setTopoReloadKey(k=>k+1))
        .catch(() => alert("Failed to remove relationship. Please try again."));
    }
  };

  return (
    <div style={{ height:"calc(100vh - 20px)", overflow:"hidden", display:"flex", flexDirection:"column", background:"#ffffff" }}>

      {/* ── Topbar ── */}
      <div style={{ background:'#ffffff', borderBottom:`1px solid ${c.border}`, padding:'10px 20px', display:'flex', alignItems:'center', gap:16, flexShrink:0 }}>
        <div style={{ fontWeight:700, fontSize:15, color:c.text }}>Topology</div>
        <div style={{ fontSize:11, color:c.textDimmer }}>Dependency graph · {monitors.length} nodes</div>
        <div style={{ marginLeft:'auto', display:'flex', alignItems:'center', gap:12 }}>
          <span style={{ fontSize:11, color:'#008C6F', fontWeight:600 }}>● {nHealthy} Healthy</span>
          <span style={{ fontSize:11, color:'#E89A2E', fontWeight:600 }}>● {nWarning} Warning</span>
          <span style={{ fontSize:11, color:'#D95C5C', fontWeight:600 }}>● {nCritical} Critical</span>
          {nDisabled > 0 && <span style={{ fontSize:11, color:'#4A4A4A', fontWeight:600 }}>● {nDisabled} Disabled</span>}
          <select style={{ fontSize:11, border:`1px solid ${c.border}`, borderRadius:4, padding:'3px 8px', background:'#ffffff', color:c.text }}
            value={topoGroupFilter} onChange={e => setTopoGroupFilter(e.target.value)}>
            <option value="all">All Groups</option>
            {[...new Set(monitors.map(m=>m.GroupName).filter(Boolean))].map(g =>
              <option key={g} value={g}>{g}</option>
            )}
          </select>
          <button onClick={() => setTopoTransform(topoInitialTransform)}
            style={{ fontSize:11, border:`1px solid ${c.blue}`, borderRadius:4, padding:'3px 10px', background:'#ffffff', color:c.blue, cursor:'pointer' }}>
            Reset View
          </button>
          <button onClick={()=>{ setTopoLinkMode(m=>{ if(m) setTopoLinkSource(null); return !m; }); }}
            style={{ fontSize:11, borderRadius:4, padding:'3px 10px', cursor:'pointer',
              border:`1px solid ${topoLinkMode ? c.red : c.blue}`,
              background: topoLinkMode ? c.redLight : '#ffffff',
              color: topoLinkMode ? c.red : c.blue }}>
            {topoLinkMode ? '✕ Cancel Link' : '⚡ Link Monitors'}
          </button>
        </div>
      </div>

      {/* ── Link mode hint bar ── */}
      {topoLinkMode && (
        <div style={{ background:c.blueLight, borderBottom:`1px solid ${c.border}`, padding:'6px 20px', fontSize:11, color:c.blue, flexShrink:0 }}>
          Click a monitor to select it as the child, then click another monitor to set it as the parent. Right-click any monitor to remove its parent relationship.
        </div>
      )}

      {/* ── SVG Canvas ── */}
      <div style={{ flex:1, overflow:'hidden', position:'relative', background:'#FFFFFF' }} ref={topoContainerRef}>
        {err && <div style={{ padding:16 }}><ErrBox msg={err} /></div>}
        {loading && <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center' }}><Spinner /></div>}
        {!loading && monitors.length > 0 && (
          <svg width="100%" height="100%"
            style={{ cursor:topoPanning?'grabbing':'grab', display:'block', userSelect:'none' }}
            onMouseDown={e=>{ if(e.button!==0)return; setTopoPanning(true); setTopoPanStart({x:e.clientX-topoTransform.x, y:e.clientY-topoTransform.y}); }}
            onMouseMove={e=>{ if(topoPanning) setTopoTransform(t=>({...t, x:e.clientX-topoPanStart.x, y:e.clientY-topoPanStart.y})); }}
            onMouseUp={()=>setTopoPanning(false)}
            onMouseLeave={()=>setTopoPanning(false)}
            onWheel={e=>{ e.preventDefault(); const delta=e.deltaY>0?0.9:1.1; setTopoTransform(t=>{ const ns=Math.min(2.5,Math.max(0.4,t.scale*delta)); const r=ns/t.scale; return {scale:ns, x:e.clientX-r*(e.clientX-t.x), y:e.clientY-r*(e.clientY-t.y)}; }); }}>
            <rect width="100%" height="100%" fill="#FFFFFF" />
            <g transform={`translate(${topoTransform.x},${topoTransform.y}) scale(${topoTransform.scale})`}>
              {/* Edges */}
              {Object.values(topoById).filter(n => n.ParentMonitorID && topoById[n.ParentMonitorID] && visibleTopoIds.has(n.MonitorID) && visibleTopoIds.has(n.ParentMonitorID)).map(node => {
                const parent = topoById[node.ParentMonitorID];
                return <path key={`edge-${node.MonitorID}`}
                  d={`M${parent.x},${parent.y+26} C${parent.x},${parent.y+70} ${node.x},${node.y-70} ${node.x},${node.y-26}`}
                  stroke="#006D8C" strokeOpacity="0.4" strokeWidth="1.5" fill="none" />;
              })}
              {/* Nodes */}
              {Object.values(topoById).filter(n => visibleTopoIds.has(n.MonitorID)).map(node => {
                const col       = node.IsEnabled === false ? '#4A4A4A' : topoStatusColor(node.EffectiveStatus);
                const label     = (node.MonitorName||'').length > 16 ? (node.MonitorName||'').substring(0,16)+'\u2026' : (node.MonitorName||'');
                const grp       = (node.GroupName||'').length > 14 ? (node.GroupName||'').substring(0,14)+'\u2026' : (node.GroupName||'');
                const letter    = (node.MonitorType||'?')[0].toUpperCase();
                const isLinkSrc = topoLinkMode && topoLinkSource === node.MonitorID;
                return (
                  <g key={node.MonitorID}
                    style={{ cursor: topoLinkMode ? 'crosshair' : 'pointer' }}
                    transform={isLinkSrc ? `translate(${node.x},${node.y}) scale(1.1)` : `translate(${node.x},${node.y})`}
                    onClick={() => topoHandleNodeClick(node)}
                    onContextMenu={e => topoHandleNodeRightClick(e, node)}
                    onMouseDown={e => e.stopPropagation()}
                    onMouseEnter={e => e.currentTarget.setAttribute('opacity','0.82')}
                    onMouseLeave={e => e.currentTarget.setAttribute('opacity','1')}>
                    {topoLinkMode && (isLinkSrc
                      ? <circle cx={0} cy={0} r={32} fill="none" stroke={c.blue} strokeWidth={3} />
                      : <circle cx={0} cy={0} r={32} fill="none" stroke={c.blue} strokeWidth={1} strokeDasharray="3 2" />
                    )}
                    <circle cx={0} cy={0} r={26} fill={col} stroke="white" strokeWidth={2}
                      strokeDasharray={node.IsSuppressed ? '4 2' : undefined} />
                    <text x={0} y={0} textAnchor="middle" dominantBaseline="central"
                      fill="white" fontSize={13} fontWeight="bold" style={{pointerEvents:'none'}}>{letter}</text>
                    <text x={0} y={42} textAnchor="middle"
                      fill={c.text} fontSize={10} style={{pointerEvents:'none'}}>{label}</text>
                    <text x={0} y={54} textAnchor="middle"
                      fill={c.textDimmer} fontSize={9} style={{pointerEvents:'none'}}>{grp}</text>
                  </g>
                );
              })}
            </g>
          </svg>
        )}
        {!loading && monitors.length === 0 && !err && (
          <div style={{ position:'absolute', inset:0, display:'flex', alignItems:'center', justifyContent:'center', color:c.textDimmer, fontSize:14 }}>No monitors configured</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 35s 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>
  );
}

// ═══ ALERTS ═══
function AlertsView({ api, canAck, machines, currentMachine, onNavigate, monitoringControl, onNavigateSynthetic }) {
  const [alerts, setAlerts]               = useState([]);
  const [groups, setGroups]               = useState([]);
  const [loading, setLoading]             = useState(true);
  const [err, setErr]                     = useState("");
  const [anomalyOnly, setAnomalyOnly]     = useState(false);
  const [unresolvedOnly, setUnresolvedOnly] = useState(true);
  const [groupFilter, setGroupFilter]     = useState("");
  const [machineFilter, setMachineFilter] = useState("");
  const [busy, setBusy]                   = useState({});
  const [recentAlerts, setRecentAlerts]   = useState([]);
  const [activeDiagId, setActiveDiagId]   = useState(null);
  const [diagLoading, setDiagLoading]     = useState(false);
  const [diagData, setDiagData]           = useState(null);
  const [diagError, setDiagError]         = useState("");
  const [diagAlert, setDiagAlert]         = useState(null);
  const [severityFilter, setSeverityFilter] = useState("all");
  const [selectedAlerts, setSelectedAlerts] = useState(new Set());

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/monitors/groups").then(r=>setGroups(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const load = useCallback(()=>{
    setLoading(true);
    const q=new URLSearchParams({top:"200",unresolvedOnly:unresolvedOnly?"true":"false"});
    if(anomalyOnly)   q.set("anomalyOnly","true");
    if(groupFilter)   q.set("groupId", groupFilter);
    if(machineFilter) q.set("sourceMachine", machineFilter);
    api("GET",`/logs/alerts?${q}`).then(r=>{setAlerts(Array.isArray(r)?r:[]); setErr("");}).catch(e=>setErr(e.message)).finally(()=>setLoading(false));
  },[api,anomalyOnly,unresolvedOnly,groupFilter,machineFilter]);
  useEffect(()=>{load();},[load]);
  useEffect(()=>{ setSelectedAlerts(new Set()); },[severityFilter,groupFilter,machineFilter,unresolvedOnly,anomalyOnly]);

  const act = async (action,id) => {
    setBusy(b=>({...b,[id]:true}));
    try { await api("POST",`/logs/alerts/${id}/${action}`,{}); load(); } catch(e){setErr(e.message);}
    finally { setBusy(b=>({...b,[id]:false})); }
  };

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  const aCritical   = alerts.filter(a=>a.Severity==="critical").length;
  const aUnresolved = alerts.filter(a=>!a.ResolvedAt).length;
  const aSuppressed = alerts.filter(a=>a.IsSuppressed).length;

  function alertHint(alertType, severity, message) {
    if (alertType === 'recovery')
      return 'Monitor self-recovered — no action required unless recurrence is frequent.';
    if (alertType === 'ping_fail')
      return 'Target is unreachable. Check host availability, firewall rules, or network path.';
    if (alertType === 'threshold_breach') {
      if (message && message.includes('301'))
        return 'HTTP 301 redirect — content match will always fail until the monitor URL is updated to the final destination.';
      if (message && message.includes('302'))
        return 'HTTP 302 temporary redirect — target URL may have moved. Verify expected endpoint.';
      if (message && (message.toLowerCase().includes('row') || message.toLowerCase().includes('count')))
        return 'Table row count breached threshold — review data growth rate and consider a purge or archival job.';
      if (severity === 'warning')
        return 'Warning threshold crossed. Monitor the trend — if persistent, investigate the root cause before it escalates.';
      if (severity === 'critical')
        return 'Critical threshold breached. Immediate investigation recommended.';
      return 'Threshold exceeded — review monitor configuration and current target state.';
    }
    if (alertType === 'ssl_expiry')
      return 'SSL certificate expiring soon — renew before expiry to avoid service disruption.';
    return 'Review monitor details and recent check history for context.';
  }

  function formatAlertTime(dateStr) {
    if (!dateStr) return { relative: "—", absolute: "—" };
    const d = parseUTC(dateStr);
    if (!d) return { relative: "—", absolute: "—" };
    const diffMs = Date.now() - d.getTime();
    const diffS  = Math.floor(diffMs / 1000);
    const diffM  = Math.floor(diffMs / 60000);
    const diffH  = Math.floor(diffMs / 3600000);
    const diffD  = Math.floor(diffMs / 86400000);
    let relative;
    if (diffS < 60)      relative = "just now";
    else if (diffM < 60) relative = `${diffM}m ago`;
    else if (diffH < 24) relative = `${diffH}h ago`;
    else if (diffD < 7)  relative = `${diffD}d ago`;
    else                 relative = d.toLocaleDateString();
    const absolute = d.toLocaleString([], { month:"2-digit", day:"2-digit", year:"numeric", hour:"2-digit", minute:"2-digit" });
    return { relative, absolute };
  }

  function parseDiagnosisText(text) {
    if (!text) return { what: "", cause: "", steps: [] };
    const t = text.trim();
    try {
      const j = JSON.parse(t);
      if (j && typeof j === 'object' && !Array.isArray(j)) {
        return {
          what:  j.whatThisMeans   || "",
          cause: j.likelyRootCause || "",
          steps: Array.isArray(j.recommendedSteps) ? j.recommendedSteps : []
        };
      }
    } catch(e) { /* fall through to markdown */ }
    const whatMatch  = t.match(/##\s*What This Means\s*\n([\s\S]*?)(?=##|$)/i);
    const causeMatch = t.match(/##\s*Likely Root Cause\s*\n([\s\S]*?)(?=##|$)/i);
    const stepsMatch = t.match(/##\s*Recommended Steps\s*\n([\s\S]*?)(?=##|$)/i);
    const steps = stepsMatch
      ? stepsMatch[1].trim().split('\n').filter(s => s.trim()).map(s => s.replace(/^\d+\.\s*/, '').trim())
      : [];
    return {
      what:  whatMatch  ? whatMatch[1].trim()  : "",
      cause: causeMatch ? causeMatch[1].trim() : "",
      steps
    };
  }

  function openDiagnosis(alert) {
    setActiveDiagId(alert.AlertID);
    setDiagAlert(alert);
    setDiagData(null);
    setDiagError("");
    setDiagLoading(true);
    api("GET", `/logs/alerts/${alert.AlertID}/diagnosis`)
      .then(r => {
        if (r && r.status === "pending") {
          setDiagData({ pending: true });
        } else {
          setDiagData(r);
        }
      })
      .catch(e => {
        if (e.message === "HTTP 404") {
          setDiagData({ pending: true });
        } else {
          setDiagError(e.message);
        }
      })
      .finally(() => setDiagLoading(false));
  }

  function closeDiagnosis() {
    setActiveDiagId(null);
    setDiagAlert(null);
    setDiagData(null);
    setDiagError("");
  }

  async function markStale(alertId) {
    await api("POST", `/logs/alerts/${alertId}/mark-stale`, {});
    openDiagnosis(diagAlert);
  }

  const diagParsed = parseDiagnosisText(diagData?.diagnosisText);

  const diagAiContent = diagLoading ? (
    <div style={{ display:"flex", alignItems:"center", gap:8, padding:"8px 0" }}>
      <div style={{ width:12, height:12, border:"2px solid #D8CCBA", borderTopColor:"#006D8C", borderRadius:"50%", animation:"spin 0.8s linear infinite", flexShrink:0 }} />
      <span style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic" }}>Analysing alert context…</span>
    </div>
  ) : diagError ? (
    <div style={{ fontSize:11, color:"#D95C5C" }}>Diagnosis unavailable.</div>
  ) : diagData?.pending ? (
    <div>
      <div style={{ fontSize:11, color:"#4A4A4A", marginBottom:8 }}>Analysis queued — check back in a moment.</div>
      <button onClick={()=>openDiagnosis(diagAlert)} style={{ fontSize:10, padding:"3px 10px", border:"1px solid #006D8C", borderRadius:3, background:"#E0F2F7", color:"#006D8C", cursor:"pointer" }}>Retry</button>
    </div>
  ) : diagData?.isSynthetic ? (
    (() => {
      const parseBold = (s) => s.split(/(\*\*[^*]+\*\*)/g).map((p,j) =>
        p.startsWith("**")&&p.endsWith("**") ? <strong key={j}>{p.slice(2,-2)}</strong> : p
      );
      return (
        <div>
          {(diagData.diagnosisText||"").split("\n").map((line,i) => {
            const t=line.trim();
            if (/^\d+\./.test(t)) { const n=t.match(/^\d+/)[0]; return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#006D8C", minWidth:14, fontSize:11 }}>{n}.</span><span style={{ fontSize:11, lineHeight:1.6 }}>{parseBold(t.slice(n.length+1).trim())}</span></div>; }
            if (t==="") return <div key={i} style={{ height:6 }} />;
            return <div key={i} style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6, marginBottom:2 }}>{parseBold(line)}</div>;
          })}
        </div>
      );
    })()
  ) : diagData ? (
    <div>
      {diagData.isRepeatMatch && (
        <div style={{ background:"#E0F2F7", border:"1px solid #006D8C", borderRadius:4, padding:"6px 8px", fontSize:11, color:"#006D8C", marginBottom:10 }}>
          Sourced from a prior alert on this monitor — same root cause likely applies.
        </div>
      )}
      {diagData.diagnosisIsStale && (
        <div style={{ background:"#FDF3E0", border:"1px solid #E89A2E", borderRadius:4, padding:"6px 8px", fontSize:11, color:"#C07A10", marginBottom:10, display:"flex", alignItems:"center", justifyContent:"space-between" }}>
          <span>Diagnosis may be outdated — conditions may have changed.</span>
          <button onClick={()=>markStale(diagAlert.AlertID)} style={{ fontSize:10, padding:"2px 8px", border:"1px solid #E89A2E", borderRadius:3, background:"#ffffff", color:"#C07A10", cursor:"pointer", marginLeft:8, flexShrink:0 }}>Re-investigate →</button>
        </div>
      )}
      {diagParsed.what && (
        <div style={{ marginBottom:12 }}>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>What this means</div>
          <div style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6 }}>{diagParsed.what}</div>
        </div>
      )}
      {diagParsed.cause && (
        <div style={{ marginBottom:12 }}>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>Likely root cause</div>
          <div style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6 }}>{diagParsed.cause}</div>
        </div>
      )}
      {diagParsed.steps.length > 0 && (
        <div>
          <div style={{ fontSize:10, fontWeight:500, textTransform:"uppercase", letterSpacing:"0.06em", color:"#006D8C", marginBottom:5 }}>Recommended steps</div>
          <ol style={{ margin:0, paddingLeft:16 }}>
            {diagParsed.steps.map((step, i) => (
              <li key={i} style={{ fontSize:11, color:"#1A1A1A", lineHeight:1.6, marginBottom:3 }}>{step}</li>
            ))}
          </ol>
        </div>
      )}
    </div>
  ) : null;

  const diagPanel = (
    <div style={{ flex:1, background:"#ffffff", border:"1px solid #D8CCBA", borderRadius:6, overflow:"hidden", position:"sticky", top:14, display:"flex", flexDirection:"column" }}>
      {!activeDiagId ? (
        <div style={{ flex:1, display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center", padding:"40px 20px", textAlign:"center" }}>
          <div style={{ width:36, height:36, borderRadius:"50%", background:"#E0F2F7", display:"flex", alignItems:"center", justifyContent:"center", fontSize:18, color:"#006D8C", marginBottom:12 }}>✦</div>
          <div style={{ fontSize:13, fontWeight:500, color:"#1A1A1A", marginBottom:6 }}>AI Diagnostics</div>
          <div style={{ fontSize:11, color:"#4A4A4A" }}>Click Diagnose on any alert to run AI-assisted root cause analysis.</div>
        </div>
      ) : (
        <div style={{ display:"flex", flexDirection:"column", height:"100%" }}>
          <div style={{ background:"#1E2B3C", padding:"10px 14px", display:"flex", justifyContent:"space-between", alignItems:"flex-start", flexShrink:0 }}>
            <div>
              <div style={{ fontSize:12, fontWeight:500, color:"#F4F8FC" }}>{diagAlert?.MonitorName || diagAlert?.FlowName || '—'}</div>
              <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>
                {diagAlert?.GroupName || (diagAlert?.FlowName ? 'synthetic' : '—')}{" · "}
                {diagAlert?.AlertTimestamp ? parseUTC(diagAlert.AlertTimestamp)?.toLocaleTimeString([], { hour:"2-digit", minute:"2-digit" }) : ""}
                {" · "}
                <span style={{ fontSize:9, fontWeight:500, padding:"1px 5px", borderRadius:3, textTransform:"uppercase",
                  background: diagAlert?.Severity==="critical" ? "#FAEAEA" : diagAlert?.Severity==="warning" ? "#FDF3E0" : "#E0F2F7",
                  color: diagAlert?.Severity==="critical" ? "#D95C5C" : diagAlert?.Severity==="warning" ? "#C07A10" : "#006D8C" }}>
                  {diagAlert?.Severity}
                </span>
              </div>
            </div>
            <button onClick={closeDiagnosis} style={{ background:"none", border:"none", cursor:"pointer", color:"#7A9AB8", fontSize:16, lineHeight:1, padding:"0 2px" }}>✕</button>
          </div>
          <div style={{ flex:1, overflowY:"auto", padding:14 }}>
            <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:6, marginBottom:12 }}>
              {[
                ["Status",     diagAlert?.Severity,  diagAlert?.Severity==="critical" ? "#D95C5C" : diagAlert?.Severity==="warning" ? "#E89A2E" : "#008C6F"],
                ["Alert type", diagAlert?.AlertType, "#1A1A1A"],
                ["Timestamp",  diagAlert?.AlertTimestamp ? parseUTC(diagAlert.AlertTimestamp)?.toLocaleDateString() : "", "#1A1A1A"],
                ["Occurrence", diagData?.isRepeatMatch ? "Repeat of prior alert" : "First occurrence", "#1A1A1A"],
              ].map(([label, value, col]) => (
                <div key={label} style={{ background:"#F5F0E8", borderRadius:4, padding:"6px 8px" }}>
                  <div style={{ fontSize:9, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:2 }}>{label}</div>
                  <div style={{ fontSize:11, fontWeight:500, color:col }}>{value}</div>
                </div>
              ))}
            </div>
            <div style={{ display:"flex", gap:4, flexWrap:"wrap", marginBottom:12 }}>
              {[
                diagAlert?.GroupName,
                diagAlert?.SourceMachine,
                diagAlert?.MonitorName && !diagAlert?.FlowName ? diagAlert.MonitorName : null,
                diagAlert?.FlowName ? diagAlert.FlowName : null,
                diagAlert?.AlertType,
              ].filter(Boolean).map((tag, i) => (
                <span key={i} style={{ fontSize:10, padding:"2px 8px", borderRadius:3, background:"#E0F2F7", color:"#006D8C", border:"1px solid #D8CCBA", fontWeight:500 }}>{tag}</span>
              ))}
            </div>
            <div style={{ borderTop:"1px solid #D8CCBA", margin:"12px 0" }} />
            {diagAiContent}
          </div>
        </div>
      )}
    </div>
  );

  const bulkAct = async (action, ids) => {
    for (const id of ids) {
      try { await api("POST", `/logs/alerts/${id}/${action}`, {}); } catch(e) { setErr(e.message); }
    }
    setSelectedAlerts(new Set());
    load();
  };

  const filteredAlerts = severityFilter === "all" ? alerts : alerts.filter(a => a.Severity?.toLowerCase() === severityFilter);

  const alertBlocks = filteredAlerts.map(a => {
    const isActive = activeDiagId === a.AlertID;
    const activeBg = a.Severity==="critical" ? "#FDF0F0" : a.Severity==="warning" ? "#FDF8EE" : "#E6F4FA";
    const activeBorderCol = a.Severity==="critical" ? "#D95C5C" : a.Severity==="warning" ? "#E89A2E" : "#006D8C";
    const blockBg = isActive ? activeBg : "#ffffff";
    const blockBorderLeft = isActive ? `3px solid ${activeBorderCol}` : "3px solid transparent";
    const machineName = machines?.find(m => m.MachineName === a.SourceMachine)?.MachineAlias || a.SourceMachine;
    const hint = alertHint(a.AlertType, a.Severity, a.Message);
    const sevBg  = a.Severity==="critical" ? "#FAEAEA" : a.Severity==="warning" ? "#FDF3E0" : "#E0F2F7";
    const sevCol = a.Severity==="critical" ? "#D95C5C" : a.Severity==="warning" ? "#C07A10" : "#006D8C";
    const fmtTime = formatAlertTime(a.AlertTimestamp);
    const isSelected = selectedAlerts.has(a.AlertID);
    const hasRecovered = a.Message && a.Message.toLowerCase().includes("recovered");
    return (
      <div key={a.AlertID} style={{ borderBottom:"1px solid #D8CCBA", background:blockBg, borderLeft:blockBorderLeft }}
        onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = "#F0F7FB"; }}
        onMouseLeave={e => { if (!isActive) e.currentTarget.style.background = "#ffffff"; }}>
        <div style={{ display:"grid", gridTemplateColumns:"32px 72px 130px 120px 80px minmax(0,1fr) 130px", alignItems:"center", padding:"8px 12px 3px 12px" }}>
          <div style={{ display:"flex", justifyContent:"center" }} onClick={e => e.stopPropagation()}>
            <input type="checkbox" checked={isSelected} onChange={e => {
              setSelectedAlerts(prev => {
                const next = new Set(prev);
                e.target.checked ? next.add(a.AlertID) : next.delete(a.AlertID);
                return next;
              });
            }} style={{ cursor:"pointer", accentColor:"#006D8C" }} />
          </div>
          <div>
            <span style={{ fontSize:9, fontWeight:500, padding:"2px 6px", borderRadius:3, textTransform:"uppercase", background:sevBg, color:sevCol }}>{a.Severity}</span>
          </div>
          <div>
            {a.MonitorName
              ? <span onClick={()=>onNavigate&&onNavigate(a.MonitorID)} style={{ fontSize:12, color:"#006D8C", fontWeight:500, cursor:onNavigate?"pointer":"default" }}>{a.MonitorName}</span>
              : a.FlowName
                ? <span onClick={()=>onNavigateSynthetic&&onNavigateSynthetic(a.FlowId,null,false)} style={{ fontSize:12, color:"#006D8C", fontWeight:500, cursor:onNavigateSynthetic?"pointer":"default" }}>{a.FlowName}</span>
                : null}
            {a.IsAnomaly && <span style={{ fontSize:9, fontWeight:500, padding:"1px 5px", borderRadius:3, background:"#ede9fe", color:"#6d28d9", marginLeft:4 }}>⚡ {a.AnomalyScore != null ? `${Number(a.AnomalyScore).toFixed(1)}σ` : "ANOMALY"}</span>}
          </div>
          <div>
            <div style={{ fontSize:11, color:"#006D8C" }}>{a.GroupName}</div>
            <div style={{ fontSize:10, color:"#4A4A4A" }}>{machineName}</div>
          </div>
          <div>
            <div style={{ fontWeight:600, fontSize:12, color:c.text }}>{fmtTime.relative}</div>
            <div style={{ fontSize:10, color:c.textMuted, marginTop:1 }}>{fmtTime.absolute}</div>
          </div>
          <div style={{ fontSize:12, color:"#1A1A1A", paddingRight:8, minWidth:0, display:"flex", alignItems:"center", gap:4 }}>
            <span style={{ overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", flex:1, minWidth:0 }}>{a.Message}</span>
            {hasRecovered && <span style={{ fontSize:9, fontWeight:600, background:"#DCF2EA", color:"#008C6F", padding:"1px 5px", borderRadius:3, marginLeft:6, flexShrink:0 }}>SELF-RECOVERED</span>}
          </div>
          <div style={{ display:"flex", flexDirection:"column", gap:3, alignItems:"flex-start", minWidth:130 }}>
            {(a.AlertType === 'synthetic-flow' || a.AlertType === 'synthetic-duration') && onNavigateSynthetic && a.FlowId && (
              <div style={{ display:"flex", gap:3, flexWrap:"wrap", width:"100%" }}>
                <button onClick={() => onNavigateSynthetic(a.FlowId, a.FlowResultId || null, false)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>
                  View Run →
                </button>
                <button
                  disabled={!a.FlowResultId}
                  title={!a.FlowResultId ? "Diagnosis requires a result ID — re-run the flow to generate a diagnosable result" : undefined}
                  onClick={() => { if (a.FlowResultId) onNavigateSynthetic(a.FlowId, a.FlowResultId, true); }}
                  style={{ fontSize:9, padding:"2px 5px", border:"1px solid #006D8C", borderRadius:3, background:"#006D8C", color:"#fff", fontWeight:500, cursor:a.FlowResultId?"pointer":"not-allowed", opacity:a.FlowResultId?1:0.5 }}>
                  ⚡ Diagnose
                </button>
              </div>
            )}
            {!a.ResolvedAt && canAck && (
              <div style={{ display:"flex", gap:3, flexWrap:"wrap" }}>
                {!a.AcknowledgedAt && (
                  <button disabled={busy[a.AlertID]} onClick={() => act("acknowledge", a.AlertID)}
                    style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>
                    {busy[a.AlertID] ? "…" : "ACK"}
                  </button>
                )}
                <button disabled={busy[a.AlertID]} onClick={() => act("resolve", a.AlertID)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #bbf7d0", borderRadius:3, background:"#dcfce7", color:"#166534", cursor:"pointer" }}>
                  {busy[a.AlertID] ? "…" : "Resolve"}
                </button>
                <button disabled={busy[a.AlertID]} onClick={() => act("suppress", a.AlertID)}
                  style={{ fontSize:9, padding:"2px 5px", border:"0.5px solid #fde68a", borderRadius:3, background:"#fef9c3", color:"#854d0e", cursor:"pointer" }}>
                  {busy[a.AlertID] ? "…" : "Suppress"}
                </button>
              </div>
            )}
            <button onClick={e => { e.stopPropagation(); openDiagnosis(a); }}
              style={{ fontSize:10, padding:"2px 8px", borderRadius:3,
                border:"1px solid #006D8C", fontWeight:500,
                background: isActive ? "#006D8C" : "#E0F2F7",
                color:       isActive ? "#ffffff"  : "#006D8C",
                cursor:"pointer", whiteSpace:"nowrap", width:"100%" }}>Diagnose</button>
          </div>
        </div>
        <div style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic", lineHeight:1.45, padding:"2px 12px 8px 14px", width:"100%", boxSizing:"border-box" }}>
          ↳ {hint}
        </div>
      </div>
    );
  });

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Total Alerts", alerts.length,  "#1e293b"],
            ["Critical",     aCritical,      aCritical   > 0 ? "#dc2626" : "#1e293b"],
            ["Unresolved",   aUnresolved,    aUnresolved > 0 ? "#d97706" : "#1e293b"],
            ["Suppressed",   aSuppressed,    "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:12 }}>
        <label style={{ display:"flex", alignItems:"center", gap:6, fontSize:11, color:"#475569", cursor:"pointer", flexShrink:0, whiteSpace:"nowrap" }}>
          <input type="checkbox" checked={unresolvedOnly} onChange={e=>setUnresolvedOnly(e.target.checked)} style={{ accentColor:"#006D8C" }} /> Unresolved only
        </label>
        <label style={{ display:"flex", alignItems:"center", gap:6, fontSize:11, color:"#7c3aed", cursor:"pointer", flexShrink:0, whiteSpace:"nowrap" }}>
          <input type="checkbox" checked={anomalyOnly} onChange={e=>setAnomalyOnly(e.target.checked)} style={{ accentColor:"#7c3aed" }} /> Anomaly only
        </label>
        <select value={severityFilter} onChange={e=>setSeverityFilter(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 Severities</option>
          <option value="critical">Critical</option>
          <option value="warning">Warning</option>
          <option value="info">Info</option>
        </select>
        <div style={{ width:"0.5px", height:18, background:"#e2e5ea", flexShrink:0 }} />
        <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 Groups</option>
          {groups.map(g=><option key={g.GroupID} value={String(g.GroupID)}>{g.GroupName}</option>)}
        </select>
        {machines && machines.length >= 1 && (
          <select value={machineFilter} onChange={e=>setMachineFilter(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 Machines</option>
            {machines.map(m=><option key={m.MachineName} value={m.MachineName}>{m.MachineAlias||m.MachineName}{m.MachineName===currentMachine?" (this)":""}</option>)}
          </select>
        )}
        <div style={{ marginLeft:"auto", flexShrink:0 }}>
          <button onClick={load} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer" }}>Refresh</button>
        </div>
      </div>

      {/* ── Workspace ── */}
      <div style={{ display:"flex", gap:14, padding:"14px 16px", width:"100%", boxSizing:"border-box", alignItems:"flex-start" }}>

        {/* Left panel — alerts table */}
        <div style={{ flex:"0 0 68%", display:"flex", flexDirection:"column", background:"#ffffff", border:"1px solid #D8CCBA", borderRadius:6, overflow:"hidden", minWidth:0 }}>
          <div style={{ display:"grid", gridTemplateColumns:"32px 72px 130px 120px 80px minmax(0,1fr) 130px", background:"#D8CCBA", borderBottom:"1px solid #D8CCBA", fontSize:11, color:"#1A1A1A", fontWeight:600, textTransform:"uppercase", letterSpacing:"0.05em", padding:"6px 12px", flexShrink:0, position:"sticky", top:0, zIndex:1 }}>
            <span style={{ display:"flex", justifyContent:"center" }}>
              <input type="checkbox" style={{ cursor:"pointer", accentColor:"#006D8C" }}
                checked={filteredAlerts.length > 0 && filteredAlerts.every(a => selectedAlerts.has(a.AlertID))}
                onChange={e => { setSelectedAlerts(e.target.checked ? new Set(filteredAlerts.map(a => a.AlertID)) : new Set()); }} />
            </span>
            <span>Severity</span><span>Monitor</span><span>Group</span><span>Time</span><span>Alert Message</span><span>Actions</span>
          </div>
          {selectedAlerts.size > 0 && (
            <div style={{ background:"#E0F2F7", borderBottom:"1px solid #006D8C", padding:"7px 16px", display:"flex", alignItems:"center", gap:8, flexShrink:0 }}>
              <span style={{ fontSize:11, color:"#1A1A1A", fontWeight:500 }}>{selectedAlerts.size} alert(s) selected</span>
              <button onClick={() => setSelectedAlerts(new Set())}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#fff", color:"#475569", cursor:"pointer" }}>Clear</button>
              {canAck && <button onClick={() => bulkAct("acknowledge", [...selectedAlerts])}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #e2e5ea", borderRadius:3, background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>ACK selected</button>}
              {canAck && <button onClick={() => bulkAct("resolve", [...selectedAlerts])}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #bbf7d0", borderRadius:3, background:"#dcfce7", color:"#166534", cursor:"pointer" }}>Resolve selected</button>}
              <div style={{ flex:1 }} />
              <button onClick={() => bulkAct("resolve", filteredAlerts.map(a => a.AlertID))}
                style={{ fontSize:11, padding:"2px 8px", border:"0.5px solid #C0D8E8", borderRadius:3, background:"#fff", color:"#006D8C", cursor:"pointer" }}>
                Resolve all filtered ({filteredAlerts.length})
              </button>
            </div>
          )}
          <div style={{ flex:1, overflowY:"auto", overflowX:"hidden" }}>
            <ErrBox msg={err} />
            {loading ? <Spinner /> : filteredAlerts.length === 0 ? (
              <div style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No alerts found.</div>
            ) : alertBlocks}
          </div>
        </div>

        {/* Right panel — diagnosis */}
        {diagPanel}

      </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 35s 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()} \u00b7 {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>
  );
}

// ═══ LOGS ═══
function LogsView({ api, machines, currentMachine, onNavigate, monitoringControl }) {
  const [logs, setLogs]                 = useState([]);
  const [loading, setLoading]           = useState(true);
  const [err, setErr]                   = useState("");
  const [filter, setFilter]             = useState({ severity:"all", search:"", machine:"" });
  const [logSort, setLogSort]           = useState({ col:'time', dir:'desc' });
  const [hoverCol, setHoverCol]         = useState(null);
  const [hoverRow, setHoverRow]         = useState(null);
  const [refreshInterval, setRefreshInterval] = useState(() => parseInt(localStorage.getItem('op1_logs_refresh_interval') || '30'));
  const [countdown, setCountdown]       = useState(() => parseInt(localStorage.getItem('op1_logs_refresh_interval') || '30'));
  const [refreshing, setRefreshing]     = useState(false);
  const [flashUpdated, setFlashUpdated] = useState(false);
  const [recentAlerts, setRecentAlerts] = useState([]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const fetchLogs = useCallback((signal) => {
    setRefreshing(true);
    const q = new URLSearchParams({top:"200"});
    if (filter.machine) q.set("sourceMachine", filter.machine);
    api("GET", `/logs/ping?${q}`, undefined, signal)
      .then(r => {
        setLogs(Array.isArray(r) ? r : []);
        setErr("");
        setLoading(false);
        setRefreshing(false);
        setFlashUpdated(true);
        setTimeout(() => setFlashUpdated(false), 1000);
      })
      .catch(e => {
        if (e.name === "AbortError") return;
        setRefreshing(false);
        setLoading(false);
        setErr(e.message);
      });
  }, [api, filter.machine]);

  const handleIntervalChange = (e) => {
    const v = parseInt(e.target.value, 10);
    localStorage.setItem('op1_logs_refresh_interval', String(v));
    setRefreshInterval(v);
    setCountdown(v);
  };

  useEffect(() => {
    const ctrl = new AbortController();
    fetchLogs(ctrl.signal);
    const pollId = setInterval(() => { setCountdown(refreshInterval); fetchLogs(ctrl.signal); }, refreshInterval * 1000);
    const onVisible = () => { if (document.visibilityState === "visible") { setCountdown(refreshInterval); fetchLogs(ctrl.signal); } };
    document.addEventListener("visibilitychange", onVisible);
    return () => { ctrl.abort(); clearInterval(pollId); document.removeEventListener("visibilitychange", onVisible); };
  }, [fetchLogs, refreshInterval]);

  useEffect(() => {
    const id = setInterval(() => setCountdown(n => n > 0 ? n - 1 : 0), 1000);
    return () => clearInterval(id);
  }, []);

  const filtered = logs.filter(l=>{
    const sev = l.Severity||(l.IsSuccess?"info":"critical");
    if (filter.severity!=="all" && sev!==filter.severity) return false;
    if (filter.search && !(l.MonitorName||"").toLowerCase().includes(filter.search.toLowerCase()) && !(l.ErrorMessage||"").toLowerCase().includes(filter.search.toLowerCase())) return false;
    return true;
  });

  const handleSortClick = col => setLogSort(prev =>
    prev.col === col ? { col, dir: prev.dir==='asc'?'desc':'asc' } : { col, dir: col==='time'?'desc':'asc' }
  );

  const SUBTYPE_LABELS = {
    'infra-ping':'Ping / ICMP','infra-tcp':'TCP Port Check','infra-svc':'Service / Daemon',
    'infra-cpu':'CPU Utilization','infra-mem':'Memory Utilization','infra-disk':'Disk Space',
    'infra-diskio':'Disk I/O','infra-net':'Network Interface','infra-proc':'Process Monitor',
    'infra-log':'System Log','infra-hw':'Hardware Health',
    'app-svc':'Service Availability','app-api':'API Endpoint','app-port':'Port Check',
    'app-jvm':'JVM / .NET Runtime','app-thread':'Thread / Worker Pool',
    'app-mq':'Message Queue','app-cache':'Cache Monitor',
    'app-kpi':'Business KPI Counter','app-log':'Application Log','app-perf':'Performance Counter',
    'db-conn':'Connectivity','db-sess':'Session Count','db-block':'Blocking / Locking',
    'db-wait':'Wait Events','db-longq':'Long-Running Queries','db-perf':'DB Performance Counter',
    'db-space':'Tablespace / Storage','db-txlog':'Transaction Log',
    'db-query':'Query / Row Count','db-sqlctr':'SQL Counter',
    'web-http':'HTTP / HTTPS','web-ssl':'SSL Certificate','web-dns':'DNS Resolution',
    'web-redir':'Redirect / URL Path','web-cm':'Content Match','web-perf':'Page Load Time',
    'web-hdr':'HTTP Headers','web-api':'API Response Validation',
    'syn-login':'Login Flow','syn-multi':'Multi-Step Web',
    'syn-pay':'Payment / Checkout','syn-app':'Application Workflow',
    'syn-apiwf':'API Workflow','syn-sql':'SQL Query Check',
    'syn-ftp':'File Transfer','syn-email':'Email Flow'
  };
  const SEV_WEIGHT = { healthy:0, warning:1, critical:2, failed:3 };
  const sortedFiltered = [...filtered].sort((a,b)=>{
    const { col, dir } = logSort;
    let av, bv;
    if      (col==='time')     { av=a.CheckTimestamp||"";     bv=b.CheckTimestamp||""; }
    else if (col==='status')   { av=SEV_WEIGHT[a.Severity||"healthy"]??0; bv=SEV_WEIGHT[b.Severity||"healthy"]??0; }
    else if (col==='type')     { const ct=x=>(x.MonitorType||"")+(x.MonitorSubType?" \u00b7 "+x.MonitorSubType:""); av=ct(a).toLowerCase(); bv=ct(b).toLowerCase(); }
    else if (col==='group')    { av=a.GroupName||"\uffff";    bv=b.GroupName||"\uffff"; }
    else if (col==='name')     { av=(a.MonitorName||"").toLowerCase(); bv=(b.MonitorName||"").toLowerCase(); }
    else if (col==='source')   { av=(a.SourceMachine||"").toLowerCase(); bv=(b.SourceMachine||"").toLowerCase(); }
    else if (col==='target')   { av=a.Target||"\uffff";       bv=b.Target||"\uffff"; }
    else if (col==='response') { av=a.ResponseTimeMs??999999; bv=b.ResponseTimeMs??999999; }
    if (av < bv) return dir==='asc'?-1:1;
    if (av > bv) return dir==='asc'?1:-1;
    return 0;
  });

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Shown",    String(filtered.length), "#1e293b"],
            ["Severity", filter.severity==="all" ? "All" : filter.severity, "#1e293b"],
            ["Source",   filter.machine ? (machines?.find(m=>m.MachineName===filter.machine)?.MachineAlias||filter.machine) : "All", "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:10 }}>
        <select value={filter.severity} onChange={e=>setFilter(p=>({...p,severity: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 Severities</option>
          <option value="critical">Critical</option>
          <option value="warning">Warning</option>
          <option value="info">Info</option>
        </select>
        <div style={{ position:"relative", display:"inline-flex", alignItems:"center" }}>
          <span style={{ position:"absolute", left:7, fontSize:10, color:"#9CA3AF", pointerEvents:"none" }}>&#x1F50D;</span>
          <input placeholder="Search monitor or message…" value={filter.search} onChange={e=>setFilter(p=>({...p,search:e.target.value}))}
            style={{ height:24, width:200, paddingLeft:24, paddingRight:6, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, outline:"none", color:"#374151", background:"#fff", fontFamily:"inherit" }} />
        </div>
        {machines && machines.length >= 1 && (
          <select value={filter.machine} onChange={e=>setFilter(p=>({...p,machine: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 Machines</option>
            {machines.map(m=><option key={m.MachineName} value={m.MachineName}>{m.MachineAlias||m.MachineName}{m.MachineName===currentMachine?" (this)":""}</option>)}
          </select>
        )}
        <div style={{ marginLeft:"auto", display:"flex", alignItems:"center", gap:5, flexShrink:0 }}>
          <select value={refreshInterval} onChange={handleIntervalChange}
            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={10}>10s</option>
            <option value={30}>30s</option>
            <option value={60}>1m</option>
            <option value={300}>5m</option>
          </select>
          <button title="Refresh now" onClick={() => { setCountdown(refreshInterval); fetchLogs(); }}
            style={{ background:"none", border:"none", cursor:"pointer", padding:0, lineHeight:1, fontSize:14, color:"#64748b",
              opacity:refreshing?1:0.5, animation:refreshing?"spin 1s linear infinite":"none" }}>&#x27F3;</button>
          <span style={{ fontSize:10, color:flashUpdated?"#008C6F":refreshing?"#006D8C":"#4A4A4A" }}>
            {flashUpdated ? "\u2713" : refreshing ? "\u2026" : `${countdown}s`}
          </span>
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", paddingBottom:28 }}>
        <div style={{ display:"grid", gridTemplateColumns:"80px 62px 220px 220px 1fr 220px 220px 88px", padding:"6px 14px", position:"sticky", top:0, zIndex:1, background:"#FFFFFF", borderBottom:"1px solid #D8CCBA" }}>
          <span onClick={()=>handleSortClick('time')} onMouseEnter={()=>setHoverCol('time')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='time'?"#F5F0E8":"transparent" }}>Time{logSort.col==='time'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('status')} onMouseEnter={()=>setHoverCol('status')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='status'?"#F5F0E8":"transparent" }}>Status{logSort.col==='status'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('type')} onMouseEnter={()=>setHoverCol('type')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='type'?"#F5F0E8":"transparent" }}>Type{logSort.col==='type'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('group')} onMouseEnter={()=>setHoverCol('group')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='group'?"#F5F0E8":"transparent" }}>Group{logSort.col==='group'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('name')} onMouseEnter={()=>setHoverCol('name')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", background:hoverCol==='name'?"#F5F0E8":"transparent" }}>Name{logSort.col==='name'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('source')} onMouseEnter={()=>setHoverCol('source')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", paddingRight:8, textAlign:"left", background:hoverCol==='source'?"#F5F0E8":"transparent" }}>Source{logSort.col==='source'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('target')} onMouseEnter={()=>setHoverCol('target')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", textAlign:"left", background:hoverCol==='target'?"#F5F0E8":"transparent" }}>Target{logSort.col==='target'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
          <span onClick={()=>handleSortClick('response')} onMouseEnter={()=>setHoverCol('response')} onMouseLeave={()=>setHoverCol(null)} style={{ fontSize:10, fontWeight:600, textTransform:"uppercase", color:"#4A4A4A", letterSpacing:"0.04em", cursor:"pointer", userSelect:"none", textAlign:"right", background:hoverCol==='response'?"#F5F0E8":"transparent" }}>Response{logSort.col==='response'&&<span style={{opacity:0.6,marginLeft:2}}>{logSort.dir==='asc'?'↑':'↓'}</span>}</span>
        </div>
        <ErrBox msg={err} />
        {loading ? <Spinner /> : (
          <div>
            {sortedFiltered.length===0 && <div style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No log entries.</div>}
            {sortedFiltered.map((l,i)=>{
              const sev      = l.Severity||(l.IsSuccess?"healthy":"critical");
              const sevBg    = sev==="critical"||sev==="failed"?"#fca5a5":sev==="warning"?"#fcd34d":"#f1f5f9";
              const sevCol   = sev==="critical"||sev==="failed"?"#991b1b":sev==="warning"?"#92400e":"#475569";
              const sevLabel = sev==="critical"||sev==="failed"?"CRIT":sev==="warning"?"WARN":"OK";
              const leftCol  = sev==="warning"?"#E89A2E":sev==="critical"||sev==="failed"?"#D95C5C":"#008C6F";
              const rowBg    = hoverRow===i ? "#F0F7FB" : i % 2 === 0 ? "#FFFFFF" : "#F5F0E8";
              const rtText   = l.ResponseTimeMs ? `${l.ResponseTimeMs}ms` : "\u2014";
              const srcLabel = l.SourceMachine ? (machines?.find(m=>m.MachineName===l.SourceMachine)?.MachineAlias||l.SourceMachine) : "";
              return (
                <div key={l.ResultID||i} onClick={()=>onNavigate&&onNavigate(l.MonitorID)} onMouseEnter={()=>setHoverRow(i)} onMouseLeave={()=>setHoverRow(null)} style={{ display:"grid", gridTemplateColumns:"80px 62px 220px 220px 1fr 220px 220px 88px", padding:"6px 14px", minHeight:34, alignItems:"center", borderLeft:`3px solid ${leftCol}`, background:rowBg, cursor:"pointer" }}>
                  <span style={{ fontFamily:"'DM Mono', monospace", fontSize:11, color:"#4A4A4A" }}>{l.CheckTimestamp?parseUTC(l.CheckTimestamp)?.toLocaleTimeString("en-US",{hour12:false}):"—"}</span>
                  <span style={{ fontSize:10, fontWeight:600, borderRadius:4, padding:"1px 6px", background:sevBg, color:sevCol, display:"inline-block", width:"fit-content" }}>{sevLabel}</span>
                  {(()=>{ const subLabel=SUBTYPE_LABELS[l.MonitorSubType]||l.MonitorSubType; const typeDisplay=subLabel?`${l.MonitorType} \u00b7 ${subLabel}`:(l.MonitorType||"\u2014"); return <span style={{ fontSize:11, color:"#1A1A1A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{typeDisplay}</span>; })()}
                  <span style={{ fontSize:11, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.GroupName||"\u2014"}</span>
                  <span style={{ fontSize:12, color:"#1A1A1A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.MonitorName}</span>
                  <span style={{ fontSize:11, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", textAlign:"left", paddingRight:8 }}>{srcLabel}</span>
                  <span style={{ fontSize:12, color:"#4A4A4A", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis", paddingRight:8 }}>{l.Target||""}</span>
                  <span style={{ fontFamily:"'DM Mono', monospace", fontSize:11, color:"#006D8C", fontWeight:600, textAlign:"right", paddingRight:8 }}>{rtText}</span>
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* ── News ticker ── */}
      <div style={{ position:"fixed", bottom:0, left:220, right:0, height:28, background:"#1e2b3c", borderTop:"0.5px solid rgba(255,255,255,0.07)", overflow:"hidden", display:"flex", alignItems:"center", zIndex:100 }}>
        <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 35s 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()} \u00b7 {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>
  );
}

// ═══ REPORTS ═══
function ReportsView({ api, canGenerate, onNavigate, monitoringControl }) {
  const REPORT_TYPES = ["Availability & Uptime","Performance Summary","Alert History","Incident Report","Monitor Inventory"];
  const DATE_OPTIONS = [{ label:"Last 7 days", days:7 }, { label:"Last 30 days", days:30 }, { label:"Last 90 days", days:90 }];
  const ALERT_TABS   = ["All","Critical","Warning","Resolved"];

  const [reportType,   setReportType]   = useState(REPORT_TYPES[0]);
  const [filterGroup,  setFilterGroup]  = useState("");
  const [filterType,   setFilterType]   = useState("");
  const [filterDays,   setFilterDays]   = useState(7);
  const [filterMonitor,setFilterMonitor]= useState(0);

  const [filters,      setFilters]      = useState(null);
  const [summary,      setSummary]      = useState(null);
  const [uptimeRows,   setUptimeRows]   = useState([]);
  const [trendData,    setTrendData]    = useState([]);
  const [alertData,    setAlertData]    = useState([]);
  const [alertTab,     setAlertTab]     = useState("All");
  const [loading,      setLoading]      = useState(false);
  const [loadErr,      setLoadErr]      = useState("");
  const [recentAlerts, setRecentAlerts] = useState([]);

  const donutRef = useRef(null);
  const barRef   = useRef(null);
  const donutChart = useRef(null);
  const barChart   = useRef(null);

  // ── Build filter query string ──
  const buildQS = (days, gId, mId, mType) => {
    const p = [`days=${days||7}`];
    if (gId)    p.push(`groupId=${gId}`);
    if (mId>0)  p.push(`monitorId=${mId}`);
    if (mType)  p.push(`monitorType=${encodeURIComponent(mType)}`);
    return "?" + p.join("&");
  };

  // ── Fetch all report data ──
  const fetchData = useCallback(async (days, gId, mId, mType) => {
    setLoading(true); setLoadErr("");
    const qs = buildQS(days, gId, mId, mType);
    try {
      const [s, u, t, a] = await Promise.all([
        api("GET", `/reports/summary${qs}`),
        api("GET", `/reports/uptime${qs}`),
        api("GET", `/reports/trend${qs}`),
        api("GET", `/reports/alerts${qs}`),
      ]);
      setSummary(s);
      setUptimeRows(Array.isArray(u) ? u : []);
      setTrendData(Array.isArray(t) ? t : []);
      setAlertData(Array.isArray(a) ? a : []);
    } catch(e) { setLoadErr("Failed to load report data — check connection."); }
    finally { setLoading(false); }
  }, [api]);

  // ── Initial load ──
  useEffect(() => {
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/reports/filters").then(r=>setFilters(r)).catch(()=>{});
    fetchData(7, "", 0, "");
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  }, []);

  // ── Chart.js donut ──
  useEffect(() => {
    if (!donutRef.current || !summary) return;
    const h = summary.monitorsHealthy || 0;
    const w = summary.monitorCount - h - (summary.monitorsDown||0) - (summary.monitorsSuppressed||0);
    const d = summary.monitorsDown || 0;
    const s = summary.monitorsSuppressed || 0;
    const total = summary.monitorCount || 1;
    const centerTextPlugin = {
      id: "op1CenterText",
      beforeDraw(chart) {
        const { width, height, ctx } = chart;
        ctx.restore();
        const fs = Math.min(height, width) * 0.18;
        ctx.font = `700 ${fs}px DM Sans, sans-serif`;
        ctx.fillStyle = "#1A1A1A";
        ctx.textBaseline = "middle";
        const text = String(total);
        const tx = (width - ctx.measureText(text).width) / 2;
        ctx.fillText(text, tx, height / 2 - 6);
        ctx.font = `400 ${fs*0.45}px DM Sans, sans-serif`;
        ctx.fillStyle = "#7A9AB8";
        const sub = "monitors";
        const sx = (width - ctx.measureText(sub).width) / 2;
        ctx.fillText(sub, sx, height / 2 + fs * 0.55);
        ctx.save();
      }
    };
    if (donutChart.current) { donutChart.current.destroy(); donutChart.current = null; }
    donutChart.current = new Chart(donutRef.current, {
      type: "doughnut",
      data: {
        labels: ["Healthy","Warning","Critical","Suppressed"],
        datasets: [{ data:[h, Math.max(w,0), d, s],
          backgroundColor:["#008C6F","#E89A2E","#D95C5C","#D8CCBA"],
          borderWidth:0, hoverOffset:4 }]
      },
      options: { responsive:false, cutout:"68%", plugins:{ legend:{ display:false }, tooltip:{ callbacks:{ label: ctx => ` ${ctx.label}: ${ctx.raw}` } } } },
      plugins: [centerTextPlugin]
    });
    return () => { if(donutChart.current){donutChart.current.destroy();donutChart.current=null;} };
  }, [summary]);

  // ── Chart.js bar ──
  useEffect(() => {
    if (!barRef.current) return;
    if (barChart.current) { barChart.current.destroy(); barChart.current = null; }
    if (!trendData || trendData.length === 0) return;
    const MONTHS = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
    const labels = trendData.map(d => {
      const dk = typeof d.date === "string" ? d.date.slice(0,10) : "";
      const dt = new Date(dk + "T00:00:00");
      return isNaN(dt) ? dk : `${MONTHS[dt.getMonth()]} ${dt.getDate()}`;
    });
    const values = trendData.map(d => d.avgResponseMs || 0);
    barChart.current = new Chart(barRef.current, {
      type: "bar",
      data: {
        labels,
        datasets: [{ label:"Avg Response (ms)", data:values,
          backgroundColor:"rgba(0,109,140,0.8)", borderRadius:4, borderSkipped:false }]
      },
      options: {
        responsive:true, maintainAspectRatio:false,
        plugins:{ legend:{ display:false }, tooltip:{ callbacks:{ label: ctx => ` ${ctx.raw} ms` } } },
        scales: {
          x: { grid:{ display:false }, ticks:{ color:"#7A9AB8", font:{ size:11 } } },
          y: { grid:{ color:"rgba(0,0,0,0.05)" }, ticks:{ color:"#7A9AB8", font:{ size:11 }, callback: v => `${v}ms` }, beginAtZero:true }
        }
      }
    });
    return () => { if(barChart.current){barChart.current.destroy();barChart.current=null;} };
  }, [trendData]);

  // ── Apply filters ──
  const doApply = () => fetchData(filterDays, filterGroup, filterMonitor, filterType);
  const doReset = () => {
    setFilterGroup(""); setFilterType(""); setFilterDays(7); setFilterMonitor(0);
    fetchData(7, "", 0, "");
  };

  // ── Date range display ──
  const now = new Date();
  const since = new Date(now.getTime() - filterDays * 86400000);
  const fmtDate = d => { const MONTHS=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; return `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`; };
  const dateRangeText = `${fmtDate(since)} – ${fmtDate(now)}`;

  // ── Monitor options for current group ──
  const monitorOptions = filters ? (
    filterGroup
      ? (filters.monitorsByGroup?.[filterGroup] || [])
      : Object.values(filters.monitorsByGroup || {}).flat()
  ) : [];

  // ── Alert table filtered by tab ──
  const filteredAlerts = alertData.filter(a => {
    if (alertTab === "All") return true;
    if (alertTab === "Critical") return (a.Severity||"").toLowerCase() === "critical";
    if (alertTab === "Warning")  return (a.Severity||"").toLowerCase() === "warning";
    if (alertTab === "Resolved") return !!a.ResolvedAt;
    return true;
  }).slice(0, 20);

  // ── Branding props ──
  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  // ── Uptime bar width ──
  const uptimePct = pct => Math.max(0, Math.min(100, pct || 0));
  const uptimeColor = pct => pct >= 99 ? "#008C6F" : pct >= 95 ? "#E89A2E" : "#D95C5C";

  // ── Alert duration ──
  const alertDuration = (a) => {
    if (!a.CreatedAt) return "—";
    const start = parseUTC(a.CreatedAt);
    if (!start) return "—";
    const end = a.ResolvedAt ? parseUTC(a.ResolvedAt) : new Date();
    if (!end) return "Ongoing";
    const diffMs = end - start;
    if (diffMs < 0) return "—";
    const mins = Math.floor(diffMs / 60000);
    if (mins < 60) return `${mins}m`;
    return `${Math.floor(mins/60)}h ${mins%60}m`;
  };

  // ── Monitor options by group for select ──
  const monitorOptGroups = filters ? Object.entries(filters.monitorsByGroup || {}) : [];

  // ── Saved report presets ──
  const applyPreset = (days) => {
    setFilterDays(days); setFilterGroup(""); setFilterType(""); setFilterMonitor(0);
    fetchData(days, "", 0, "");
  };

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:12 }}>
          <button onClick={()=>window.print()} style={{ padding:"6px 14px", borderRadius:6, border:"1px solid #006D8C", background:"#ffffff", color:"#006D8C", fontSize:12, fontWeight:600, cursor:"pointer" }}>Save Report</button>
          <button onClick={()=>window.print()} style={{ padding:"6px 14px", borderRadius:6, border:"none", background:"#006D8C", color:"#ffffff", fontSize:12, fontWeight:600, cursor:"pointer" }}>Export PDF</button>
        </div>
      </div>

      {/* ── Body: left panel + main content ── */}
      <div style={{ display:"flex", flex:1, overflow:"hidden" }}>

        {/* ── Left panel ── */}
        <div style={{ width:232, flexShrink:0, background:"#ffffff", borderRight:"1px solid #D8CCBA", display:"flex", flexDirection:"column", overflowY:"auto" }}>

          {/* Report Type */}
          <div style={{ padding:"14px 14px 10px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:7 }}>Report Type</div>
            <select value={reportType} onChange={e=>setReportType(e.target.value)}
              style={{ width:"100%", padding:"6px 8px", borderRadius:6, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff", cursor:"pointer" }}>
              {REPORT_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
            </select>
          </div>

          <div style={{ borderTop:"1px solid #F0EDE7", margin:"0 14px" }} />

          {/* Filters */}
          <div style={{ padding:"10px 14px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:9 }}>Filters</div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Monitor Group</div>
              <select value={filterGroup} onChange={e=>{ setFilterGroup(e.target.value); setFilterMonitor(0); }}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value="">All Groups</option>
                {(filters?.groups||[]).map(g=><option key={g} value={g}>{g||"Ungrouped"}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Monitor Type</div>
              <select value={filterType} onChange={e=>setFilterType(e.target.value)}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value="">All Types</option>
                {(filters?.monitorTypes||[]).map(t=><option key={t} value={t}>{t}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:8 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>Date Range</div>
              <select value={filterDays} onChange={e=>setFilterDays(parseInt(e.target.value))}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                {DATE_OPTIONS.map(o=><option key={o.days} value={o.days}>{o.label}</option>)}
              </select>
            </div>

            <div style={{ marginBottom:12 }}>
              <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:3 }}>
                {filterGroup ? `All in ${filterGroup||"Ungrouped"}` : "Specific Monitor"}
              </div>
              <select value={filterMonitor} onChange={e=>setFilterMonitor(parseInt(e.target.value)||0)}
                style={{ width:"100%", padding:"5px 7px", borderRadius:5, border:"1px solid #D8CCBA", fontSize:12, color:"#1A1A1A", background:"#fff" }}>
                <option value={0}>{filterGroup ? `All in ${filterGroup||"group"}` : "All Monitors"}</option>
                {filterGroup
                  ? (filters?.monitorsByGroup?.[filterGroup]||[]).map(m=><option key={m.id} value={m.id}>{m.name}</option>)
                  : monitorOptGroups.map(([grp, mons]) =>
                      <optgroup key={grp} label={grp||"Ungrouped"}>
                        {mons.map(m=><option key={m.id} value={m.id}>{m.name}</option>)}
                      </optgroup>
                    )
                }
              </select>
            </div>

            <div style={{ display:"flex", gap:6 }}>
              <button onClick={doApply} style={{ flex:1, padding:"7px 0", borderRadius:6, border:"none", background:"#006D8C", color:"#fff", fontSize:12, fontWeight:600, cursor:"pointer" }}>Apply</button>
              <button onClick={doReset} style={{ flex:1, padding:"7px 0", borderRadius:6, border:"1px solid #006D8C", background:"#fff", color:"#006D8C", fontSize:12, fontWeight:600, cursor:"pointer" }}>Reset</button>
            </div>
          </div>

          <div style={{ borderTop:"1px solid #F0EDE7", margin:"0 14px" }} />

          {/* Saved Reports */}
          <div style={{ padding:"10px 14px 16px" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:9 }}>Saved Reports</div>
            {[{label:"Weekly Ops Summary", days:7},{label:"Monthly SLA Report", days:30}].map(p =>
              <button key={p.label} onClick={()=>applyPreset(p.days)}
                style={{ width:"100%", display:"flex", alignItems:"center", gap:8, padding:"7px 8px", borderRadius:6, border:"none", background:"transparent", textAlign:"left", cursor:"pointer", fontSize:12, color:"#4A4A4A", marginBottom:2 }}
                onMouseEnter={e=>e.currentTarget.style.background="#F5F0E8"}
                onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
                <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                  <rect x="2" y="1" width="10" height="14" rx="1.5" stroke="#7A9AB8" strokeWidth="1.5"/>
                  <line x1="4.5" y1="5.5" x2="9.5" y2="5.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                  <line x1="4.5" y1="8.5" x2="9.5" y2="8.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                  <line x1="4.5" y1="11.5" x2="7.5" y2="11.5" stroke="#7A9AB8" strokeWidth="1.2"/>
                </svg>
                {p.label}
              </button>
            )}
          </div>
        </div>

        {/* ── Main content ── */}
        <div style={{ flex:1, overflowY:"auto", background:"#F5F0E8" }}>

          {/* Date range strip */}
          <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 16px", background:"#ffffff", borderBottom:"1px solid #D8CCBA", flexShrink:0 }}>
            <div style={{ display:"flex", gap:6 }}>
              {[{l:"Today",d:1},{l:"7 days",d:7},{l:"30 days",d:30},{l:"90 days",d:90}].map(o =>
                <button key={o.d} onClick={()=>{ setFilterDays(o.d); fetchData(o.d, filterGroup, filterMonitor, filterType); }}
                  style={{ padding:"4px 10px", borderRadius:14, border:`1px solid ${filterDays===o.d?"#006D8C":"#D8CCBA"}`, background:filterDays===o.d?"#006D8C":"#fff", color:filterDays===o.d?"#fff":"#4A4A4A", fontSize:11, fontWeight:600, cursor:"pointer" }}>
                  {o.l}
                </button>
              )}
            </div>
            <span style={{ fontSize:11, color:"#7A9AB8", fontWeight:500 }}>{dateRangeText}</span>
          </div>

          {loading && <div style={{ padding:"24px 16px", color:"#7A9AB8", fontSize:12, textAlign:"center" }}>Loading…</div>}
          {loadErr && <div style={{ padding:"16px", color:"#D95C5C", fontSize:12 }}>{loadErr}</div>}

          {!loading && !loadErr && (
            <div style={{ padding:"14px 16px" }}>

              {/* KPI row */}
              <div style={{ display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:12, marginBottom:14 }}>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Overall Uptime</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#008C6F" }}>{summary ? `${summary.uptimePct.toFixed(2)}%` : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>last {filterDays} days</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Avg Response Time</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#006D8C" }}>{summary ? `${summary.avgResponseMs} ms` : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.monitorCount||0} monitors</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Total Alerts</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#D95C5C" }}>{summary ? summary.totalAlerts : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.criticalAlerts||0} critical · {summary?.warningAlerts||0} warning</div>
                </div>
                <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px 16px" }}>
                  <div style={{ fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.07em", marginBottom:4 }}>Monitors Down</div>
                  <div style={{ fontSize:22, fontWeight:700, color:"#E89A2E" }}>{summary ? summary.monitorsDown : "—"}</div>
                  <div style={{ fontSize:10, color:"#7A9AB8", marginTop:2 }}>{summary?.monitorsHealthy||0} healthy · {summary?.monitorsSuppressed||0} suppressed</div>
                </div>
              </div>

              {/* Uptime by Monitor — full width, grouped */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", overflow:"hidden", marginBottom:14 }}>
                {/* Section header with inline compact donut */}
                <div style={{ padding:"11px 14px", borderBottom:"1px solid #F0EDE7", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <span style={{ fontSize:12, fontWeight:600, color:"#1A1A1A" }}>Uptime by Monitor</span>
                  <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                    <canvas ref={donutRef} width={60} height={60} style={{ flexShrink:0 }} />
                    <div style={{ display:"flex", flexDirection:"column", gap:3 }}>
                      {[["Healthy","#008C6F",summary?.monitorsHealthy||0],["Warning","#E89A2E",Math.max(0,(summary?.monitorCount||0)-(summary?.monitorsHealthy||0)-(summary?.monitorsDown||0)-(summary?.monitorsSuppressed||0))],["Critical","#D95C5C",summary?.monitorsDown||0]].map(([lbl,col,val])=>(
                        <span key={lbl} style={{ display:"inline-flex", alignItems:"center", gap:5, fontSize:10, color:"#4A4A4A" }}>
                          <span style={{ width:7, height:7, borderRadius:"50%", background:col, display:"inline-block", flexShrink:0 }} />
                          {lbl} <strong style={{ color:"#1A1A1A" }}>{val}</strong>
                        </span>
                      ))}
                    </div>
                  </div>
                </div>
                {/* Grouped monitor rows */}
                {uptimeRows.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No check history for this period.</div>
                  : (() => {
                      const groups = {};
                      uptimeRows.forEach(r => {
                        const gn = r.groupName || "";
                        if (!groups[gn]) groups[gn] = [];
                        groups[gn].push(r);
                      });
                      const sortedKeys = Object.keys(groups).sort((a, b) => {
                        if (!a) return 1; if (!b) return -1;
                        return a.localeCompare(b);
                      });
                      const colHdr = (
                        <div style={{ display:"flex", alignItems:"center", padding:"6px 12px", borderBottom:"1px solid #D8CCBA", background:"#fff" }}>
                          <div style={{ flex:1, minWidth:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em" }}>Monitor</div>
                          <div style={{ width:120, flexShrink:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em" }}>Type</div>
                          <div style={{ width:80, flexShrink:0, fontSize:10, fontWeight:600, color:"#4A4A4A", textTransform:"uppercase", letterSpacing:"0.06em", textAlign:"right" }}>Uptime</div>
                          <div style={{ width:120, flexShrink:0, marginLeft:10 }} />
                        </div>
                      );
                      return sortedKeys.map((gn, gi) => {
                        const mons = groups[gn].slice().sort((a,b) => a.uptimePct - b.uptimePct);
                        const avgPct = mons.reduce((s,r) => s + r.uptimePct, 0) / mons.length;
                        const allHealthy = mons.every(r => r.uptimePct >= 100);
                        const groupLabel = gn || "Ungrouped";
                        return (
                          <div key={gn||"ungrouped"} style={{ marginTop: gi > 0 ? 12 : 0 }}>
                            {gi === 0 && colHdr}
                            <div style={{ background:"#F5F0E8", padding:"8px 12px", borderBottom:"1px solid #D8CCBA", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                              <span style={{ fontSize:12, fontWeight:700, color:"#1A1A1A" }}>{groupLabel}</span>
                              <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                                {allHealthy
                                  ? <span style={{ fontSize:11, color:"#008C6F", fontWeight:600 }}>✓ All healthy</span>
                                  : <span style={{ fontSize:12, fontWeight:700, color: avgPct>=99?"#008C6F":avgPct>=95?"#E89A2E":"#D95C5C" }}>{avgPct.toFixed(2)}%</span>
                                }
                                <span style={{ fontSize:11, color:"#7A9AB8" }}>{mons.length} monitor{mons.length!==1?"s":""}</span>
                              </div>
                            </div>
                            {mons.map((r,i) => (
                              <div key={r.monitorId} style={{ display:"flex", alignItems:"center", padding:"7px 12px", borderTop:"1px solid #F0EDE7", background: i%2===0?"#FFFFFF":"#F5F0E8", fontSize:12 }}>
                                <div onClick={()=>onNavigate&&onNavigate(r.monitorId)} style={{ flex:1, minWidth:0, color:"#006D8C", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", cursor:onNavigate?"pointer":"default" }}>{r.name}</div>
                                <div style={{ width:120, flexShrink:0 }}>
                                  <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#E0F2F7", color:"#006D8C" }}>{r.monitorType}</span>
                                </div>
                                <div style={{ width:80, flexShrink:0, textAlign:"right", color: r.uptimePct>=99?"#008C6F":r.uptimePct>=95?"#E89A2E":"#D95C5C", fontWeight:600 }}>{r.uptimePct.toFixed(2)}%</div>
                                <div style={{ width:120, flexShrink:0, marginLeft:10 }}>
                                  <div style={{ height:6, borderRadius:3, background:"#F0EDE7", overflow:"hidden" }}>
                                    <div style={{ height:"100%", width:`${uptimePct(r.uptimePct)}%`, background:uptimeColor(r.uptimePct), borderRadius:3 }} />
                                  </div>
                                </div>
                              </div>
                            ))}
                          </div>
                        );
                      });
                    })()
                }
              </div>

              {/* Response trend bar chart */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", padding:"14px", marginBottom:14 }}>
                <div style={{ fontSize:12, fontWeight:600, color:"#1A1A1A", marginBottom:12 }}>Response Time Trend</div>
                {trendData.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No response time data for this period.</div>
                  : <div style={{ height:200, position:"relative" }}><canvas ref={barRef} /></div>
                }
              </div>

              {/* Alert history */}
              <div style={{ background:"#fff", borderRadius:8, border:"1px solid #D8CCBA", overflow:"hidden" }}>
                <div style={{ padding:"11px 14px", borderBottom:"1px solid #F0EDE7", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <span style={{ fontSize:12, fontWeight:600, color:"#1A1A1A" }}>Alert History</span>
                  <div style={{ display:"flex", gap:4 }}>
                    {ALERT_TABS.map(t=>(
                      <button key={t} onClick={()=>setAlertTab(t)}
                        style={{ padding:"3px 10px", borderRadius:12, border:`1px solid ${alertTab===t?"#006D8C":"#D8CCBA"}`, background:alertTab===t?"#006D8C":"#fff", color:alertTab===t?"#fff":"#4A4A4A", fontSize:11, fontWeight:500, cursor:"pointer" }}>
                        {t}
                      </button>
                    ))}
                  </div>
                </div>
                {filteredAlerts.length === 0
                  ? <div style={{ padding:"24px", textAlign:"center", color:"#7A9AB8", fontSize:12 }}>No alerts in this period.</div>
                  : (
                    <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                      <thead>
                        <tr style={{ background:"#F5F0E8" }}>
                          {["Time","Monitor","Severity","Message","Duration"].map(h=>(
                            <th key={h} style={{ padding:"7px 12px", textAlign:"left", fontSize:9, fontWeight:700, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{h}</th>
                          ))}
                        </tr>
                      </thead>
                      <tbody>
                        {filteredAlerts.map((a,i) => {
                          const sev = (a.Severity||"").toLowerCase();
                          const sevColors = { critical:{ bg:"#FAEAEA", color:"#D95C5C" }, warning:{ bg:"#FDF3E0", color:"#E89A2E" }, info:{ bg:"#F5F0E8", color:"#4A4A4A" } };
                          const sc = sevColors[sev] || { bg:"#F0EDE7", color:"#4A4A4A" };
                          return (
                            <tr key={a.AlertID||i} style={{ borderTop:"1px solid #F0EDE7", background: i%2===0?"#fff":"#FAFAFA" }}>
                              <td style={{ padding:"7px 12px", color:"#7A9AB8", fontSize:11, whiteSpace:"nowrap" }}>{a.CreatedAt ? parseUTC(a.CreatedAt)?.toLocaleString([],{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})||"—" : "—"}</td>
                              <td style={{ padding:"7px 12px", maxWidth:160, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}><span onClick={()=>a.MonitorID&&onNavigate&&onNavigate(a.MonitorID)} style={{ color:"#006D8C", cursor:a.MonitorID&&onNavigate?"pointer":"default" }}>{a.MonitorName||"—"}</span></td>
                              <td style={{ padding:"7px 12px" }}>
                                <span style={{ fontSize:10, borderRadius:4, padding:"2px 7px", background:sc.bg, color:sc.color, fontWeight:600 }}>{a.Severity||"—"}</span>
                              </td>
                              <td style={{ padding:"7px 12px", color:"#4A4A4A", maxWidth:220, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{a.Message||"—"}</td>
                              <td style={{ padding:"7px 12px", color:"#7A9AB8", fontSize:11, whiteSpace:"nowrap" }}>{a.ResolvedAt ? alertDuration(a) : <span style={{ color:"#E89A2E", fontWeight:600 }}>Ongoing</span>}</td>
                            </tr>
                          );
                        })}
                      </tbody>
                    </table>
                  )
                }
              </div>

            </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 35s 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()} \u00b7 {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>
  );
}

// ═══ CONFIG ═══
const MONITOR_DEFAULT_SECTIONS = [
  { label:"General", keys:["default_check_frequency_seconds","default_failure_threshold","default_timeout_ms","default_response_time_warning_ms","default_response_time_critical_ms","default_ssl_expiry_warning_days","default_anomaly_baseline_days"] },
  { label:"Thresholds — Infrastructure", keys:["default_cpu_warning_percent","default_cpu_critical_percent","default_memory_warning_percent","default_memory_critical_percent","default_disk_warning_percent","default_disk_critical_percent"] },
  { label:"Thresholds — Database", keys:["default_db_query_latency_warning_ms","default_db_query_latency_critical_ms","default_db_connection_pool_warning_pct"] },
  { label:"Thresholds — Website", keys:["default_page_load_warning_ms","default_page_load_critical_ms"] },
  { label:"Scheduler & Retention", keys:["default_escalation_repeat_minutes","default_retention_hot_days","default_scheduler_refresh_seconds","default_max_concurrent_checks"] },
];
const MONITOR_DEFAULT_LABELS = {
  default_check_frequency_seconds:"Check Frequency (seconds)", default_failure_threshold:"Failure Threshold",
  default_timeout_ms:"Timeout (ms)", default_response_time_warning_ms:"Response Time Warning (ms)",
  default_response_time_critical_ms:"Response Time Critical (ms)", default_ssl_expiry_warning_days:"SSL Expiry Warning (days)",
  default_anomaly_baseline_days:"Anomaly Baseline Days", default_cpu_warning_percent:"CPU Warning (%)",
  default_cpu_critical_percent:"CPU Critical (%)", default_memory_warning_percent:"Memory Warning (%)",
  default_memory_critical_percent:"Memory Critical (%)", default_disk_warning_percent:"Disk Warning (%)",
  default_disk_critical_percent:"Disk Critical (%)", default_db_query_latency_warning_ms:"DB Query Latency Warning (ms)",
  default_db_query_latency_critical_ms:"DB Query Latency Critical (ms)", default_db_connection_pool_warning_pct:"DB Pool Warning (%)",
  default_page_load_warning_ms:"Page Load Warning (ms)", default_page_load_critical_ms:"Page Load Critical (ms)",
  default_escalation_repeat_minutes:"Escalation Repeat (minutes)", default_retention_hot_days:"Hot Retention (days)",
  default_scheduler_refresh_seconds:"Scheduler Refresh (seconds)", default_max_concurrent_checks:"Max Concurrent Checks",
};
const MONITOR_DEFAULT_DESC = {
  default_check_frequency_seconds:"Default check frequency (seconds)",
  default_failure_threshold:"Consecutive failures before alert",
  default_timeout_ms:"Check timeout (ms)",
  default_response_time_warning_ms:"Response time warning threshold (ms)",
  default_response_time_critical_ms:"Response time critical threshold (ms)",
  default_ssl_expiry_warning_days:"SSL cert expiry warning (days)",
  default_anomaly_baseline_days:"Anomaly detection baseline window (days)",
  default_cpu_warning_percent:"CPU warning threshold (%)",
  default_cpu_critical_percent:"CPU critical threshold (%)",
  default_memory_warning_percent:"Memory warning threshold (%)",
  default_memory_critical_percent:"Memory critical threshold (%)",
  default_disk_warning_percent:"Disk warning threshold (%)",
  default_disk_critical_percent:"Disk critical threshold (%)",
  default_db_query_latency_warning_ms:"DB query latency warning (ms)",
  default_db_query_latency_critical_ms:"DB query latency critical (ms)",
  default_db_connection_pool_warning_pct:"DB connection pool warning (%)",
  default_page_load_warning_ms:"Page load warning (ms)",
  default_page_load_critical_ms:"Page load critical (ms)",
  default_escalation_repeat_minutes:"Escalation repeat interval (minutes)",
  default_retention_hot_days:"Hot tier retention (days)",
  default_scheduler_refresh_seconds:"Scheduler monitor refresh interval (seconds)",
  default_max_concurrent_checks:"Max concurrent monitor checks",
};

function CredentialsTab({ api }) {
  const CRED_TYPES = ["Windows","Database","SNMP","SSH","APIKey","Generic"];
  const TYPE_COLORS = {
    Windows:  { bg:"#E0F2F7", color:"#006D8C" },
    Database: { bg:"#DCF2EA", color:"#008C6F" },
    SNMP:     { bg:"#FDF3E0", color:"#E89A2E" },
    SSH:      { bg:"#F0F7FB", color:"#4A4A4A" },
    APIKey:   { bg:"#FAEAEA", color:"#D95C5C" },
    Generic:  { bg:"#F5F0E8", color:"#6B6B6B" },
  };
  const BLANK = { Name:"", CredentialType:"Windows", Username:"", Password:"", Notes:"" };

  const [creds, setCreds]       = useState([]);
  const [loading, setLoading]   = useState(true);
  const [err, setErr]           = useState("");
  const [ok, setOk]             = useState("");
  const [form, setForm]         = useState(BLANK);
  const [editId, setEditId]     = useState(null);
  const [showPass, setShowPass] = useState(false);
  const [busy, setBusy]         = useState(false);

  const load = useCallback(() => {
    setLoading(true);
    api("GET", "/credentials")
      .then(r => { setCreds(Array.isArray(r) ? r : []); setErr(""); })
      .catch(e => setErr(e.message))
      .finally(() => setLoading(false));
  }, [api]);

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

  const resetForm = () => { setForm(BLANK); setEditId(null); setShowPass(false); };

  const startEdit = (c) => {
    setForm({ Name: c.Name, CredentialType: c.CredentialType, Username: c.Username, Password: "", Notes: c.Notes || "" });
    setEditId(c.CredentialID);
    setShowPass(false);
  };

  const save = async () => {
    setErr(""); setOk(""); setBusy(true);
    try {
      if (editId) {
        await api("PUT", `/credentials/${editId}`, form);
        setOk("Credential updated.");
      } else {
        await api("POST", "/credentials", form);
        setOk("Credential created.");
      }
      resetForm(); load();
    } catch(e) { setErr(e.message); }
    finally { setBusy(false); }
  };

  const del = async (c) => {
    if (!window.confirm(`Delete credential "${c.Name}"?`)) return;
    setErr(""); setOk("");
    try {
      await api("DELETE", `/credentials/${c.CredentialID}`);
      setOk("Credential deleted.");
      load();
    } catch(e) { setErr(e.message); }
  };

  const f = (k, v) => setForm(p => ({ ...p, [k]: v }));

  const THstyle = { padding:"7px 12px", textAlign:"left", fontSize:11, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.05em", position:"sticky", top:0, zIndex:1, background:"#D8CCBA" };
  const TDstyle = { padding:"8px 12px", fontSize:12, color:"#1A1A1A", borderBottom:"0.5px solid #f1f5f9" };

  return (
    <div>
      <ErrBox msg={err} /><OkBox msg={ok} />
      <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, overflow:"hidden", marginBottom:16 }}>
        <div style={{ padding:"10px 14px", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", display:"flex", alignItems:"center", gap:12 }}>
          <span style={{ fontSize:11, fontWeight:700, color:"#1A1A1A" }}>Credential Vault</span>
          <span style={{ fontSize:11, color:"#94a3b8" }}>{creds.length} stored</span>
        </div>
        {loading ? <Spinner /> : creds.length === 0 ? (
          <div style={{ padding:32, textAlign:"center", color:"#94a3b8", fontSize:12 }}>No credentials stored. Add one below.</div>
        ) : (
          <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
            <thead><tr style={{ background:"#D8CCBA", borderBottom:"1px solid #C0C0B0" }}>
              {["Name","Type","Username","Notes","Monitors","Actions"].map(h =>
                <th key={h} style={THstyle}>{h}</th>
              )}
            </tr></thead>
            <tbody>
              {creds.map(c => {
                const tc = TYPE_COLORS[c.CredentialType] || TYPE_COLORS.Generic;
                const isEditing = editId === c.CredentialID;
                return (
                  <tr key={c.CredentialID} style={{ background: isEditing ? "#F0F7FB" : "#fff" }}>
                    <td style={TDstyle}><span style={{ fontWeight:600 }}>{c.Name}</span></td>
                    <td style={TDstyle}>
                      <span style={{ fontSize:10, fontWeight:600, padding:"2px 7px", borderRadius:10, background:tc.bg, color:tc.color }}>{c.CredentialType}</span>
                    </td>
                    <td style={{ ...TDstyle, fontFamily:"monospace" }}>{c.Username}</td>
                    <td style={{ ...TDstyle, color:"#64748b" }}>{c.Notes || "\u2014"}</td>
                    <td style={{ ...TDstyle, textAlign:"center" }}>{c.MonitorCount}</td>
                    <td style={TDstyle}>
                      <div style={{ display:"flex", gap:4 }}>
                        <button onClick={() => startEdit(c)}
                          style={{ fontSize:10, padding:"2px 8px", border:"1px solid #006D8C", borderRadius:3, background:"#E0F2F7", color:"#006D8C", cursor:"pointer" }}>Edit</button>
                        <button onClick={() => del(c)} disabled={c.MonitorCount > 0}
                          title={c.MonitorCount > 0 ? `Used by ${c.MonitorCount} monitor(s)` : "Delete"}
                          style={{ fontSize:10, padding:"2px 8px", border:"1px solid #D95C5C", borderRadius:3, background: c.MonitorCount > 0 ? "#f1f5f9" : "#FAEAEA", color: c.MonitorCount > 0 ? "#94a3b8" : "#D95C5C", cursor: c.MonitorCount > 0 ? "not-allowed" : "pointer" }}>Delete</button>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </div>

      <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
        <div style={{ fontSize:11, fontWeight:700, color:"#1A1A1A", marginBottom:12, textTransform:"uppercase", letterSpacing:"0.05em" }}>
          {editId ? "Edit Credential" : "Add Credential"}
        </div>
        <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:12, marginBottom:12 }}>
          <div>
            <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Name *</label>
            <input value={form.Name} onChange={e=>f("Name",e.target.value)} placeholder="e.g. DC01 Admin"
              style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none" }} />
          </div>
          <div>
            <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Type</label>
            <select value={form.CredentialType} onChange={e=>f("CredentialType",e.target.value)}
              style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none", background:"#fff" }}>
              {CRED_TYPES.map(t=><option key={t} value={t}>{t}</option>)}
            </select>
          </div>
          <div>
            <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Username *</label>
            <input value={form.Username} onChange={e=>f("Username",e.target.value)} placeholder="domain\user or username"
              style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none" }} />
          </div>
          <div style={{ position:"relative" }}>
            <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>
              {editId ? "New Password (blank = keep)" : "Password *"}
            </label>
            <div style={{ position:"relative" }}>
              <input type={showPass?"text":"password"} value={form.Password} onChange={e=>f("Password",e.target.value)}
                placeholder={editId ? "Leave blank to keep existing" : "Enter password"}
                style={{ width:"100%", fontSize:12, padding:"6px 28px 6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none" }} />
              <button onClick={()=>setShowPass(p=>!p)} tabIndex={-1}
                style={{ position:"absolute", right:6, top:"50%", transform:"translateY(-50%)", background:"none", border:"none", cursor:"pointer", fontSize:12, color:"#94a3b8" }}>
                {showPass?"\uD83D\uDE48":"\uD83D\uDC41"}
              </button>
            </div>
          </div>
          <div style={{ gridColumn:"2 / span 2" }}>
            <label style={{ fontSize:10, fontWeight:600, color:"#64748b", textTransform:"uppercase", display:"block", marginBottom:4 }}>Notes</label>
            <input value={form.Notes} onChange={e=>f("Notes",e.target.value)} placeholder="Optional description"
              style={{ width:"100%", fontSize:12, padding:"6px 8px", border:"1px solid #e2e5ea", borderRadius:4, outline:"none" }} />
          </div>
        </div>
        <div style={{ display:"flex", gap:8 }}>
          <button onClick={save} disabled={busy}
            style={{ fontSize:12, padding:"6px 18px", border:"none", borderRadius:4, background:"#006D8C", color:"#fff", cursor: busy ? "not-allowed" : "pointer", fontWeight:600 }}>
            {busy ? "Saving\u2026" : editId ? "Update" : "Save"}
          </button>
          {editId && (
            <button onClick={resetForm}
              style={{ fontSize:12, padding:"6px 14px", border:"1px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#475569", cursor:"pointer" }}>
              Cancel
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

function ConfigView({ api, machines: machinesProp, currentMachine, onMachinesChange, monitoringControl }) {
  const [configTab, setConfigTab]           = useState("notifications");
  const [notifCfg, setNotifCfg]             = useState([]);
  const [retPolicies, setRetPolicies]       = useState([]);
  const [monDefaults, setMonDefaults]       = useState({});
  const [monDefaultsDirty, setMonDefaultsDirty] = useState({});
  const [err, setErr]                       = useState("");
  const [ok, setOk]                         = useState("");
  const [aiCfg, setAiCfg]                   = useState(null);
  const [aiCfgLoading, setAiCfgLoading]     = useState(false);
  const [aiCfgSaving, setAiCfgSaving]       = useState(false);
  const [aiCfgSaved, setAiCfgSaved]         = useState(false);
  const [recentAlerts, setRecentAlerts]     = useState([]);
  // ── Notifications tab state ──
  const [notifChs, setNotifChs]                       = useState([]);
  const [notifRecs, setNotifRecs]                     = useState([]);
  const [notifSettings, setNotifSettings]             = useState({});
  const [notifLoading, setNotifLoading]               = useState(false);
  const [notifChannelModal, setNotifChannelModal]     = useState(null);
  const [notifRecipientModal, setNotifRecipientModal] = useState(null);
  const [notifTestState, setNotifTestState]           = useState({});
  const [notifSaving, setNotifSaving]                 = useState(false);
  const [notifRulesDraft, setNotifRulesDraft]         = useState({});
  const [notifTestCh, setNotifTestCh]                 = useState('');
  const [notifTestRec, setNotifTestRec]               = useState('');
  const [notifTestResult, setNotifTestResult]         = useState(null);
  const [notifChannelDraft, setNotifChannelDraft]     = useState({});
  const [notifRecipientDraft, setNotifRecipientDraft] = useState({});

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/config/notifications").then(r=>setNotifCfg(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/retention").then(r=>setRetPolicies(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/config/monitor-defaults").then(r=>{ if(r && typeof r==="object") setMonDefaults(r); }).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  useEffect(()=>{
    if (configTab === "ai-diagnosis" && aiCfg === null) {
      setAiCfgLoading(true);
      api("GET","/config/ai-diagnosis").then(r=>{ if(r && typeof r==="object") setAiCfg(r); }).catch(()=>{}).finally(()=>setAiCfgLoading(false));
    }
  },[configTab, aiCfg, api]);

  useEffect(()=>{
    if (configTab !== "notifications") return;
    setNotifLoading(true);
    Promise.all([
      api("GET","/notifications/channels"),
      api("GET","/notifications/recipients"),
      api("GET","/notifications/settings"),
    ]).then(([chs,recs,settings])=>{
      setNotifChs(Array.isArray(chs)?chs:[]);
      setNotifRecs(Array.isArray(recs)?recs:[]);
      setNotifSettings(settings&&typeof settings==='object'?settings:{});
    }).catch(()=>{}).finally(()=>setNotifLoading(false));
  },[configTab,api]);

  useEffect(()=>{ setNotifRulesDraft(notifSettings); },[notifSettings]);

  const saveAiCfg = async () => {
    if (!aiCfg) return;
    setAiCfgSaving(true);
    try {
      await api("PUT","/config/ai-diagnosis", { enabled: aiCfg.enabled, minSeverity: aiCfg.minSeverity, cooldownMinutes: aiCfg.cooldownMinutes });
      setAiCfgSaved(true);
      setTimeout(()=>setAiCfgSaved(false), 2000);
    } catch(e){ setErr(e.message); }
    finally { setAiCfgSaving(false); }
  };

  const runPurge = async () => {
    setErr(""); setOk("");
    try { await api("POST","/retention/purge",{}); setOk("Purge job triggered."); } catch(e){setErr(e.message);}
  };

  const saveDefaults = async () => {
    setErr(""); setOk("");
    try {
      const payload = { ...monDefaults, ...monDefaultsDirty };
      await api("POST","/config/monitor-defaults/save", payload);
      setMonDefaults(payload); setMonDefaultsDirty({});
      setOk("Monitor defaults saved.");
    } catch(e){ setErr(e.message); }
  };

  const loadNotificationsTab = () => {
    setNotifLoading(true);
    Promise.all([
      api("GET","/notifications/channels"),
      api("GET","/notifications/recipients"),
      api("GET","/notifications/settings"),
    ]).then(([chs,recs,settings])=>{
      setNotifChs(Array.isArray(chs)?chs:[]);
      setNotifRecs(Array.isArray(recs)?recs:[]);
      setNotifSettings(settings&&typeof settings==='object'?settings:{});
    }).catch(()=>{}).finally(()=>setNotifLoading(false));
  };

  const saveNotifSetting = (key, value) => {
    api("POST","/notifications/settings",{key,value:String(value)})
      .then(()=>setNotifSettings(prev=>({...prev,[key]:String(value)})))
      .catch(()=>{});
  };

  const CONFIG_TABS = [
    { key:"notifications", label:"Notifications" },
    { key:"credentials",   label:"Credentials" },
    { key:"retention",     label:"Retention" },
    { key:"monitor-defaults", label:"Monitor Defaults" },
    { key:"machines",      label:"Source Monitors" },
    { key:"ai-diagnosis",  label:"AI Diagnosis" },
  ];

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};
  const notifChannels = [...new Set(notifCfg.map(n=>n.ConfigType).filter(Boolean))].length;

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Notif Configs",  String(notifCfg.length),  "#1e293b"],
            ["Ret Policies",   String(retPolicies.length), "#1e293b"],
            ["Channels",       String(notifChannels),     "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Tab bar ── */}
      <div style={{ display:"flex", gap:0, borderBottom:"0.5px solid #e2e5ea", background:"#ffffff", flexShrink:0 }}>
        {CONFIG_TABS.map(t => (
          <button key={t.key} onClick={()=>setConfigTab(t.key)}
            style={{ background:"transparent", border:"none", borderBottom: configTab===t.key ? "2px solid #006D8C" : "2px solid transparent", color: configTab===t.key ? "#006D8C" : "#64748b", padding:"10px 18px", fontSize:12, fontWeight: configTab===t.key ? 600 : 400, cursor:"pointer", whiteSpace:"nowrap" }}>
            {t.label}
          </button>
        ))}
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"16px" }}>
        <ErrBox msg={err} /><OkBox msg={ok} />
        {configTab==="notifications" && (()=>{
          const notifEnabled = notifSettings['Notifications_Enabled'] === 'true';
          const tog = (on, fn) => (
            <div onClick={fn} style={{ width:36, height:20, borderRadius:10, background:on?c.blue:"#e2e5ea", position:"relative", cursor:"pointer", transition:"background 0.2s", flexShrink:0 }}>
              <div style={{ position:"absolute", top:3, left:on?17:3, width:14, height:14, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
            </div>
          );
          const chIcon = (t) => t==='email' ? {icon:"@",bg:c.blueLight,fg:c.blue} : t==='webhook' ? {icon:"⇒",bg:c.yellowLight,fg:c.yellow} : {icon:"?",bg:"#f1f5f9",fg:c.textMuted};
          const chSummary = (ch) => { try { const cfg=JSON.parse(ch.ConfigJson||'{}'); if(ch.ChannelType==='email') return cfg.SmtpHost?`${cfg.SmtpHost}:${cfg.SmtpPort||587}`:'—'; if(ch.ChannelType==='webhook') return cfg.Url?(cfg.Url.length>42?cfg.Url.slice(0,40)+'…':cfg.Url):'—'; } catch{} return '—'; };
          const fmtTested = (dt) => { if(!dt) return '—'; const d=parseUTC(dt); if(!d) return '—'; const now=new Date(); const today=d.getDate()===now.getDate()&&d.getMonth()===now.getMonth()&&d.getFullYear()===now.getFullYear(); return today?'Today '+d.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}):d.toLocaleDateString([],{day:'2-digit',month:'short'})+' '+d.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); };
          const chStatus = (ch) => ch.LastTestSuccess===true ? <span style={{fontSize:11,color:c.green,fontWeight:600}}>● Connected</span> : ch.LastTestSuccess===false ? <span style={{fontSize:11,color:c.red,fontWeight:600}}>● Failed</span> : <span style={{fontSize:11,color:c.textMuted}}>● Not tested</span>;
          const inpS = { width:"100%", padding:"7px 10px", border:`1px solid ${c.border}`, borderRadius:6, fontSize:13, background:"#fff", color:c.text, outline:"none", boxSizing:"border-box" };
          const lblS = { fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.05em", marginBottom:4, display:"block" };
          const cardStyle = { background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden" };
          const tblHdr = ["Channel","Type","Status","Last tested",""].map(h=><th key={h} style={{padding:"8px 12px",textAlign:"left",fontSize:9,fontWeight:700,color:c.textMuted,textTransform:"uppercase",letterSpacing:"0.05em"}}>{h}</th>);

          const notifGlobalBanner = (
            <div style={{ background:notifEnabled?c.greenLight:c.redLight, border:`1px solid ${notifEnabled?c.green:c.red}`, borderRadius:8, padding:"14px 16px", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
              <div>
                <div style={{ fontSize:13, fontWeight:600, color:notifEnabled?c.green:c.red }}>{notifEnabled?"Notifications enabled globally":"Notifications disabled globally"}</div>
                <div style={{ fontSize:11, color:c.textMuted, marginTop:3 }}>{notifEnabled?"All channels and recipients are active. Disable to suppress all alert delivery system-wide.":"No notifications will be sent regardless of channel or recipient configuration."}</div>
              </div>
              {tog(notifEnabled,()=>saveNotifSetting('Notifications_Enabled',notifEnabled?'false':'true'))}
            </div>
          );

          const notifChannelsCard = (
            <div style={cardStyle}>
              <div style={{ padding:"12px 16px", borderBottom:`1px solid ${c.border}`, display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                <div>
                  <div style={{ fontSize:13, fontWeight:600, color:c.text }}>Delivery channels</div>
                  <div style={{ fontSize:11, color:c.textMuted, marginTop:2 }}>Configure how OP1 sends notifications</div>
                </div>
                <Btn variant="primary" onClick={()=>{ setNotifChannelDraft({ChannelType:'email',DisplayName:'',IsEnabled:true,_cfg:{SmtpHost:'',SmtpPort:587,SmtpUsername:'',SmtpPassword:'',FromAddress:'',FromName:'OP1 Operations Portal',EnableSsl:true}}); setNotifChannelModal('add'); }}>+ Add channel</Btn>
              </div>
              {notifChs.length===0 ? (
                <div style={{ padding:"32px 16px", textAlign:"center", color:c.textMuted, fontSize:12 }}>No channels configured. Add an email or webhook channel to start sending notifications.</div>
              ) : (
                <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                  <thead><tr style={{ borderBottom:`1px solid ${c.border}`, background:"#f8fafc" }}>{tblHdr}</tr></thead>
                  <tbody>
                    {notifChs.map(ch=>{
                      const {icon,bg,fg}=chIcon(ch.ChannelType);
                      const ts=notifTestState[ch.ChannelID];
                      return (
                        <tr key={ch.ChannelID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                          <td style={{ padding:"10px 12px" }}>
                            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                              <div style={{ width:32,height:32,borderRadius:6,background:bg,color:fg,display:"flex",alignItems:"center",justifyContent:"center",fontSize:14,fontWeight:700,flexShrink:0 }}>{icon}</div>
                              <div>
                                <div style={{ fontWeight:600, fontSize:13, color:c.text }}>{ch.DisplayName}</div>
                                <div style={{ fontSize:11, color:c.textMuted, fontFamily:"monospace" }}>{chSummary(ch)}</div>
                              </div>
                            </div>
                          </td>
                          <td style={{ padding:"10px 12px" }}><span style={{ fontSize:10,borderRadius:4,padding:"2px 7px",background:bg,color:fg,fontWeight:600,textTransform:"uppercase" }}>{ch.ChannelType}</span></td>
                          <td style={{ padding:"10px 12px" }}>{chStatus(ch)}</td>
                          <td style={{ padding:"10px 12px", color:c.textMuted, fontSize:11 }}>{fmtTested(ch.LastTestedAt)}</td>
                          <td style={{ padding:"10px 12px" }}>
                            <div style={{ display:"flex", gap:6, justifyContent:"flex-end" }}>
                              <Btn variant="secondary" style={{ fontSize:11,padding:"4px 10px" }} disabled={ts==='sending'}
                                onClick={async()=>{ setNotifTestState(p=>({...p,[ch.ChannelID]:'sending'})); try { await api("POST",`/notifications/channels/${ch.ChannelID}/test`,{}); setNotifTestState(p=>({...p,[ch.ChannelID]:'ok'})); loadNotificationsTab(); } catch { setNotifTestState(p=>({...p,[ch.ChannelID]:'fail'})); } }}>
                                {ts==='sending'?'Testing…':ts==='ok'?'✓ OK':ts==='fail'?'✗ Failed':'Test'}
                              </Btn>
                              <Btn variant="secondary" style={{ fontSize:11,padding:"4px 10px" }}
                                onClick={()=>{ try { const cfg=JSON.parse(ch.ConfigJson||'{}'); setNotifChannelDraft({ChannelID:ch.ChannelID,DisplayName:ch.DisplayName,ChannelType:ch.ChannelType,IsEnabled:ch.IsEnabled,_cfg:{SmtpHost:cfg.SmtpHost||'',SmtpPort:cfg.SmtpPort||587,SmtpUsername:cfg.SmtpUsername||'',SmtpPassword:cfg.SmtpPassword||'',FromAddress:cfg.FromAddress||'',FromName:cfg.FromName||'OP1 Operations Portal',EnableSsl:cfg.EnableSsl!==false,Url:cfg.Url||'',Secret:cfg.Secret||''}}); } catch { setNotifChannelDraft({ChannelID:ch.ChannelID,DisplayName:ch.DisplayName,ChannelType:ch.ChannelType,IsEnabled:ch.IsEnabled,_cfg:{}}); } setNotifChannelModal(ch); }}>Edit</Btn>
                              <Btn variant="danger" style={{ fontSize:11,padding:"4px 10px" }}
                                onClick={async()=>{ if(!window.confirm('Delete this channel? This will also remove all recipients assigned to it.')) return; try { await api("DELETE",`/notifications/channels/${ch.ChannelID}`); loadNotificationsTab(); } catch(e) { alert(e.message); } }}>Delete</Btn>
                            </div>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          );

          const recipInits = (name) => { const p=(name||'').trim().split(/\s+/); return p.length>=2?(p[0][0]+p[p.length-1][0]).toUpperCase():(name||'?')[0].toUpperCase(); };
          const notifRecipientsCard = (
            <div style={cardStyle}>
              <div style={{ padding:"12px 16px", borderBottom:`1px solid ${c.border}`, display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                <div>
                  <div style={{ fontSize:13, fontWeight:600, color:c.text }}>Recipients</div>
                  <div style={{ fontSize:11, color:c.textMuted, marginTop:2 }}>Global list — assign to monitors or groups from the monitor form</div>
                </div>
                <Btn variant="primary" onClick={()=>{ setNotifRecipientDraft({DisplayName:'',ChannelID:notifChs[0]?.ChannelID||'',Destination:'',IsActive:true}); setNotifRecipientModal('add'); }}>+ Add recipient</Btn>
              </div>
              {notifRecs.length===0 ? (
                <div style={{ padding:"32px 16px", textAlign:"center", color:c.textMuted, fontSize:12 }}>No recipients configured. Add a recipient to begin assigning notifications.</div>
              ) : (
                <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                  <thead><tr style={{ borderBottom:`1px solid ${c.border}`, background:"#f8fafc" }}>
                    {["Name","Channel","Destination","Assigned to","Active",""].map(h=><th key={h} style={{padding:"8px 12px",textAlign:"left",fontSize:9,fontWeight:700,color:c.textMuted,textTransform:"uppercase",letterSpacing:"0.05em"}}>{h}</th>)}
                  </tr></thead>
                  <tbody>
                    {notifRecs.map(r=>{
                      const {bg:rb,fg:rf}=chIcon(r.ChannelType);
                      return (
                        <tr key={r.RecipientID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                          <td style={{ padding:"10px 12px" }}>
                            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                              <div style={{ width:28,height:28,borderRadius:"50%",background:c.blueLight,color:c.blue,display:"flex",alignItems:"center",justifyContent:"center",fontSize:11,fontWeight:700,flexShrink:0 }}>{recipInits(r.DisplayName)}</div>
                              <span style={{ fontWeight:600, color:c.text }}>{r.DisplayName}</span>
                            </div>
                          </td>
                          <td style={{ padding:"10px 12px" }}><span style={{ fontSize:10,borderRadius:4,padding:"2px 7px",background:rb,color:rf,fontWeight:600,textTransform:"uppercase" }}>{r.ChannelType}</span></td>
                          <td style={{ padding:"10px 12px", fontFamily:"monospace", fontSize:11, color:c.textMuted }}>{r.Destination}</td>
                          <td style={{ padding:"10px 12px" }}>{r.AssignedCount>0?<span style={{fontSize:11,color:c.textMuted}}>{r.AssignedCount} monitor{r.AssignedCount!==1?'s':''}</span>:<span style={{fontSize:11,color:c.textMuted}}>Unassigned</span>}</td>
                          <td style={{ padding:"10px 12px" }}>
                            {tog(r.IsActive,async()=>{ await api("PUT",`/notifications/recipients/${r.RecipientID}`,{DisplayName:r.DisplayName,ChannelID:r.ChannelID,Destination:r.Destination,IsActive:!r.IsActive}); loadNotificationsTab(); })}
                          </td>
                          <td style={{ padding:"10px 12px" }}>
                            <div style={{ display:"flex", gap:6, justifyContent:"flex-end" }}>
                              <Btn variant="secondary" style={{ fontSize:11,padding:"4px 10px" }} onClick={()=>{ setNotifRecipientDraft({DisplayName:r.DisplayName,ChannelID:r.ChannelID,Destination:r.Destination,IsActive:r.IsActive}); setNotifRecipientModal(r); }}>Edit</Btn>
                              <Btn variant="danger" style={{ fontSize:11,padding:"4px 10px" }}
                                onClick={async()=>{ if(!window.confirm(r.AssignedCount>0?`This recipient is assigned to ${r.AssignedCount} monitor${r.AssignedCount!==1?'s':''}. Remove anyway?`:'Remove this recipient?')) return; try { await api("DELETE",`/notifications/recipients/${r.RecipientID}`); loadNotificationsTab(); } catch(e) { alert(e.message); } }}>Remove</Btn>
                            </div>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              )}
            </div>
          );

          const notifRulesCard = (
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
              <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom:16 }}>
                <div>
                  <div style={{ fontSize:13, fontWeight:600, color:c.text }}>Delivery rules — global defaults</div>
                  <div style={{ fontSize:11, color:c.textMuted, marginTop:2 }}>Overridable per monitor assignment</div>
                </div>
                <Btn variant="primary" style={{ fontSize:11,padding:"6px 14px" }} loading={notifSaving}
                  onClick={async()=>{ setNotifSaving(true); try { for(const [k,v] of Object.entries(notifRulesDraft)) await api("POST","/notifications/settings",{key:k,value:String(v)}); setNotifSettings({...notifRulesDraft}); } catch{} setNotifSaving(false); }}>
                  Save rules
                </Btn>
              </div>
              <div style={{ display:"flex", gap:16, flexWrap:"wrap", marginBottom:16 }}>
                <div style={{ flex:"1 1 200px" }}>
                  <label style={lblS}>Minimum severity to notify</label>
                  <select value={notifRulesDraft['Notifications_MinSeverity']||'warning'} onChange={e=>setNotifRulesDraft(p=>({...p,'Notifications_MinSeverity':e.target.value}))} style={inpS}>
                    <option value="warning">Warning and above</option>
                    <option value="critical">Critical only</option>
                  </select>
                </div>
                <div style={{ flex:"1 1 180px" }}>
                  <label style={lblS}>Cooldown between repeat alerts</label>
                  <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                    <input type="number" min={0} max={1440} value={notifRulesDraft['Notifications_CooldownMinutes']||'30'} onChange={e=>setNotifRulesDraft(p=>({...p,'Notifications_CooldownMinutes':e.target.value}))} style={{...inpS,width:80}} />
                    <span style={{ fontSize:12, color:c.textMuted }}>minutes</span>
                  </div>
                </div>
                <div style={{ flex:"1 1 260px" }}>
                  <label style={lblS}>Suppress child if parent failing</label>
                  <div style={{ display:"flex", alignItems:"center", gap:10, marginTop:4 }}>
                    {tog(notifRulesDraft['Notifications_SuppressChildIfParentFailing']==='true',()=>setNotifRulesDraft(p=>({...p,'Notifications_SuppressChildIfParentFailing':p['Notifications_SuppressChildIfParentFailing']==='true'?'false':'true'})))}
                    <div><div style={{ fontSize:12,fontWeight:600,color:c.text }}>Enabled</div><div style={{ fontSize:11,color:c.textMuted }}>Child alerts suppressed when parent is failing</div></div>
                  </div>
                </div>
              </div>
              <div style={{ display:"flex", gap:16, flexWrap:"wrap" }}>
                <div style={{ flex:"1 1 240px" }}>
                  <label style={lblS}>Recovery notifications</label>
                  <div style={{ display:"flex", alignItems:"center", gap:10, marginTop:4 }}>
                    {tog(notifRulesDraft['Notifications_SendRecovery']==='true',()=>setNotifRulesDraft(p=>({...p,'Notifications_SendRecovery':p['Notifications_SendRecovery']==='true'?'false':'true'})))}
                    <div><div style={{ fontSize:12,fontWeight:600,color:c.text }}>Send recovered notification</div><div style={{ fontSize:11,color:c.textMuted }}>Fires when a monitor returns to healthy</div></div>
                  </div>
                </div>
                <div style={{ flex:"1 1 240px" }}>
                  <label style={lblS}>Include AI diagnosis</label>
                  <div style={{ display:"flex", alignItems:"center", gap:10, marginTop:4 }}>
                    {tog(notifRulesDraft['Notifications_IncludeAiDiagnosis']==='true',()=>setNotifRulesDraft(p=>({...p,'Notifications_IncludeAiDiagnosis':p['Notifications_IncludeAiDiagnosis']==='true'?'false':'true'})))}
                    <div><div style={{ fontSize:12,fontWeight:600,color:c.text }}>Attach diagnosis if available</div><div style={{ fontSize:11,color:c.textMuted }}>Includes AI diagnosis result in notification body</div></div>
                  </div>
                </div>
              </div>
            </div>
          );

          const notifTestCard = (
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
              <div style={{ marginBottom:14 }}>
                <div style={{ fontSize:13, fontWeight:600, color:c.text }}>Send test notification</div>
                <div style={{ fontSize:11, color:c.textMuted, marginTop:2 }}>Validate delivery without triggering a real alert</div>
              </div>
              <div style={{ display:"flex", gap:12, alignItems:"flex-end" }}>
                <div style={{ flex:"1 1 160px" }}>
                  <label style={lblS}>Channel</label>
                  <select value={notifTestCh} onChange={e=>setNotifTestCh(e.target.value)} style={inpS}>
                    <option value="">Select channel…</option>
                    {notifChs.map(ch=><option key={ch.ChannelID} value={String(ch.ChannelID)}>{ch.DisplayName} — {ch.ChannelType}</option>)}
                  </select>
                </div>
                <div style={{ flex:"1 1 160px" }}>
                  <label style={lblS}>Recipient</label>
                  <select value={notifTestRec} onChange={e=>setNotifTestRec(e.target.value)} style={inpS}>
                    <option value="">Select recipient…</option>
                    {notifRecs.filter(r=>r.IsActive).map(r=><option key={r.RecipientID} value={String(r.RecipientID)}>{r.DisplayName} ({r.Destination})</option>)}
                  </select>
                </div>
                <div style={{ display:"flex", alignItems:"center", gap:8, paddingBottom:1 }}>
                  <Btn variant="primary" disabled={!notifTestCh||notifTestResult==='sending'}
                    onClick={async()=>{ setNotifTestResult('sending'); try { await api("POST",`/notifications/channels/${notifTestCh}/test`,notifTestRec?{recipientId:parseInt(notifTestRec)}:{}); setNotifTestResult('ok'); } catch { setNotifTestResult('fail'); } }}>
                    {notifTestResult==='sending'?'Sending…':'Send test'}
                  </Btn>
                  {notifTestResult==='ok'  && <span style={{ fontSize:12,color:c.green,fontWeight:600 }}>✓ Sent</span>}
                  {notifTestResult==='fail' && <span style={{ fontSize:12,color:c.red,fontWeight:600 }}>✗ Failed</span>}
                </div>
              </div>
            </div>
          );

          return (
            <div style={{ display:"flex", flexDirection:"column", gap:16 }}>
              {notifLoading
                ? <div style={{ textAlign:"center", padding:40, color:c.textMuted }}>Loading…</div>
                : <>{notifGlobalBanner}{notifChannelsCard}{notifRecipientsCard}{notifRulesCard}{notifTestCard}</>
              }
              {notifChannelModal !== null && (()=>{
                const isEdit = notifChannelModal !== 'add';
                const d = notifChannelDraft;
                const chType = d.ChannelType || 'email';
                const emailFields = (
                  <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
                    {[
                      {k:'SmtpHost',label:'SMTP host',ph:'smtp.example.com'},
                      {k:'SmtpPort',label:'SMTP port',ph:'587'},
                      {k:'SmtpUsername',label:'SMTP username',ph:'user@example.com'},
                      {k:'SmtpPassword',label:'SMTP password',ph:'••••••••'},
                      {k:'FromAddress',label:'From address',ph:'alerts@example.com'},
                      {k:'FromName',label:'From name',ph:'OP1 Alerts'},
                    ].map(f=>(
                      <div key={f.k}>
                        <label style={lblS}>{f.label}</label>
                        <input type={f.k==='SmtpPassword'?'password':'text'} placeholder={f.ph} value={d._cfg?.[f.k]||''} onChange={e=>setNotifChannelDraft(p=>({...p,_cfg:{...p._cfg,[f.k]:e.target.value}}))} style={inpS} />
                      </div>
                    ))}
                    <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                      <input type="checkbox" id="chssl" checked={!!d._cfg?.EnableSsl} onChange={e=>setNotifChannelDraft(p=>({...p,_cfg:{...p._cfg,EnableSsl:e.target.checked}}))} />
                      <label htmlFor="chssl" style={{ fontSize:12, color:c.text }}>Enable SSL/TLS</label>
                    </div>
                  </div>
                );
                const webhookFields = (
                  <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
                    <div>
                      <label style={lblS}>Webhook URL</label>
                      <input type="text" placeholder="https://hooks.example.com/..." value={d._cfg?.Url||''} onChange={e=>setNotifChannelDraft(p=>({...p,_cfg:{...p._cfg,Url:e.target.value}}))} style={inpS} />
                    </div>
                    <div>
                      <label style={lblS}>Secret (optional — for HMAC-SHA256 signing)</label>
                      <input type="password" placeholder="leave blank to disable signing" value={d._cfg?.Secret||''} onChange={e=>setNotifChannelDraft(p=>({...p,_cfg:{...p._cfg,Secret:e.target.value}}))} style={inpS} />
                    </div>
                  </div>
                );
                const handleSave = async () => {
                  const configJson = JSON.stringify(d._cfg || {});
                  try {
                    if (isEdit) {
                      await api("PUT", `/notifications/channels/${d.ChannelID}`, { DisplayName: d.DisplayName, ConfigJson: configJson, IsEnabled: d.IsEnabled });
                    } else {
                      await api("POST", "/notifications/channels", { ChannelType: chType, DisplayName: d.DisplayName, ConfigJson: configJson, IsEnabled: true });
                    }
                    loadNotificationsTab();
                    setNotifChannelModal(null);
                  } catch(e) { alert(e.message); }
                };
                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:480, maxWidth:"95vw", maxHeight:"90vh", display:"flex", flexDirection:"column", boxShadow:"0 8px 32px rgba(0,0,0,0.18)" }}>
                      <div style={{ background:"#1E2B3C", color:"#fff", padding:"14px 20px", borderRadius:"10px 10px 0 0", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                        <span style={{ fontWeight:700, fontSize:14 }}>{isEdit ? 'Edit channel' : 'Add channel'}</span>
                        <button onClick={()=>setNotifChannelModal(null)} style={{ background:"none", border:"none", color:"rgba(255,255,255,0.7)", fontSize:18, cursor:"pointer", lineHeight:1 }}>×</button>
                      </div>
                      <div style={{ overflowY:"auto", padding:20, display:"flex", flexDirection:"column", gap:14 }}>
                        <div>
                          <label style={lblS}>Display name</label>
                          <input type="text" placeholder="e.g. Primary email relay" value={d.DisplayName||''} onChange={e=>setNotifChannelDraft(p=>({...p,DisplayName:e.target.value}))} style={inpS} />
                        </div>
                        {!isEdit && (
                          <div>
                            <label style={lblS}>Channel type</label>
                            <select value={chType} onChange={e=>setNotifChannelDraft(p=>({...p,ChannelType:e.target.value,_cfg:{}}))} style={inpS}>
                              <option value="email">Email (SMTP)</option>
                              <option value="webhook">Webhook (HTTP POST)</option>
                            </select>
                          </div>
                        )}
                        {chType === 'email' ? emailFields : webhookFields}
                      </div>
                      <div style={{ padding:"12px 20px", borderTop:`1px solid ${c.border}`, display:"flex", gap:8, justifyContent:"flex-end" }}>
                        <Btn variant="secondary" onClick={()=>setNotifChannelModal(null)}>Cancel</Btn>
                        <Btn variant="primary" onClick={handleSave}>Save channel</Btn>
                      </div>
                    </div>
                  </div>
                );
              })()}
              {notifRecipientModal !== null && (()=>{
                const isEdit = notifRecipientModal !== 'add';
                const d = notifRecipientDraft;
                const selCh = notifChs.find(c=>c.ChannelID === (parseInt(d.ChannelID)||0));
                const destLabel = selCh?.ChannelType === 'webhook' ? 'Webhook URL override (leave blank to use channel URL)' : 'Email address';
                const destPlaceholder = selCh?.ChannelType === 'webhook' ? 'https://… (optional)' : 'user@example.com';
                const handleRecSave = async () => {
                  try {
                    if (isEdit) {
                      await api("PUT", `/notifications/recipients/${notifRecipientModal.RecipientID}`, { DisplayName: d.DisplayName, Destination: d.Destination, IsActive: d.IsActive });
                    } else {
                      await api("POST", "/notifications/recipients", { DisplayName: d.DisplayName, ChannelID: parseInt(d.ChannelID), Destination: d.Destination, IsActive: true });
                    }
                    loadNotificationsTab();
                    setNotifRecipientModal(null);
                  } catch(e) { alert(e.message); }
                };
                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:420, maxWidth:"95vw", display:"flex", flexDirection:"column", boxShadow:"0 8px 32px rgba(0,0,0,0.18)" }}>
                      <div style={{ background:"#1E2B3C", color:"#fff", padding:"14px 20px", borderRadius:"10px 10px 0 0", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                        <span style={{ fontWeight:700, fontSize:14 }}>{isEdit ? 'Edit recipient' : 'Add recipient'}</span>
                        <button onClick={()=>setNotifRecipientModal(null)} style={{ background:"none", border:"none", color:"rgba(255,255,255,0.7)", fontSize:18, cursor:"pointer", lineHeight:1 }}>×</button>
                      </div>
                      <div style={{ padding:20, display:"flex", flexDirection:"column", gap:14 }}>
                        <div>
                          <label style={lblS}>Display name</label>
                          <input type="text" placeholder="e.g. On-call Team" value={d.DisplayName||''} onChange={e=>setNotifRecipientDraft(p=>({...p,DisplayName:e.target.value}))} style={inpS} />
                        </div>
                        {!isEdit && (
                          <div>
                            <label style={lblS}>Channel</label>
                            <select value={d.ChannelID||''} onChange={e=>setNotifRecipientDraft(p=>({...p,ChannelID:e.target.value}))} style={inpS}>
                              <option value="">Select channel…</option>
                              {notifChs.map(ch=><option key={ch.ChannelID} value={ch.ChannelID}>{ch.DisplayName} ({ch.ChannelType})</option>)}
                            </select>
                          </div>
                        )}
                        <div>
                          <label style={lblS}>{destLabel}</label>
                          <input type="text" placeholder={destPlaceholder} value={d.Destination||''} onChange={e=>setNotifRecipientDraft(p=>({...p,Destination:e.target.value}))} style={inpS} />
                        </div>
                      </div>
                      <div style={{ padding:"12px 20px", borderTop:`1px solid ${c.border}`, display:"flex", gap:8, justifyContent:"flex-end" }}>
                        <Btn variant="secondary" onClick={()=>setNotifRecipientModal(null)}>Cancel</Btn>
                        <Btn variant="primary" onClick={handleRecSave}>Save recipient</Btn>
                      </div>
                    </div>
                  </div>
                );
              })()}
            </div>
          );
        })()}
        {configTab==="retention" && (
          <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, overflow:"hidden" }}>
            <div style={{ padding:"12px 16px", borderBottom:"0.5px solid #e2e5ea", display:"flex", justifyContent:"space-between", alignItems:"center" }}>
              <div>
                <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Data Retention Policies</div>
                <div style={{ fontSize:11, color:"#94a3b8", marginTop:2 }}>Hot / warm / cold tiers. Purge runs daily at 2:00 AM UTC.</div>
              </div>
              <Btn variant="danger" onClick={runPurge}>Run Purge Now</Btn>
            </div>
            {retPolicies.length===0 ? <div style={{ padding:32, textAlign:"center", color:"#94a3b8", fontSize:12 }}>No policies found.</div> : (
              <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                <thead><tr style={{ borderBottom:"0.5px solid #e2e5ea", background:"#f8fafc" }}>
                  {["Table","Hot (d)","Warm (d)","Cold (d)","Active","Last Purge"].map(h=><th key={h} style={{ padding:"8px 12px", textAlign:"left", fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>{h}</th>)}
                </tr></thead>
                <tbody>
                  {retPolicies.map(p=>(
                    <tr key={p.PolicyID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                      <td style={{ padding:"8px 12px", fontFamily:"monospace", fontSize:11 }}>{p.TableName}</td>
                      <td style={{ padding:"8px 12px", fontWeight:700 }}>{p.HotRetentionDays}</td>
                      <td style={{ padding:"8px 12px" }}>{p.WarmRetentionDays}</td>
                      <td style={{ padding:"8px 12px" }}>{p.ColdRetentionDays}</td>
                      <td style={{ padding:"8px 12px" }}>{p.IsActive?<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#dcfce7", color:"#166534" }}>ON</span>:<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#fca5a5", color:"#991b1b" }}>OFF</span>}</td>
                      <td style={{ padding:"8px 12px", color:"#94a3b8", fontSize:11 }}>{p.LastPurgeAt?parseUTC(p.LastPurgeAt)?.toLocaleDateString():"—"}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </div>
        )}
        {configTab==="monitor-defaults" && (
          <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
            <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12 }}>
              <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Monitor Defaults</div>
              <Btn variant="primary" onClick={saveDefaults} style={{ fontSize:11, padding:"6px 14px" }}>Save Defaults</Btn>
            </div>
            <div style={{ fontSize:12, color:"#94a3b8", marginBottom:16 }}>These values pre-populate the Add Monitor form. Edit any field and click Save Defaults.</div>
            {MONITOR_DEFAULT_SECTIONS.map(sec=>(
              <div key={sec.label} style={{ marginBottom:20 }}>
                <div style={{ fontSize:10, fontWeight:700, color:"#006D8C", letterSpacing:"0.08em", textTransform:"uppercase", marginBottom:8, paddingBottom:4, borderBottom:"0.5px solid #f1f5f9" }}>{sec.label}</div>
                <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                  <thead><tr>
                    {["Setting","Value","Description"].map(h=><th key={h} style={{ padding:"6px 10px", textAlign:"left", fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>{h}</th>)}
                  </tr></thead>
                  <tbody>
                    {sec.keys.map(k=>{
                      const val = monDefaultsDirty[k] ?? monDefaults[k] ?? "";
                      return (
                        <tr key={k} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                          <td style={{ padding:"7px 10px", fontWeight:600, fontSize:12 }}>{MONITOR_DEFAULT_LABELS[k]||k}</td>
                          <td style={{ padding:"4px 10px" }}>
                            <input type="number" value={val}
                              onChange={e=>setMonDefaultsDirty(p=>({...p,[k]:e.target.value}))}
                              style={{ width:100, padding:"4px 8px", border:"0.5px solid #e2e5ea", borderRadius:4, fontSize:12, fontFamily:"monospace", background:"#ffffff", color:"#1e293b" }} />
                          </td>
                          <td style={{ padding:"7px 10px", color:"#94a3b8", fontSize:11 }}>{MONITOR_DEFAULT_DESC[k]||""}</td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            ))}
          </div>
        )}
        {configTab==="credentials" && (
          <CredentialsTab api={api} />
        )}
        {configTab==="machines" && (
          <MachineManagementSection api={api} machines={machinesProp||[]} currentMachine={currentMachine} onRefresh={onMachinesChange} setErr={setErr} setOk={setOk} />
        )}
        {configTab==="ai-diagnosis" && (
          <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16, maxWidth:520 }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:14 }}>AI Diagnosis Settings</div>
            {aiCfgLoading ? (
              <div style={{ color:"#94a3b8", fontSize:12, padding:"16px 0" }}>Loading…</div>
            ) : aiCfg === null ? (
              <div style={{ color:"#94a3b8", fontSize:12, padding:"16px 0" }}>Failed to load settings.</div>
            ) : (
              <div style={{ display:"flex", flexDirection:"column", gap:16 }}>
                {/* Enable toggle */}
                <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between" }}>
                  <div>
                    <div style={{ fontSize:13, fontWeight:600, color:"#1e293b" }}>Enable AI Diagnosis</div>
                    <div style={{ fontSize:11, color:"#94a3b8", marginTop:2 }}>Automatically diagnose critical alerts using AI</div>
                  </div>
                  <div onClick={()=>setAiCfg(p=>({...p, enabled:!p.enabled}))}
                    style={{ width:36, height:20, borderRadius:10, background:aiCfg.enabled?"#006D8C":"#e2e5ea", position:"relative", cursor:"pointer", transition:"background 0.2s", flexShrink:0 }}>
                    <div style={{ position:"absolute", top:3, left:aiCfg.enabled?17:3, width:14, height:14, borderRadius:"50%", background:"#fff", transition:"left 0.2s" }} />
                  </div>
                </div>
                {/* Min severity */}
                <div style={{ opacity: aiCfg.enabled ? 1 : 0.4, pointerEvents: aiCfg.enabled ? "auto" : "none" }}>
                  <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:6, textTransform:"uppercase", letterSpacing:"0.05em" }}>Minimum Severity</div>
                  <select value={aiCfg.minSeverity} onChange={e=>setAiCfg(p=>({...p, minSeverity:e.target.value}))}
                    style={{ width:"100%", padding:"8px 12px", border:"1px solid #e2e5ea", borderRadius:6, fontSize:13, background:"#fff", color:"#1e293b", outline:"none" }}>
                    <option value="critical">Critical only</option>
                    <option value="warning">Warning and above</option>
                    <option value="info">All alerts</option>
                  </select>
                </div>
                {/* Cooldown */}
                <div style={{ opacity: aiCfg.enabled ? 1 : 0.4, pointerEvents: aiCfg.enabled ? "auto" : "none" }}>
                  <div style={{ fontSize:11, fontWeight:600, color:"#4A4A4A", marginBottom:6, textTransform:"uppercase", letterSpacing:"0.05em" }}>Cooldown (same monitor)</div>
                  <select value={String(aiCfg.cooldownMinutes)} onChange={e=>setAiCfg(p=>({...p, cooldownMinutes:parseInt(e.target.value)}))}
                    style={{ width:"100%", padding:"8px 12px", border:"1px solid #e2e5ea", borderRadius:6, fontSize:13, background:"#fff", color:"#1e293b", outline:"none" }}>
                    <option value="15">15 min</option>
                    <option value="30">30 min</option>
                    <option value="60">1 hour</option>
                    <option value="120">2 hours</option>
                    <option value="240">4 hours</option>
                  </select>
                </div>
                {/* Usage */}
                <div style={{ fontSize:12, color:"#4A4A4A" }}>
                  {aiCfg.firesLast24h > 0
                    ? `Fired ${aiCfg.firesLast24h} time${aiCfg.firesLast24h !== 1 ? "s" : ""} in the last 24 hours`
                    : "No diagnoses in the last 24 hours"}
                </div>
                {/* Save */}
                <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                  <button onClick={saveAiCfg} disabled={aiCfgSaving}
                    style={{ padding:"8px 20px", background:"#006D8C", color:"#fff", border:"none", borderRadius:6, fontSize:13, fontWeight:600, cursor:"pointer" }}>
                    {aiCfgSaving ? "Saving…" : "Save"}
                  </button>
                  {aiCfgSaved && <span style={{ fontSize:12, color:"#008C6F", fontWeight:600 }}>Saved ✓</span>}
                </div>
              </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 35s 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()} \u00b7 {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>
  );
}

// ─── Machine Management Section ─────────────────────────────────────────────
function MachineManagementSection({ api, machines, currentMachine, onRefresh, setErr, setOk }) {
  const [summaries, setSummaries] = useState([]);
  const [loading, setLoading]     = useState(true);
  const [aliases, setAliases]     = useState({});
  const [deleteConfirm, setDeleteConfirm] = useState(null); // machineName
  const [deleteInput, setDeleteInput]     = useState("");
  const [busy, setBusy]           = useState({});
  const [pauseReasons, setPauseReasons]   = useState({});

  const load = useCallback(()=>{
    setLoading(true);
    api("GET","/config/machine-summary")
      .then(r=>{ setSummaries(Array.isArray(r)?r:[]); setLoading(false); })
      .catch(()=>setLoading(false));
  },[api]);

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

  const saveAlias = async (machineName) => {
    setBusy(b=>({...b,[machineName+"_alias"]:true}));
    try {
      await api("POST","/config/monitoring-control/alias",{ MachineName:machineName, Alias: aliases[machineName]||"" });
      setOk("Alias saved."); load(); onRefresh && onRefresh();
    } catch(e){ setErr(e.message); }
    finally { setBusy(b=>({...b,[machineName+"_alias"]:false})); }
  };

  const toggleMachine = async (machineName, isActive) => {
    setBusy(b=>({...b,[machineName+"_toggle"]:true}));
    try {
      await api("POST","/config/monitoring-control/toggle",{
        MachineName: machineName, IsActive: !isActive,
        Reason: isActive ? (pauseReasons[machineName]||"Admin pause") : null,
      });
      setOk(isActive?"Monitoring paused.":"Monitoring resumed."); load(); onRefresh && onRefresh();
    } catch(e){ setErr(e.message); }
    finally { setBusy(b=>({...b,[machineName+"_toggle"]:false})); }
  };

  const deleteMachine = async () => {
    if (!deleteConfirm || deleteInput !== "DELETE") return;
    setBusy(b=>({...b,[deleteConfirm+"_del"]:true}));
    try {
      await api("DELETE",`/config/machine/${encodeURIComponent(deleteConfirm)}`);
      setOk(`Machine '${deleteConfirm}' deleted.`); setDeleteConfirm(null); setDeleteInput(""); load(); onRefresh && onRefresh();
    } catch(e){ setErr(e.message); }
    finally { setBusy(b=>({...b,[deleteConfirm+"_del"]:false})); }
  };

  return (
    <Card>
      <SectionHead>Source Monitor Registry</SectionHead>
      <p style={{ fontSize:12, color:c.textDim, marginBottom:16 }}>
        Each OP1 instance registers itself here. Machines can be paused to stop monitoring without stopping the service.
      </p>
      {loading ? <Spinner /> : summaries.length===0 ? <div style={{ color:c.textDimmer, fontSize:12 }}>No machines registered.</div> : (
        <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
          {summaries.map(s => {
            const isCurrent = s.MachineName === currentMachine;
            const aliasVal = aliases[s.MachineName] ?? s.MachineAlias ?? "";
            return (
              <div key={s.MachineName} style={{ border:`1px solid ${c.border}`, borderRadius:8, padding:16, background: isCurrent ? "rgba(0,109,140,0.04)" : c.surface, borderLeft: isCurrent ? `3px solid ${c.blue}` : `3px solid ${c.border}` }}>
                <div style={{ display:"flex", alignItems:"flex-start", gap:12, flexWrap:"wrap" }}>
                  <div style={{ flex:1, minWidth:200 }}>
                    <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:4 }}>
                      <span style={{ fontFamily:"mono", fontSize:13, fontWeight:700, color:c.text }}>{s.MachineName}</span>
                      {isCurrent && <Badge color={c.blue}>this machine</Badge>}
                      {s.IsActive ? <Badge color={c.green}>Active</Badge> : <Badge color={c.yellow}>Paused</Badge>}
                    </div>
                    {s.MachineAlias && <div style={{ fontSize:11, color:c.textDim, marginBottom:6 }}>Alias: {s.MachineAlias}</div>}
                    <div style={{ display:"flex", gap:16, flexWrap:"wrap", fontSize:11, color:c.textDimmer }}>
                      <span>{s.MonitorCount} monitor{s.MonitorCount!==1?"s":""}</span>
                      <span>{s.ChecksLast24h} checks last 24h</span>
                      {s.LastCheckAt && <span>Last check: {parseUTC(s.LastCheckAt)?.toLocaleString()}</span>}
                    </div>
                    {!s.IsActive && s.PauseReason && <div style={{ fontSize:11, color:c.amber, marginTop:4 }}>Pause reason: {s.PauseReason}</div>}
                    {!s.IsActive && s.PausedAt && <div style={{ fontSize:10, color:c.textDimmer }}>Paused: {parseUTC(s.PausedAt)?.toLocaleString()}</div>}
                  </div>
                  <div style={{ display:"flex", gap:8, alignItems:"center", flexWrap:"wrap" }}>
                    <div style={{ display:"flex", gap:4, alignItems:"center" }}>
                      <input value={aliasVal} onChange={e=>setAliases(a=>({...a,[s.MachineName]:e.target.value}))}
                        placeholder="Set alias…"
                        style={{ width:130, padding:"4px 8px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11, fontFamily:"mono", background:c.surface, color:c.text }} />
                      <Btn variant="secondary" loading={busy[s.MachineName+"_alias"]} onClick={()=>saveAlias(s.MachineName)} style={{ fontSize:11, padding:"4px 10px" }}>Save</Btn>
                    </div>
                    {s.IsActive ? (
                      <div style={{ display:"flex", gap:4, alignItems:"center" }}>
                        <input value={pauseReasons[s.MachineName]||""} onChange={e=>setPauseReasons(r=>({...r,[s.MachineName]:e.target.value}))}
                          placeholder="Pause reason…"
                          style={{ width:130, padding:"4px 8px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11, background:c.surface, color:c.text }} />
                        <Btn variant="warning" loading={busy[s.MachineName+"_toggle"]} onClick={()=>toggleMachine(s.MachineName, true)} style={{ fontSize:11, padding:"4px 10px" }}>Pause</Btn>
                      </div>
                    ) : (
                      <Btn variant="success" loading={busy[s.MachineName+"_toggle"]} onClick={()=>toggleMachine(s.MachineName, false)} style={{ fontSize:11, padding:"4px 10px" }}>Resume</Btn>
                    )}
                    {!isCurrent && (
                      deleteConfirm === s.MachineName ? (
                        <div style={{ display:"flex", gap:4, alignItems:"center" }}>
                          <input value={deleteInput} onChange={e=>setDeleteInput(e.target.value)}
                            placeholder='Type "DELETE"'
                            style={{ width:110, padding:"4px 8px", border:`1px solid ${c.red}`, borderRadius:4, fontSize:11, background:c.surface, color:c.red }} />
                          <Btn variant="danger" loading={busy[s.MachineName+"_del"]} onClick={deleteMachine} style={{ fontSize:11, padding:"4px 10px" }} disabled={deleteInput!=="DELETE"}>Confirm</Btn>
                          <Btn variant="ghost" onClick={()=>{ setDeleteConfirm(null); setDeleteInput(""); }} style={{ fontSize:11 }}>Cancel</Btn>
                        </div>
                      ) : (
                        <Btn variant="danger" onClick={()=>{ setDeleteConfirm(s.MachineName); setDeleteInput(""); }} style={{ fontSize:11, padding:"4px 10px" }}>Delete</Btn>
                      )
                    )}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </Card>
  );
}

// ═══ USERS ═══
function UsersView({ api, monitoringControl }) {
  const [users, setUsers]               = useState([]);
  const [loading, setLoading]           = useState(true);
  const [err, setErr]                   = useState("");
  const [ok, setOk]                     = useState("");
  const [page, setPage]                 = useState(1);
  const [search, setSearch]             = useState("");
  const [roleFilter, setRoleFilter]     = useState("all");
  const [statusFilter, setStatusFilter] = useState("all");
  const [newUser, setNewUser]           = useState({ Email:"", DisplayName:"", RoleID:"2", Password:"" });
  const [busy, setBusy]                 = useState({});
  const [recentAlerts, setRecentAlerts] = useState([]);
  const PAGE_SIZE = 20;

  const load = useCallback(()=>{
    setLoading(true);
    const q=new URLSearchParams({page,pageSize:PAGE_SIZE});
    if(roleFilter!=="all") q.set("roleFilter",roleFilter);
    if(search) q.set("search",search);
    api("GET",`/users?${q}`).then(r=>{setUsers(Array.isArray(r)?r:(r?.Data||r?.Users||[])); setErr("");}).catch(e=>setErr(e.message)).finally(()=>setLoading(false));
  },[api,page,roleFilter,search]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

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

  const createUser = async () => {
    setErr(""); setOk("");
    try {
      await api("POST","/users",{ Email:newUser.Email, DisplayName:newUser.DisplayName, Password:newUser.Password, RoleID:parseInt(newUser.RoleID,10) });
      setOk("User created."); setNewUser({Email:"",DisplayName:"",RoleID:"2",Password:""}); load();
    } catch(e){setErr(e.message);}
  };

  const lockUser = async (id,lock) => {
    setBusy(b=>({...b,[id]:true}));
    try { await api("POST",`/users/${id}/${lock?"lock":"unlock"}`,{}); load(); } catch(e){setErr(e.message);} finally{setBusy(b=>({...b,[id]:false}));}
  };

  const resetPwd = async (id) => {
    setBusy(b=>({...b,[id]:true}));
    try { await api("POST",`/users/${id}/reset-password`,{}); setOk("Password reset email sent."); } catch(e){setErr(e.message);} finally{setBusy(b=>({...b,[id]:false}));}
  };

  const filtered = users.filter(u=>{
    if(statusFilter==="active"   && !u.IsActive)  return false;
    if(statusFilter==="inactive" && u.IsActive)   return false;
    if(statusFilter==="locked"   && !u.IsLocked)  return false;
    return true;
  });

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};

  const uAdmins = users.filter(u=>u.RoleName==="Admin").length;
  const uActive = users.filter(u=>u.IsActive && !u.IsLocked).length;

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Total Users", String(users.length), "#1e293b"],
            ["Admins",      String(uAdmins),       uAdmins > 0 ? "#0369a1" : "#1e293b"],
            ["Active",      String(uActive),        "#16a34a"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Filter bar ── */}
      <div style={{ display:"flex", alignItems:"center", background:"#f8fafc", borderBottom:"0.5px solid #e2e5ea", height:36, padding:"0 12px", flexShrink:0, gap:10 }}>
        <div style={{ position:"relative", display:"inline-flex", alignItems:"center" }}>
          <span style={{ position:"absolute", left:7, fontSize:10, color:"#9CA3AF", pointerEvents:"none" }}>&#x1F50D;</span>
          <input placeholder="Search email or name…" value={search} onChange={e=>{setSearch(e.target.value);setPage(1);}}
            style={{ height:24, width:180, paddingLeft:24, paddingRight:6, fontSize:11, border:"0.5px solid #e2e5ea", borderRadius:4, outline:"none", color:"#374151", background:"#fff", fontFamily:"inherit" }} />
        </div>
        <select value={roleFilter} onChange={e=>{setRoleFilter(e.target.value);setPage(1);}}
          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 Roles</option>
          <option value="Admin">Admin</option>
          <option value="User">User</option>
          <option value="Viewer">Viewer</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 value="active">Active</option>
          <option value="inactive">Inactive</option>
          <option value="locked">Locked</option>
        </select>
        <div style={{ marginLeft:"auto", flexShrink:0 }}>
          <button onClick={load} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#fff", color:"#475569", cursor:"pointer" }}>Refresh</button>
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"16px" }}>
        <ErrBox msg={err} /><OkBox msg={ok} />
        {loading ? <Spinner /> : (
          <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, overflow:"hidden", marginBottom:16 }}>
            <div style={{ overflowX:"auto" }}>
              <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
                <thead><tr style={{ borderBottom:"1px solid #C0C0B0", background:"#D8CCBA" }}>
                  {["Email","Display Name","Role","Status","Last Login","Actions"].map(h=>(
                    <th key={h} style={{ padding:"10px 14px", textAlign:"left", fontSize:11, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.05em", position:"sticky", top:0, zIndex:1, background:"#D8CCBA" }}>{h}</th>
                  ))}
                </tr></thead>
                <tbody>
                  {filtered.map(u=>(
                    <tr key={u.UserID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                      <td style={{ padding:"10px 14px", fontFamily:"monospace", fontSize:11 }}>{u.Email}</td>
                      <td style={{ padding:"10px 14px", fontSize:12 }}>{u.DisplayName}</td>
                      <td style={{ padding:"10px 14px" }}>
                        <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px",
                          background: u.RoleName==="Admin"?"#e0f2fe":u.RoleName==="Viewer"?"#f3e8ff":"#f1f5f9",
                          color:      u.RoleName==="Admin"?"#0369a1":u.RoleName==="Viewer"?"#6b21a8":"#475569" }}>
                          {u.RoleName}
                        </span>
                      </td>
                      <td style={{ padding:"10px 14px" }}>
                        {u.IsLocked
                          ? <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#fca5a5", color:"#991b1b" }}>Locked</span>
                          : u.IsActive
                            ? <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#dcfce7", color:"#166534" }}>Active</span>
                            : <span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#f1f5f9", color:"#475569" }}>Inactive</span>}
                      </td>
                      <td style={{ padding:"10px 14px", fontSize:11, color:"#94a3b8" }}>{u.LastLoginAt?parseUTC(u.LastLoginAt)?.toLocaleString():"Never"}</td>
                      <td style={{ padding:"10px 14px" }}>
                        <div style={{ display:"flex", gap:4 }}>
                          <button disabled={busy[u.UserID]} onClick={()=>lockUser(u.UserID,!u.IsLocked)}
                            style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #e2e5ea", background:"#f1f5f9", color: u.IsLocked?"#166534":"#854d0e", cursor:"pointer" }}>
                            {u.IsLocked?"Unlock":"Lock"}
                          </button>
                          <button disabled={busy[u.UserID]} onClick={()=>resetPwd(u.UserID)}
                            style={{ fontSize:10, padding:"2px 8px", borderRadius:4, border:"0.5px solid #e2e5ea", background:"#f1f5f9", color:"#475569", cursor:"pointer" }}>Reset Pwd</button>
                        </div>
                      </td>
                    </tr>
                  ))}
                  {filtered.length===0 && <tr><td colSpan={6} style={{ padding:32, textAlign:"center", color:"#94a3b8" }}>No users found.</td></tr>}
                </tbody>
              </table>
            </div>
            <div style={{ padding:"10px 16px", display:"flex", gap:8, alignItems:"center", borderTop:"0.5px solid #f1f5f9" }}>
              <button onClick={()=>setPage(p=>Math.max(1,p-1))} disabled={page===1}
                style={{ fontSize:10, padding:"4px 10px", border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#475569", cursor:page===1?"default":"pointer", opacity:page===1?0.5:1 }}>← Prev</button>
              <span style={{ fontSize:11, color:"#64748b" }}>Page {page}</span>
              <button onClick={()=>setPage(p=>p+1)} disabled={filtered.length<PAGE_SIZE}
                style={{ fontSize:10, padding:"4px 10px", border:"0.5px solid #e2e5ea", borderRadius:4, background:"#fff", color:"#475569", cursor:filtered.length<PAGE_SIZE?"default":"pointer", opacity:filtered.length<PAGE_SIZE?0.5:1 }}>Next →</button>
            </div>
          </div>
        )}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>Add New User</div>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:14 }}>
            <Input label="Email" type="email" placeholder="user@company.com" value={newUser.Email} onChange={e=>setNewUser(p=>({...p,Email:e.target.value}))} />
            <Input label="Display Name" placeholder="Jane Smith" value={newUser.DisplayName} onChange={e=>setNewUser(p=>({...p,DisplayName:e.target.value}))} />
            <Input label="Temporary Password" type="password" placeholder="Min 12 chars" value={newUser.Password} onChange={e=>setNewUser(p=>({...p,Password:e.target.value}))} />
            <Sel label="Role" value={newUser.RoleID} onChange={e=>setNewUser(p=>({...p,RoleID:e.target.value}))} options={[{value:"1",label:"Admin — Full Access"},{value:"2",label:"User — Operator"},{value:"3",label:"Viewer — Read Only"}]} />
          </div>
          <div style={{ display:"flex", justifyContent:"flex-end" }}><Btn onClick={createUser}>Create User</Btn></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 35s 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()} \u00b7 {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>
  );
}

// ═══ MY PROFILE ═══
function ProfileView({ api, user, monitoringControl }) {
  const [sessions, setSessions]         = useState([]);
  const [history, setHistory]           = useState([]);
  const [passForm, setPassForm]         = useState({ CurrentPassword:"", NewPassword:"", ConfirmPassword:"" });
  const [err, setErr]                   = useState("");
  const [ok, setOk]                     = useState("");
  const [loading, setLoading]           = useState(false);
  const [busy, setBusy]                 = useState({});
  const [recentAlerts, setRecentAlerts] = useState([]);

  useEffect(()=>{
    const id = "op1-ticker-kf";
    if (!document.getElementById(id)) {
      const el = document.createElement("style"); el.id = id;
      el.textContent = "@keyframes op1-ticker { from { transform:translateX(0) } to { transform:translateX(-50%) } }";
      document.head.appendChild(el);
    }
    api("GET","/auth/sessions").then(r=>setSessions(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/auth/login-history").then(r=>setHistory(Array.isArray(r)?r:[])).catch(()=>{});
    api("GET","/alerts/recent?top=20").then(r=>setRecentAlerts(Array.isArray(r)?r:[])).catch(()=>{});
  },[api]);

  const changePass = async () => {
    setErr(""); setOk("");
    if(passForm.NewPassword!==passForm.ConfirmPassword){setErr("Passwords do not match");return;}
    setLoading(true);
    try { await api("POST","/auth/change-password",passForm); setOk("Password changed."); setPassForm({CurrentPassword:"",NewPassword:"",ConfirmPassword:""}); }
    catch(e){setErr(e.message);} finally{setLoading(false);}
  };

  const revokeSession = async (sid) => {
    setBusy(b=>({...b,[sid]:true}));
    try { await api("POST",`/auth/sessions/${sid}/revoke`,{}); setSessions(s=>s.filter(x=>x.SessionID!==sid)); } catch{}
    finally{setBusy(b=>({...b,[sid]:false}));}
  };

  const logoutAll = async () => {
    try { await api("POST","/auth/sessions/logout-all",{}); setSessions([]); setOk("All other sessions logged out."); } catch(e){setErr(e.message);}
  };

  const { isPaused: monIsPaused, canManageConfig, pausePopover, setPausePopover, pauseReason, setPauseReason, pauseBusy, togglePause, currentCtrl } = monitoringControl || {};
  const lastLogin = history.find(h=>h.WasSuccessful)?.AttemptedAt;

  const SecHead = ({ children }) => (
    <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>{children}</div>
  );

  return (
    <div style={{ 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.0.236</div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:0 }}>
          {[
            ["Role",         user.RoleName||"—",    "#1e293b"],
            ["Sessions",     String(sessions.length), "#1e293b"],
            ["Last Login",   lastLogin ? parseUTC(lastLogin)?.toLocaleDateString() : "—", "#1e293b"],
          ].flatMap(([lbl, val, col], i, arr) => [
            <div key={lbl} style={{ padding:"0 16px", textAlign:"center" }}>
              <div style={{ fontSize:13, fontWeight:600, color:col }}>{val}</div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>{lbl}</div>
            </div>,
            i < arr.length-1 && <div key={`div-${i}`} style={{ width:"0.5px", height:28, background:"#e2e5ea", flexShrink:0 }} />,
          ].filter(Boolean))}
        </div>
      </div>

      {/* ── Scrollable content ── */}
      <div style={{ flex:1, overflowY:"auto", padding:"16px", display:"flex", flexDirection:"column", gap:16 }}>
        <ErrBox msg={err} /><OkBox msg={ok} />

        {/* Account Info */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Account Info</SecHead>
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:16, fontSize:13 }}>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Display Name</div>
              <div style={{ fontWeight:600, color:"#1e293b" }}>{user.DisplayName}</div>
            </div>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Role</div>
              <span style={{ fontSize:11, borderRadius:4, padding:"2px 8px",
                background: user.RoleName==="Admin"?"#e0f2fe":user.RoleName==="Viewer"?"#f3e8ff":"#f1f5f9",
                color:      user.RoleName==="Admin"?"#0369a1":user.RoleName==="Viewer"?"#6b21a8":"#475569" }}>
                {user.RoleName}
              </span>
            </div>
            <div>
              <div style={{ fontSize:9, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:4 }}>Username</div>
              <div style={{ fontFamily:"monospace", color:"#1e293b" }}>{user.Username}</div>
            </div>
          </div>
        </div>

        {/* Change Password */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Change Password</SecHead>
          <div style={{ display:"flex", flexDirection:"column", gap:12 }}>
            <Input label="Current Password" type="password" value={passForm.CurrentPassword} onChange={e=>setPassForm(p=>({...p,CurrentPassword:e.target.value}))} />
            <Input label="New Password" type="password" placeholder="Min 12 chars, upper+lower+digit+special" value={passForm.NewPassword} onChange={e=>setPassForm(p=>({...p,NewPassword:e.target.value}))} />
            <Input label="Confirm New Password" type="password" value={passForm.ConfirmPassword} onChange={e=>setPassForm(p=>({...p,ConfirmPassword:e.target.value}))} />
            <div style={{ display:"flex", justifyContent:"flex-end" }}><Btn onClick={changePass} loading={loading}>Change Password</Btn></div>
          </div>
        </div>

        {/* Active Sessions */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:12, paddingBottom:8, borderBottom:"0.5px solid #f1f5f9" }}>
            <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em" }}>Active Sessions</div>
            <Btn variant="danger" onClick={logoutAll} style={{ fontSize:10, padding:"4px 10px" }}>Log out all other sessions</Btn>
          </div>
          {sessions.length===0 ? <div style={{ color:"#94a3b8", fontSize:12 }}>No active sessions.</div> : (
            <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
              <thead><tr style={{ borderBottom:"1px solid #C0C0B0", background:"#D8CCBA" }}>
                {["IP Address","Created","Last Active",""].map(h=><th key={h} style={{ padding:"7px 10px", textAlign:"left", fontSize:11, fontWeight:600, color:"#1A1A1A", textTransform:"uppercase", letterSpacing:"0.05em", position:"sticky", top:0, zIndex:1, background:"#D8CCBA" }}>{h}</th>)}
              </tr></thead>
              <tbody>
                {sessions.map(s=>(
                  <tr key={s.SessionID} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:11 }}>{s.IPAddress||"—"}</td>
                    <td style={{ padding:"7px 10px", fontSize:11, color:"#64748b" }}>{s.CreatedAt?parseUTC(s.CreatedAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px", fontSize:11, color:"#64748b" }}>{s.LastActivityAt?parseUTC(s.LastActivityAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px" }}><Btn variant="danger" loading={busy[s.SessionID]} onClick={()=>revokeSession(s.SessionID)} style={{ fontSize:10, padding:"3px 8px" }}>Revoke</Btn></td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

        {/* Login History */}
        <div style={{ background:"#ffffff", border:"0.5px solid #e2e5ea", borderRadius:8, padding:16 }}>
          <SecHead>Login History (Last 20)</SecHead>
          {history.length===0 ? <div style={{ color:"#94a3b8", fontSize:12 }}>No login history.</div> : (
            <table style={{ width:"100%", borderCollapse:"collapse", fontSize:12 }}>
              <thead><tr style={{ borderBottom:"0.5px solid #e2e5ea" }}>
                {["Time","IP Address","Result","User Agent"].map(h=><th key={h} style={{ padding:"7px 10px", textAlign:"left", fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>{h}</th>)}
              </tr></thead>
              <tbody>
                {history.slice(0,20).map((h,i)=>(
                  <tr key={i} style={{ borderBottom:"0.5px solid #f1f5f9" }}>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:10, color:"#94a3b8" }}>{h.AttemptedAt?parseUTC(h.AttemptedAt)?.toLocaleString():"—"}</td>
                    <td style={{ padding:"7px 10px", fontFamily:"monospace", fontSize:11 }}>{h.IPAddress||"—"}</td>
                    <td style={{ padding:"7px 10px" }}>{h.WasSuccessful?<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#dcfce7", color:"#166534" }}>Success</span>:<span style={{ fontSize:10, borderRadius:4, padding:"1px 6px", background:"#fca5a5", color:"#991b1b" }}>Failed</span>}</td>
                    <td style={{ padding:"7px 10px", fontSize:10, color:"#94a3b8", maxWidth:180, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{h.UserAgent||"—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </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 35s 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()} \u00b7 {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>
  );
}

// ═══ AGENTS VIEW ═══
function AgentsView({ api }) {
  const [agents, setAgents]               = useState([]);
  const [loading, setLoading]             = useState(true);
  const [err, setErr]                     = useState("");
  const [showModal, setShowModal]         = useState(false);
  const [regForm, setRegForm]             = useState({ name: "", description: "" });
  const [regResult, setRegResult]         = useState(null);
  const [registering, setRegistering]     = useState(false);
  const [regErr, setRegErr]               = useState("");
  const [copied, setCopied]               = useState(false);
  const [shownKeyId, setShownKeyId]       = useState(null);
  const [shownKey, setShownKey]           = useState("");
  const [keyCopied, setKeyCopied]         = useState(false);

  const loadAgents = useCallback(() => {
    api("GET", "/agents")
      .then(r => { setAgents(Array.isArray(r) ? r : []); setErr(""); })
      .catch(e => setErr(e.message || "Failed to load agents"))
      .finally(() => setLoading(false));
  }, [api]);

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

  const relTime = (ts) => {
    if (!ts) return "Never";
    const d = parseUTC(ts);
    if (!d) return "Never";
    const secs = Math.floor((Date.now() - d.getTime()) / 1000);
    if (secs < 60) return `${secs}s ago`;
    if (secs < 3600) return `${Math.floor(secs / 60)}m ago`;
    if (secs < 86400) return `${Math.floor(secs / 3600)}h ago`;
    return `${Math.floor(secs / 86400)}d ago`;
  };

  const isOnline = (ts) => {
    if (!ts) return false;
    const d = parseUTC(ts);
    if (!d) return false;
    return (Date.now() - d.getTime()) < 5 * 60 * 1000;
  };

  const handleRegister = async () => {
    if (!regForm.name.trim()) { setRegErr("Host name is required."); return; }
    setRegistering(true); setRegErr("");
    try {
      const r = await api("POST", "/agents", { Name: regForm.name.trim(), Description: regForm.description.trim() });
      setRegResult({ agentHostId: r.AgentHostId, name: r.Name, apiKey: r.ApiKey });
    } catch (e) {
      setRegErr(e.message || "Registration failed.");
    } finally {
      setRegistering(false);
    }
  };

  const handleDownload = (apiKey) => {
    const a = document.createElement("a");
    a.href = `/api/agent/download?key=${encodeURIComponent(apiKey)}`;
    a.click();
  };

  const handleRedownload = (agent) => {
    window.open('/api/agents/download', '_blank');
  };

  const handleRevoke = async (agentHostId) => {
    if (!confirm("Revoke this agent host? The agent will stop being able to check in. This cannot be undone.")) return;
    try {
      await api("DELETE", `/agents/${agentHostId}`);
      if (shownKeyId === agentHostId) { setShownKeyId(null); setShownKey(""); }
      loadAgents();
    } catch (e) {
      alert("Revoke failed: " + (e.message || "Unknown error"));
    }
  };

  const handleShowKey = async (agentHostId) => {
    if (shownKeyId === agentHostId) { setShownKeyId(null); setShownKey(""); return; }
    try {
      const r = await api("GET", `/agents/${agentHostId}/key`);
      setShownKey(r.apiKey || r.ApiKey || "");
      setShownKeyId(agentHostId);
      setKeyCopied(false);
    } catch (e) {
      alert("Failed to retrieve key: " + (e.message || "Unknown error"));
    }
  };

  const handleKeyCopy = () => {
    if (shownKey) {
      navigator.clipboard.writeText(shownKey).catch(() => {});
      setKeyCopied(true);
      setTimeout(() => setKeyCopied(false), 2000);
    }
  };

  const handleCopy = () => {
    if (regResult?.apiKey) {
      navigator.clipboard.writeText(regResult.apiKey).catch(() => {});
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }
  };

  const handleCloseModal = () => {
    setShowModal(false);
    setRegForm({ name: "", description: "" });
    setRegResult(null);
    setRegErr("");
    setCopied(false);
    if (regResult) loadAgents();
  };

  // ── Agent card ──
  const agentCards = agents.map(agent => {
    const online = isOnline(agent.LastSeenAt);
    const neverSeen = !agent.LastSeenAt;
    const statusPill = neverSeen
      ? { label: "Never contacted", bg: "#9CA3AF22", color: "#6B7280", border: "#9CA3AF35" }
      : online
        ? { label: "Healthy", bg: "#DCF2EA", color: "#008C6F", border: "#008C6F35" }
        : { label: "Offline", bg: "#FAEAEA", color: "#D95C5C", border: "#D95C5C35" };

    const lastSeenColor = neverSeen ? "#9CA3AF" : online ? "#008C6F" : "#D95C5C";
    const keyPreview = agent.ApiKey
      ? `${agent.ApiKey.slice(0, 6)}...${agent.ApiKey.slice(-4)}`
      : "—";

    return (
      <div key={agent.AgentHostId} style={{ background: "#ffffff", border: `1px solid ${c.border}`, borderRadius: 8, marginBottom: 14, opacity: online || neverSeen ? 1 : 0.75 }}>

        {/* Row 1 — Header */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 16px", borderBottom: `1px solid ${c.border}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
            <div style={{ width: 36, height: 36, borderRadius: 8, background: "#006D8C", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#ffffff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <rect x="2" y="3" width="20" height="14" rx="2" />
                <line x1="8" y1="21" x2="16" y2="21" />
                <line x1="12" y1="17" x2="12" y2="21" />
              </svg>
            </div>
            <div>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
                <span style={{ fontSize: 13, fontWeight: 700, color: c.text }}>{agent.Name}</span>
                <span style={{ fontSize: 10, fontWeight: 600, padding: "1px 7px", borderRadius: 10, background: statusPill.bg, color: statusPill.color, border: `1px solid ${statusPill.border}` }}>{statusPill.label}</span>
              </div>
              <div style={{ fontSize: 11, color: c.textDimmer }}>
                {agent.Platform || "Windows"} · {agent.IpAddress || "IP unknown"} · Agent v{agent.Version || "not yet connected"}
              </div>
            </div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span style={{ fontSize: 10, fontWeight: 600, padding: "2px 9px", borderRadius: 10, background: "#E0F2F7", color: "#006D8C", border: "1px solid #006D8C35" }}>Agent</span>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "5px 10px" }} onClick={() => alert("Coming soon")}>Configure</Btn>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "5px 10px" }} onClick={() => handleRedownload(agent)}>↓ Re-download</Btn>
          </div>
        </div>

        {/* Row 2 — Stats bar */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr" }}>
          {[
            { label: "Monitors Assigned", value: agent.MonitorCount ?? 0, color: c.text },
            { label: "Healthy", value: agent.MonitorCount ?? 0, color: "#008C6F" },
            { label: "Check Interval", value: "60s", color: c.textDimmer },
            { label: "Last Check-in", value: relTime(agent.LastSeenAt), color: lastSeenColor },
          ].map((stat, i) => (
            <div key={stat.label} style={{ padding: "10px 16px", borderRight: i < 3 ? `1px solid ${c.border}` : "none", textAlign: "center" }}>
              <div style={{ fontSize: 15, fontWeight: 700, color: stat.color, fontFamily: "monospace" }}>{stat.value}</div>
              <div style={{ fontSize: 10, color: c.textDimmer, textTransform: "uppercase", letterSpacing: "0.05em", marginTop: 2 }}>{stat.label}</div>
            </div>
          ))}
        </div>

        {/* Row 3 — Footer */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 16px", background: "#F0F7FB", borderTop: `1px solid ${c.border}`, borderRadius: shownKeyId === agent.AgentHostId ? "0" : "0 0 8px 8px" }}>
          <span style={{ fontSize: 11, color: c.textDimmer }}>
            Last seen: <span style={{ color: lastSeenColor }}>{relTime(agent.LastSeenAt)}</span>
            {" · "}Key: <span style={{ fontFamily: "monospace" }}>{keyPreview}</span>
          </span>
          <div style={{ display: "flex", gap: 6 }}>
            <Btn variant="secondary" style={{ fontSize: 11, padding: "4px 10px" }} onClick={() => handleShowKey(agent.AgentHostId)}>{shownKeyId === agent.AgentHostId ? "Hide Key" : "Show Key"}</Btn>
            <Btn variant="danger" style={{ fontSize: 11, padding: "4px 10px" }} onClick={() => handleRevoke(agent.AgentHostId)}>Revoke</Btn>
          </div>
        </div>

        {/* Row 4 — Key reveal (inline, only when shown) */}
        {shownKeyId === agent.AgentHostId && (
          <div style={{ padding: "12px 16px", borderTop: `1px solid ${c.border}`, background: "#FAFAFA", borderRadius: "0 0 8px 8px" }}>
            <div style={{ fontSize: 10, color: c.textDimmer, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }}>API Key</div>
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <div style={{ flex: 1, background: "#F0F7FB", border: `1px solid ${c.border}`, borderRadius: 4, padding: "7px 10px", fontFamily: "monospace", fontSize: 11, color: c.text, wordBreak: "break-all", userSelect: "all" }}>{shownKey}</div>
              <Btn variant="secondary" style={{ fontSize: 11, padding: "6px 12px", flexShrink: 0 }} onClick={handleKeyCopy}>{keyCopied ? "Copied ✓" : "Copy"}</Btn>
            </div>
          </div>
        )}
      </div>
    );
  });

  // ── Empty state ──
  const emptyState = (
    <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "60px 20px", textAlign: "center" }}>
      <div style={{ border: `2px dashed ${c.border}`, borderRadius: 12, padding: "40px 48px", maxWidth: 520 }}>
        <div style={{ width: 48, height: 48, borderRadius: 10, background: "#E0F2F7", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }}>
          <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#006D8C" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="2" y="3" width="20" height="14" rx="2" />
            <line x1="8" y1="21" x2="16" y2="21" />
            <line x1="12" y1="17" x2="12" y2="21" />
          </svg>
        </div>
        <div style={{ fontSize: 15, fontWeight: 700, color: c.text, marginBottom: 8 }}>No agent hosts registered</div>
        <div style={{ fontSize: 12, color: c.textDimmer, lineHeight: 1.7, marginBottom: 20 }}>
          Register an agent host to begin monitoring machines behind firewalls or on remote networks. Download the agent, install it as a Windows service, and it will connect automatically.
        </div>
        <Btn onClick={() => setShowModal(true)}>+ Register Agent Host</Btn>
      </div>
    </div>
  );

  // ── Register modal ──
  const modalContent = showModal && (
    <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.75)", zIndex: 500, display: "flex", alignItems: "center", justifyContent: "center" }}
      onClick={e => { if (e.target === e.currentTarget) handleCloseModal(); }}>
      <div style={{ background: "#ffffff", borderRadius: 12, padding: 24, width: 480, maxWidth: "calc(100vw - 32px)", boxShadow: "0 20px 60px rgba(0,0,0,0.35)", animation: "fadeIn 0.2s ease" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 20 }}>
          <span style={{ fontSize: 15, fontWeight: 700, color: c.text }}>Register Agent Host</span>
          <button onClick={handleCloseModal} style={{ background: "none", border: "none", cursor: "pointer", color: c.textDimmer, fontSize: 20, lineHeight: 1, padding: "0 0 0 8px" }}>×</button>
        </div>

        {!regResult ? (
          <div>
            {regErr && <ErrBox msg={regErr} />}
            <div style={{ display: "flex", flexDirection: "column", gap: 14, marginBottom: 20 }}>
              <Input label="HOST NAME" placeholder="e.g. Sheldon, Office-Server-01" value={regForm.name} onChange={e => setRegForm(f => ({ ...f, name: e.target.value }))} onKeyDown={e => e.key === "Enter" && handleRegister()} />
              <Input label="DESCRIPTION" placeholder="e.g. Home lab machine, Houston office server" value={regForm.description} onChange={e => setRegForm(f => ({ ...f, description: e.target.value }))} />
            </div>
            <Btn loading={registering} onClick={handleRegister} style={{ width: "100%", padding: "10px 16px", fontSize: 13 }}>Generate &amp; Register</Btn>
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", gap: 12 }}>
            <div style={{ width: 32, height: 32, borderRadius: "50%", background: "#DCF2EA", display: "flex", alignItems: "center", justifyContent: "center" }}>
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#008C6F" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                <polyline points="20 6 9 17 4 12" />
              </svg>
            </div>
            <div style={{ fontSize: 14, fontWeight: 700, color: c.text }}>Agent host registered</div>
            <div style={{ fontSize: 11, color: "#B45309", background: "#FEF3C7", border: "1px solid #FDE68A", borderRadius: 6, padding: "8px 12px", width: "100%" }}>
              Your API key has been generated. Copy it now — it will not be shown again.
            </div>
            <div style={{ background: "#F0F7FB", borderRadius: 6, padding: 12, width: "100%", textAlign: "left" }}>
              <div style={{ fontFamily: "monospace", fontSize: 11, color: c.text, wordBreak: "break-all", userSelect: "all" }}>{regResult.apiKey}</div>
            </div>
            <div style={{ display: "flex", gap: 8, width: "100%" }}>
              <Btn variant="secondary" onClick={handleCopy} style={{ flex: 1, fontSize: 12 }}>{copied ? "Copied ✓" : "Copy Key"}</Btn>
            </div>
            <div style={{ width: "100%", height: 1, background: c.border, margin: "4px 0" }} />
            <div style={{ fontSize: 11, color: c.textDimmer, textAlign: "left", width: "100%" }}>
              Extract both files to the same folder. Run <span style={{ fontFamily: "monospace" }}>OP1AgentSetup.exe</span> as Administrator.
            </div>
            <Btn onClick={() => handleDownload(regResult.apiKey)} style={{ width: "100%", fontSize: 12, padding: "10px 16px" }}>↓ Download Agent Setup</Btn>
            <Btn variant="secondary" onClick={handleCloseModal} style={{ width: "100%", fontSize: 12 }}>Close</Btn>
          </div>
        )}
      </div>
    </div>
  );

  return (
    <div style={{ 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", 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.0.236</div>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <div style={{ textAlign: "center" }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: "#1e293b" }}>{agents.length}</div>
            <div style={{ fontSize: 9, color: "#94a3b8", textTransform: "uppercase", letterSpacing: "0.06em" }}>Agent Hosts</div>
          </div>
          <div style={{ width: "0.5px", height: 28, background: "#e2e5ea" }} />
          <div style={{ textAlign: "center" }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: "#008C6F" }}>{agents.filter(a => isOnline(a.LastSeenAt)).length}</div>
            <div style={{ fontSize: 9, color: "#94a3b8", textTransform: "uppercase", letterSpacing: "0.06em" }}>Online</div>
          </div>
          <div style={{ width: "0.5px", height: 28, background: "#e2e5ea" }} />
          <Btn onClick={() => setShowModal(true)} style={{ fontSize: 12, padding: "7px 14px" }}>+ Register Agent Host</Btn>
        </div>
      </div>

      {/* Subtitle bar */}
      <div style={{ background: "#F0F7FB", borderBottom: `1px solid ${c.border}`, padding: "6px 20px", flexShrink: 0 }}>
        <span style={{ fontSize: 11, color: c.textDimmer, fontWeight: 500 }}>Agents · Agent-based host monitoring</span>
      </div>

      {/* Main content */}
      <div style={{ flex: 1, overflowY: "auto", padding: 20, background: "#F0F7FB" }}>
        {loading && <Spinner />}
        {!loading && err && <ErrBox msg={err} />}
        {!loading && !err && agents.length === 0 && emptyState}
        {!loading && !err && agents.length > 0 && agentCards}
      </div>

      {/* Modal */}
      {modalContent}
    </div>
  );
}

// ═══ SYNTHETIC USER FLOWS ═══

function SyntheticView({ api, canManage, initialNav, onNavConsumed }) {
  const [view, setView] = useState("list");
  const [editFlowId, setEditFlowId] = useState(null);
  const [resultsFlowId, setResultsFlowId] = useState(null);
  const [targetResultId, setTargetResultId] = useState(null);

  useEffect(() => {
    if (!initialNav?.editFlowId) return;
    setEditFlowId(initialNav.editFlowId);
    setView("edit");
    if (onNavConsumed) onNavConsumed();
  }, [initialNav?.editFlowId]);

  useEffect(() => {
    if (!initialNav?.resultsFlowId) return;
    setResultsFlowId(initialNav.resultsFlowId);
    setView("results");
    if (initialNav.resultsResultId) setTargetResultId(initialNav.resultsResultId);
    if (initialNav.autodiagnose) setSynthAutodiagnose(true);
    if (onNavConsumed) onNavConsumed();
  }, [initialNav?.resultsFlowId]);

  // ── List state ──
  const [flows, setFlows] = useState([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState(null);
  const [ok, setOk] = useState(null);
  const [runningId, setRunningId] = useState(null);

  const loadFlows = useCallback(() => {
    setLoading(true); setErr(null);
    api("GET", "/synthetic/flows")
      .then(r => setFlows(Array.isArray(r) ? r : []))
      .catch(e => setErr(e.message))
      .finally(() => setLoading(false));
  }, [api]);

  useEffect(() => { if (view === "list") loadFlows(); }, [view, loadFlows]);

  // ── Edit state ──
  const [flowDraft, setFlowDraft] = useState(null);
  const [steps, setSteps] = useState([]);
  const [editLoading, setEditLoading] = useState(false);
  const [editErr, setEditErr] = useState(null);
  const [saving, setSaving] = useState(false);
  const [stepErrors, setStepErrors] = useState({});
  const [editGroups, setEditGroups] = useState([]);

  useEffect(() => {
    if (view !== "edit") return;
    api("GET", "/monitors/groups")
      .then(r => setEditGroups(Array.isArray(r) ? r.sort((a,b)=>a.GroupName.localeCompare(b.GroupName)) : []))
      .catch(() => setEditGroups([]));
  }, [view, api]);

  // ── Results state ──
  const [results, setResults] = useState([]);
  const [selectedResult, setSelectedResult] = useState(null);
  const [resultsLoading, setResultsLoading] = useState(false);
  const [resultsErr, setResultsErr] = useState(null);

  // ── Diagnosis state ──
  const [synthDiagnosis, setSynthDiagnosis] = useState(null);
  const [synthDiagnosing, setSynthDiagnosing] = useState(false);
  const [synthDiagError, setSynthDiagError] = useState(null);
  const [synthDiagThread, setSynthDiagThread] = useState([]);
  const [synthDiagFollowUp, setSynthDiagFollowUp] = useState('');
  const [synthDiagSending, setSynthDiagSending] = useState(false);
  const [synthAutodiagnose, setSynthAutodiagnose] = useState(false);

  useEffect(() => {
    if (view !== "edit") return;
    if (editFlowId === null) {
      setFlowDraft({ FlowName:"", GroupName:"", Description:"", IsEnabled:true, ScheduleIntervalMinutes:5, ExecutionTarget:"server", AgentHostId:null, BaselineDays:30, DurationWarnMs:3000, DurationCritMs:6000, RetentionDays:14 });
      setSteps([]); setEditErr(null);
      return;
    }
    setEditLoading(true); setEditErr(null);
    api("GET", `/synthetic/flows/${editFlowId}`)
      .then(r => {
        if (!r) { setEditErr("Flow not found."); return; }
        setFlowDraft({ FlowName:r.FlowName, GroupName:r.GroupName||"", Description:r.Description||"", IsEnabled:r.IsEnabled, ScheduleIntervalMinutes:r.ScheduleIntervalMinutes, ExecutionTarget:r.ExecutionTarget, AgentHostId:r.AgentHostId, BaselineDays:r.BaselineDays, DurationWarnMs:r.DurationWarnMs, DurationCritMs:r.DurationCritMs, RetentionDays:r.RetentionDays });
        setSteps((r.Steps||[]).map((s,i) => ({ ...s, _key: i })));
      })
      .catch(e => setEditErr(e.message))
      .finally(() => setEditLoading(false));
  }, [view, editFlowId, api]);

  useEffect(() => {
    if (view !== "results" || !resultsFlowId) return;
    setResultsLoading(true); setResultsErr(null);
    api("GET", `/synthetic/flows/${resultsFlowId}/results`)
      .then(r => {
        const list = Array.isArray(r) ? r : [];
        setResults(list);
        const target = targetResultId ? list.find(x => x.ResultId === targetResultId) : null;
        setSelectedResult(target || list[0] || null);
        setTargetResultId(null);
      })
      .catch(e => setResultsErr(e.message))
      .finally(() => setResultsLoading(false));
  }, [view, resultsFlowId, api]);

  const fmtDur = ms => ms == null ? "—" : ms < 1000 ? `${ms}ms` : `${(ms/1000).toFixed(1)}s`;
  const fmtDate = dt => { const d = parseUTC(dt); return d ? d.toLocaleString() : "—"; };
  const uptimeColor = pct => pct == null ? "#9CA3AF" : pct >= 99 ? c.green : pct >= 95 ? c.amber : c.red;

  const runFlow = async (flowId) => {
    setRunningId(flowId);
    try {
      await api("POST", `/synthetic/flows/${flowId}/run`);
      setOk(`Flow ${flowId} started.`); setTimeout(() => setOk(null), 3000);
    } catch(e) { setErr(e.message); }
    finally { setRunningId(null); }
  };

  const toggleEnabled = async (flow) => {
    try {
      const detail = await api("GET", `/synthetic/flows/${flow.FlowId}`);
      if (!detail) return;
      await api("PUT", `/synthetic/flows/${flow.FlowId}`, { ...detail, IsEnabled: !flow.IsEnabled, Steps: detail.Steps||[] });
      setFlows(prev => prev.map(f => f.FlowId === flow.FlowId ? { ...f, IsEnabled: !f.IsEnabled } : f));
    } catch(e) { setErr(e.message); }
  };

  const deleteFlow = async (flowId) => {
    if (!confirm("Delete this flow?")) return;
    try {
      await api("DELETE", `/synthetic/flows/${flowId}`);
      setFlows(prev => prev.filter(f => f.FlowId !== flowId));
      setOk("Flow deleted."); setTimeout(() => setOk(null), 3000);
    } catch(e) { setErr(e.message); }
  };

  const synthDiagPanelRef = React.useRef(null);

  const runSynthDiagnosis = useCallback(async (followUp = null) => {
    if (!selectedResult?.ResultId) return;
    setSynthDiagnosing(true);
    if (synthDiagPanelRef.current) { setTimeout(() => synthDiagPanelRef.current?.scrollIntoView({behavior:'smooth', block:'nearest'}), 50); }
    try {
      const data = await api("POST", `/synthetic/diagnose/${selectedResult.ResultId}`, { followUp });
      if (data && data.diagnosis) {
        if (!followUp) {
          setSynthDiagnosis(data.diagnosis);
          setSynthDiagThread([{ role:'assistant', content:data.diagnosis }]);
        } else {
          setSynthDiagThread(prev => [...prev, { role:'user', content:followUp }, { role:'assistant', content:data.diagnosis }]);
        }
      } else {
        setSynthDiagError((data && data.error) || 'Diagnosis failed');
      }
    } catch(e) {
      setSynthDiagError(e.message);
    } finally {
      setSynthDiagnosing(false);
      setSynthDiagSending(false);
      setSynthDiagFollowUp('');
    }
  }, [api, selectedResult?.ResultId]);

  // Reset diagnosis state when selected result changes
  useEffect(() => {
    setSynthDiagnosis(null);
    setSynthDiagThread([]);
    setSynthDiagError(null);
    setSynthDiagFollowUp('');
    setSynthDiagnosing(false);
  }, [selectedResult?.ResultId]);

  // Auto-fire diagnosis when navigated from Alert Inspector
  useEffect(() => {
    if (!synthAutodiagnose || !selectedResult?.ResultId || synthDiagnosis || synthDiagnosing) return;
    setSynthAutodiagnose(false);
    runSynthDiagnosis();
  }, [synthAutodiagnose, selectedResult?.ResultId, synthDiagnosis, synthDiagnosing, runSynthDiagnosis]);

  const saveFlow = async () => {
    if (!flowDraft?.FlowName?.trim()) { setEditErr("Flow name is required."); return; }
    const errs = {};
    steps.forEach((s, i) => { if (s.StepType?.toLowerCase() === "navigate" && !s.TargetUrl?.trim()) errs[i] = "Navigate step requires a URL"; });
    setStepErrors(errs);
    if (Object.keys(errs).length > 0) return;
    setStepErrors({});
    setSaving(true); setEditErr(null);
    try {
      const body = { ...flowDraft, Steps: steps.map((s,i) => ({ StepOrder:i+1, StepType:s.StepType, Label:s.Label||null, TargetUrl:s.TargetUrl||null, FieldLabel:s.FieldLabel||null, FieldValue:s.FieldValue||null, ButtonText:s.ButtonText||null, AssertType:s.AssertType||null, AssertValue:s.AssertValue||null, ExtractLabel:s.ExtractLabel||null, ExtractAs:s.ExtractAs||null, WaitForLoad:s.WaitForLoad!==false, TimeoutMs:s.TimeoutMs||10000, WarnMs:s.WarnMs||null, CritMs:s.CritMs||null })) };
      if (editFlowId === null) { await api("POST", "/synthetic/flows", body); } else { await api("PUT", `/synthetic/flows/${editFlowId}`, body); }
      setOk("Flow saved."); setTimeout(() => setOk(null), 3000);
      setView("list");
    } catch(e) { setEditErr(e.message); }
    finally { setSaving(false); }
  };

  const addStep = (type) => setSteps(prev => [...prev, { _key:Date.now(), StepType:type, StepOrder:prev.length+1, Label:"", TargetUrl:"", FieldLabel:"", FieldValue:"", ButtonText:"", AssertType:"contains_text", AssertValue:"", ExtractLabel:"", ExtractAs:"", WaitForLoad:true, TimeoutMs:10000, WarnMs:null, CritMs:null }]);
  const removeStep = (key) => setSteps(prev => prev.filter(s => s._key !== key));
  const updateStep = (key, field, val) => setSteps(prev => prev.map(s => s._key === key ? { ...s, [field]:val } : s));
  const moveStep = (key, dir) => setSteps(prev => {
    const idx = prev.findIndex(s => s._key === key); const next = [...prev]; const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return prev;
    [next[idx], next[swap]] = [next[swap], next[idx]]; return next;
  });

  // ══ LIST VIEW ══
  const FLOW_COL_GRID = "24px 1fr 80px 50px 160px 70px 80px 56px 180px";

  const listView = (() => {
    const total = flows.length;
    const passing = flows.filter(f => f.LastRunPassed === true).length;
    const failing = flows.filter(f => f.LastRunPassed === false).length;
    const durVals = flows.map(f => f.LastDurationMs).filter(v => v != null);
    const avgDur = durVals.length ? Math.round(durVals.reduce((a,b)=>a+b,0)/durVals.length) : null;
    const uptVals = flows.map(f => f.UptimePct30d).filter(v => v != null);
    const avgUpt = uptVals.length ? (uptVals.reduce((a,b)=>a+b,0)/uptVals.length).toFixed(1) : null;

    const statBar = (
      <div style={{ display:"flex", gap:0, background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", flexShrink:0, alignItems:"center" }}>
        {[["TOTAL FLOWS", total, "#1A1A1A"], ["PASSING", passing, c.green], ["FAILING", failing, failing>0?c.red:"#9CA3AF"], ["AVG DURATION", avgDur!=null?fmtDur(avgDur):"—", "#1A1A1A"], ["AVG UPTIME 30D", avgUpt!=null?`${avgUpt}%`:"—", uptimeColor(avgUpt?parseFloat(avgUpt):null)]].map(([label,val,color]) => (
          <div key={label} style={{ display:"flex", flexDirection:"column", alignItems:"flex-start", minWidth:110, padding:"4px 20px 4px 0", borderRight:`1px solid ${c.borderLight}`, marginRight:20 }}>
            <div style={{ fontSize:9, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:2 }}>{label}</div>
            <div style={{ fontSize:20, fontWeight:700, color, lineHeight:1 }}>{val}</div>
          </div>
        ))}
        <div style={{ marginLeft:"auto" }}>
          {canManage && <Btn variant="primary" onClick={() => { setEditFlowId(null); setView("edit"); }}>+ New Flow</Btn>}
        </div>
      </div>
    );

    const groups = {};
    flows.forEach(f => { const g = f.GroupName||""; if (!groups[g]) groups[g]=[]; groups[g].push(f); });
    const groupNames = Object.keys(groups).sort((a,b) => a===""?1:b===""?-1:a.localeCompare(b));

    const hCell = { fontSize:10, textTransform:"uppercase", color:"#4A4A4A", fontWeight:500, letterSpacing:"0.04em", padding:"5px 8px", overflow:"hidden" };
    const subHeader = (
      <div style={{ display:"grid", gridTemplateColumns:FLOW_COL_GRID, background:"#FAF8F4", borderBottom:"1.5px solid #D8CCBA" }}>
        <div style={hCell}></div>
        <div style={hCell}>Name</div>
        <div style={hCell}>Target</div>
        <div style={hCell}>Steps</div>
        <div style={hCell}>Last Run</div>
        <div style={hCell}>Duration</div>
        <div style={hCell}>Uptime 30d</div>
        <div style={{ ...hCell, textAlign:"center" }}>On</div>
        <div style={hCell}>Actions</div>
      </div>
    );

    const flowRow = (f) => {
      const dotColor = f.LastRunPassed==null?"#9CA3AF":f.LastRunPassed?c.green:c.red;
      const durColor = f.LastDurationMs==null?"#9CA3AF":f.LastDurationMs>6000?c.red:f.LastDurationMs>3000?c.amber:c.green;
      return (
        <div key={f.FlowId} style={{ display:"grid", gridTemplateColumns:FLOW_COL_GRID, alignItems:"center", padding:"3px 0", borderBottom:`1px solid #F0F4F8`, background:"#FFFFFF", fontSize:12 }}>
          <div style={{ display:"flex", justifyContent:"center" }}>
            <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:dotColor }} />
          </div>
          <div style={{ padding:"0 8px", fontWeight:500, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>
            <span style={{ cursor:"pointer", color:c.blue }} onClick={() => { setResultsFlowId(f.FlowId); setView("results"); }}>{f.FlowName}</span>
          </div>
          <div style={{ padding:"0 8px", color:"#64748b", fontSize:11 }}>{f.ExecutionTarget==="server"?"Server":"Agent"}</div>
          <div style={{ padding:"0 8px", color:"#64748b" }}>{f.StepCount}</div>
          <div style={{ padding:"0 8px", color:"#64748b", fontSize:11 }}>{f.LastRunAt?new Date(f.LastRunAt).toLocaleString():"Never"}</div>
          <div style={{ padding:"0 8px", color:durColor, fontFamily:"monospace" }}>{fmtDur(f.LastDurationMs)}</div>
          <div style={{ padding:"0 8px", color:uptimeColor(f.UptimePct30d), fontFamily:"monospace" }}>{f.UptimePct30d!=null?`${f.UptimePct30d}%`:"—"}</div>
          <div style={{ display:"flex", justifyContent:"center" }}>
            <SmallToggle checked={f.IsEnabled} onClick={() => toggleEnabled(f)} />
          </div>
          <div style={{ display:"flex", gap:3, padding:"0 4px", alignItems:"center" }}>
            <Btn variant="secondary" disabled={runningId===f.FlowId} onClick={() => runFlow(f.FlowId)} style={{ padding:"3px 6px", fontSize:11, minWidth:24, textAlign:"center" }} title="Run now">▶</Btn>
            <Btn variant="secondary" onClick={() => { setResultsFlowId(f.FlowId); setView("results"); }} style={{ padding:"3px 6px", fontSize:11 }}>Res</Btn>
            {canManage && <Btn variant="secondary" onClick={() => { setEditFlowId(f.FlowId); setView("edit"); }} style={{ padding:"3px 6px", fontSize:11 }}>Edit</Btn>}
            {canManage && <Btn variant="danger" onClick={() => deleteFlow(f.FlowId)} style={{ padding:"3px 6px", fontSize:11, marginLeft:12 }}>Del</Btn>}
          </div>
        </div>
      );
    };

    const groupPanels = groupNames.map(g => {
      const gFlows = groups[g]; const gName = g||"Ungrouped";
      const gPassing = gFlows.filter(f => f.LastRunPassed===true).length; const gTotal = gFlows.length;
      const gColor = gPassing===gTotal&&gTotal>0?c.green:gFlows.some(f=>f.LastRunPassed===false)?c.red:"#9CA3AF";
      return (
        <div key={g} style={{ marginBottom:12, border:`1px solid #D8CCBA`, borderRadius:6, overflow:"hidden" }}>
          <div style={{ display:"flex", alignItems:"center", background:"#EDE8DF", borderLeft:`3px solid ${gColor}`, padding:"6px 12px", gap:8 }}>
            <span style={{ fontWeight:600, fontSize:12, color:"#1A1A1A", flex:1 }}>{gName}</span>
            <span style={{ fontSize:11, color:"#64748b" }}>{gPassing}/{gTotal} passing</span>
          </div>
          {subHeader}
          {gFlows.map(f => flowRow(f))}
        </div>
      );
    });

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        {statBar}
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"6px 20px", flexShrink:0 }}>
          <span style={{ fontSize:11, color:c.textDimmer, fontWeight:500 }}>Synthetic Flows · Browser-based user flow monitoring</span>
        </div>
        <div style={{ flex:1, overflowY:"auto", padding:20, background:"#FFFFFF" }}>
          {loading && <Spinner />}
          {!loading && err && <ErrBox msg={err} />}
          {ok && <div style={{ background:"#DCF2EA", border:`1px solid ${c.green}`, borderRadius:6, padding:"8px 14px", marginBottom:12, fontSize:12, color:c.green, fontWeight:500 }}>{ok}</div>}
          {!loading && !err && flows.length===0 && (
            <div style={{ textAlign:"center", padding:"60px 20px", color:"#94a3b8" }}>
              <div style={{ fontSize:32, marginBottom:12 }}>🌐</div>
              <div style={{ fontSize:16, fontWeight:600, marginBottom:8, color:"#1e293b" }}>No synthetic flows yet</div>
              <div style={{ fontSize:12, marginBottom:20 }}>Create a flow to start monitoring user journeys</div>
              {canManage && <Btn variant="primary" onClick={() => { setEditFlowId(null); setView("edit"); }}>+ Create First Flow</Btn>}
            </div>
          )}
          {!loading && !err && flows.length>0 && groupPanels}
        </div>
      </div>
    );
  })();

  // ══ EDIT VIEW ══
  const editView = (() => {
    if (!flowDraft) return editLoading ? <div style={{ padding:32 }}><Spinner /></div> : <ErrBox msg={editErr||"Loading…"} />;
    const fd = flowDraft;
    const setFd = (patch) => setFlowDraft(p => ({ ...p, ...(typeof patch==="function"?patch(p):patch) }));
    const inpS = { width:"100%", padding:"6px 10px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, background:"#fff", color:"#1e293b", boxSizing:"border-box" };
    const lblS = { fontSize:11, fontWeight:600, color:"#64748b", marginBottom:3, display:"block" };
    const sxS = { padding:"6px 10px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:12, background:"#fff", color:"#1e293b" };
    const stepTypeLabel = { navigate:"Navigate", fill:"Fill Field", click:"Click Button", assert:"Assert", extract:"Extract" };

    const stepEditor = (s, idx) => {
      const type = s.StepType;
      const fields = (() => {
        if (type==="navigate") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
            <div><label style={lblS}>URL *</label><input style={inpS} value={s.TargetUrl||""} onChange={e=>{ updateStep(s._key,"TargetUrl",e.target.value); if(stepErrors[idx]) setStepErrors(prev=>{ const n={...prev}; delete n[idx]; return n; }); }} placeholder="https://example.com" /></div>
            <div><label style={lblS}>Label</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Open login page" /></div>
          </div>
        );
        if (type==="fill") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Field Label *</label><input style={inpS} value={s.FieldLabel||""} onChange={e=>updateStep(s._key,"FieldLabel",e.target.value)} placeholder="Username" /></div>
            <div><label style={lblS}>Value *</label><input style={inpS} value={s.FieldValue||""} onChange={e=>updateStep(s._key,"FieldValue",e.target.value)} placeholder={"admin or {{vault.cred}}"} /></div>
            <div><label style={lblS}>Label</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Enter username" /></div>
          </div>
        );
        if (type==="click") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Button Text *</label><input style={inpS} value={s.ButtonText||""} onChange={e=>updateStep(s._key,"ButtonText",e.target.value)} placeholder="Log In" /></div>
            <div><label style={lblS}>Label</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Click login button" /></div>
          </div>
        );
        if (type==="assert") return (
          <div style={{ display:"grid", gridTemplateColumns:"160px 1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Assert Type *</label>
              <select style={{ ...sxS, width:"100%" }} value={s.AssertType||"contains_text"} onChange={e=>updateStep(s._key,"AssertType",e.target.value)}>
                <option value="contains_text">Contains Text</option>
                <option value="element_visible">Element Visible</option>
                <option value="url_contains">URL Contains</option>
                <option value="status_ok">Status OK</option>
              </select>
            </div>
            <div><label style={lblS}>Expected Value *</label><input style={inpS} value={s.AssertValue||""} onChange={e=>updateStep(s._key,"AssertValue",e.target.value)} placeholder="Welcome, admin" /></div>
            <div><label style={lblS}>Label</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Verify login" /></div>
          </div>
        );
        if (type==="extract") return (
          <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr", gap:8 }}>
            <div><label style={lblS}>Field Label *</label><input style={inpS} value={s.ExtractLabel||""} onChange={e=>updateStep(s._key,"ExtractLabel",e.target.value)} placeholder="Element label" /></div>
            <div><label style={lblS}>Store As *</label><input style={inpS} value={s.ExtractAs||""} onChange={e=>updateStep(s._key,"ExtractAs",e.target.value)} placeholder="extracted_value" /></div>
            <div><label style={lblS}>Label</label><input style={inpS} value={s.Label||""} onChange={e=>updateStep(s._key,"Label",e.target.value)} placeholder="Extract page title" /></div>
          </div>
        );
        return null;
      })();
      return (
        <div key={s._key} style={{ border:`1px solid ${c.border}`, borderRadius:6, background:"#fff", marginBottom:8, overflow:"hidden" }}>
          <div style={{ display:"flex", alignItems:"center", background:"#F8FAFC", padding:"6px 12px", gap:8, borderBottom:`1px solid ${c.border}` }}>
            <span style={{ width:20, height:20, background:c.blue, color:"#fff", borderRadius:"50%", display:"inline-flex", alignItems:"center", justifyContent:"center", fontSize:10, fontWeight:700, flexShrink:0 }}>{idx+1}</span>
            <span style={{ fontSize:11, fontWeight:600, color:c.blue, flex:1 }}>{stepTypeLabel[type]||type}</span>
            <div style={{ display:"flex", gap:2 }}>
              <button onClick={()=>moveStep(s._key,-1)} disabled={idx===0} style={{ background:"transparent", border:"none", cursor:idx===0?"default":"pointer", color:"#94a3b8", padding:"2px 4px", fontSize:11 }}>▲</button>
              <button onClick={()=>moveStep(s._key,1)} disabled={idx===steps.length-1} style={{ background:"transparent", border:"none", cursor:idx===steps.length-1?"default":"pointer", color:"#94a3b8", padding:"2px 4px", fontSize:11 }}>▼</button>
              <button onClick={()=>removeStep(s._key)} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.red, padding:"2px 4px", fontSize:12, fontWeight:700 }}>✕</button>
            </div>
          </div>
          <div style={{ padding:12 }}>
            {fields}
            {stepErrors[idx] && <div style={{ color:"#DC2626", fontSize:12, marginTop:4 }}>{stepErrors[idx]}</div>}
            <div style={{ marginTop:8, display:"flex", gap:16, alignItems:"center" }}>
              <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b", cursor:"pointer" }}>
                <input type="checkbox" checked={s.WaitForLoad!==false} onChange={e=>updateStep(s._key,"WaitForLoad",e.target.checked)} />
                Wait for load
              </label>
              <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                Timeout (ms):
                <input type="number" value={s.TimeoutMs||10000} onChange={e=>updateStep(s._key,"TimeoutMs",parseInt(e.target.value)||10000)}
                  style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} />
              </label>
            </div>
            {(type==="navigate"||type==="click"||type==="fill") && (
              <div style={{ marginTop:6, display:"flex", gap:16, alignItems:"center" }}>
                <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                  Step warn (ms):
                  <input type="number" min={0} value={s.WarnMs||""} onChange={e=>updateStep(s._key,"WarnMs",e.target.value?parseInt(e.target.value)||null:null)}
                    style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} placeholder="e.g. 2000" />
                </label>
                <label style={{ display:"flex", alignItems:"center", gap:4, fontSize:11, color:"#64748b" }}>
                  Step crit (ms):
                  <input type="number" min={0} value={s.CritMs||""} onChange={e=>updateStep(s._key,"CritMs",e.target.value?parseInt(e.target.value)||null:null)}
                    style={{ width:72, padding:"3px 6px", border:`1px solid ${c.border}`, borderRadius:4, fontSize:11 }} placeholder="e.g. 5000" />
                </label>
              </div>
            )}
          </div>
        </div>
      );
    };

    const addStepBtns = ["navigate","fill","click","assert","extract"].map(type => (
      <button key={type} onClick={() => addStep(type)}
        style={{ background:c.blue, color:"#fff", border:"none", borderRadius:4, padding:"5px 12px", fontSize:11, cursor:"pointer", fontWeight:500 }}>
        + {stepTypeLabel[type]}
      </button>
    ));

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", display:"flex", alignItems:"center", gap:12, flexShrink:0 }}>
          <button onClick={() => setView("list")} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.blue, fontSize:12, padding:"4px 8px", borderRadius:4 }}>← Back</button>
          <span style={{ fontSize:14, fontWeight:700, color:"#1e293b" }}>{editFlowId===null?"New Flow":"Edit Flow"}</span>
          <div style={{ marginLeft:"auto", display:"flex", gap:8 }}>
            <Btn variant="secondary" onClick={() => setView("list")}>Cancel</Btn>
            <Btn variant="primary" loading={saving} onClick={saveFlow}>Save Flow</Btn>
          </div>
        </div>
        <div style={{ flex:1, overflowY:"auto", padding:20, background:"#FFFFFF" }}>
          {editErr && <ErrBox msg={editErr} />}
          <div style={{ display:"grid", gridTemplateColumns:"1fr 300px", gap:16, maxWidth:1100 }}>
            <div>
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16, marginBottom:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Flow Settings</div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12, marginBottom:12 }}>
                  <div><label style={lblS}>Flow Name *</label><input style={inpS} value={fd.FlowName} onChange={e=>setFd({FlowName:e.target.value})} placeholder="Login Flow" /></div>
                  <div><label style={lblS}>Group</label>
                    <select style={{ ...sxS, width:"100%" }} value={fd.GroupName||""} onChange={e=>setFd({GroupName:e.target.value})}>
                      <option value="">Select group...</option>
                      {editGroups.map(g => <option key={g.GroupID} value={g.GroupName}>{g.GroupName}</option>)}
                    </select>
                  </div>
                </div>
                <div style={{ marginBottom:12 }}><label style={lblS}>Description</label><input style={inpS} value={fd.Description||""} onChange={e=>setFd({Description:e.target.value})} placeholder="Monitors the login flow" /></div>
                <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr 1fr 1fr", gap:12 }}>
                  <div><label style={lblS}>Interval (min)</label><input type="number" min={1} style={inpS} value={fd.ScheduleIntervalMinutes} onChange={e=>setFd({ScheduleIntervalMinutes:parseInt(e.target.value)||5})} /></div>
                  <div><label style={lblS}>Target</label>
                    <select style={{ ...sxS, width:"100%" }} value={fd.ExecutionTarget} onChange={e=>setFd({ExecutionTarget:e.target.value})}>
                      <option value="server">Server</option>
                      <option value="agent">Agent</option>
                    </select>
                  </div>
                  <div><label style={lblS}>Warn (ms)</label><input type="number" min={0} style={inpS} value={fd.DurationWarnMs} onChange={e=>setFd({DurationWarnMs:parseInt(e.target.value)||3000})} /></div>
                  <div><label style={lblS}>Crit (ms)</label><input type="number" min={0} style={inpS} value={fd.DurationCritMs} onChange={e=>setFd({DurationCritMs:parseInt(e.target.value)||6000})} /></div>
                </div>
                <div style={{ marginTop:12, display:"flex", alignItems:"center", gap:8 }}>
                  <SmallToggle checked={fd.IsEnabled} onClick={()=>setFd({IsEnabled:!fd.IsEnabled})} />
                  <span style={{ fontSize:12, color:"#64748b" }}>Enabled (schedule this flow)</span>
                </div>
              </div>
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Steps ({steps.length})</div>
                {steps.length===0 && (
                  <div style={{ textAlign:"center", padding:"24px", color:"#94a3b8", fontSize:12, border:"1px dashed #e2e5ea", borderRadius:6, marginBottom:12 }}>No steps yet. Add a step below.</div>
                )}
                {steps.map((s,i) => stepEditor(s,i))}
                <div style={{ display:"flex", gap:6, flexWrap:"wrap", paddingTop:8, borderTop:`1px solid #f1f5f9` }}>{addStepBtns}</div>
              </div>
            </div>
            <div>
              <div style={{ background:"#F8FAFC", border:`1px solid ${c.border}`, borderRadius:6, padding:12, fontSize:11, color:"#64748b" }}>
                <div style={{ fontWeight:600, color:"#1e293b", marginBottom:6 }}>Credential Vault</div>
                <div style={{ marginBottom:4 }}>Use <code style={{ background:"#e2e5ea", borderRadius:3, padding:"1px 4px" }}>{"{{vault.name}}"}</code> in field values to inject credentials at runtime.</div>
                <div style={{ color:"#94a3b8" }}>Credentials are managed in Config → Credentials.</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  })();

  // ══ RESULTS VIEW ══
  const resultsView = (() => {
    const flowName = (resultsFlowId && flows.find(f=>f.FlowId===resultsFlowId)?.FlowName)||`Flow ${resultsFlowId}`;

    const statusBadge = (passed) => (
      <span style={{ display:"inline-flex", alignItems:"center", gap:4, padding:"2px 8px", borderRadius:12, fontSize:10, fontWeight:700, background:passed?"#DCF2EA":"#FAEAEA", color:passed?c.green:c.red }}>
        {passed?"PASS":"FAIL"}
      </span>
    );

    const resultDetail = selectedResult ? (() => {
      const r = selectedResult;
      const stepRes = (r.StepResults||r.stepResults||r.steps)||[];
      const maxDur = Math.max(...stepRes.map(s=>s.DurationMs||0), 1);
      const flowMeta = flows.find(f=>f.FlowId===resultsFlowId);
      // Compute DurationStatus: per-step rollup + flow-level safety net
      const durationStatus = (() => {
        if (stepRes.some(s=>s.StepStatus==="critical")) return "critical";
        if (stepRes.some(s=>s.StepStatus==="warn")) return "warn";
        if (flowMeta && flowMeta.DurationCritMs>0 && r.TotalDurationMs>=flowMeta.DurationCritMs) return "critical";
        if (flowMeta && flowMeta.DurationWarnMs>0 && r.TotalDurationMs>=flowMeta.DurationWarnMs) return "warn";
        return "ok";
      })();
      const renderSynthDiagMd = (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:"#5DD4F4", letterSpacing:"0.5px", borderBottom:"1px solid rgba(93,212,244,0.3)", paddingBottom:3, marginBottom:6, marginTop:i>0?10:0 }}>{t.slice(3)}</div>;
          if (t==="---") return <hr key={i} style={{ border:"none", borderTop:"1px solid #2D4A66", margin:"8px 0" }} />;
          if (t.startsWith("- ")||t.startsWith("• "))
            return <div key={i} style={{ display:"flex", gap:6, marginBottom:3 }}><span style={{ flexShrink:0, color:"#5DD4F4", 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:"#5DD4F4", 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 waterfall = stepRes.map((s,i) => {
        const pct = maxDur>0?Math.round((s.DurationMs||0)/maxDur*100):0;
        // Bar colour by StepStatus (4b)
        const barColor = s.Skipped?"#e2e5ea"
          : s.StepStatus==="critical"?c.red
          : s.StepStatus==="warn"?c.amber
          : s.StepStatus==="failed"?c.red
          : s.StepStatus==="ok"?c.green
          : s.Passed?c.green:c.red;
        const dotColor = s.Skipped?"#9CA3AF":s.StepStatus==="critical"||s.StepStatus==="failed"?c.red:s.StepStatus==="warn"?c.amber:s.Passed?c.green:c.red;
        // Tick mark positions (4c)
        const warnPct = (s.WarnMs&&maxDur>0)?Math.min(100,Math.round(s.WarnMs/maxDur*100)):null;
        const critPct = (s.CritMs&&maxDur>0)?Math.min(100,Math.round(s.CritMs/maxDur*100)):null;
        return (
          <div key={s.StepResultId||i} style={{ display:"grid", gridTemplateColumns:"20px 180px 1fr 70px", gap:8, alignItems:"center", padding:"4px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
            <div style={{ display:"flex", justifyContent:"center" }}>
              <span style={{ display:"inline-block", width:7, height:7, borderRadius:"50%", background:dotColor }} />
            </div>
            <div style={{ overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap", color:"#1e293b" }}>{s.Label||s.StepType}</div>
            <div style={{ background:"#f1f5f9", borderRadius:3, height:12, position:"relative" }}>
              <div style={{ position:"absolute", left:0, top:0, height:"100%", width:`${pct}%`, background:barColor, borderRadius:3 }} />
              {warnPct!=null && <div style={{ position:"absolute", left:`${warnPct}%`, top:0, width:2, height:"100%", background:c.amber, opacity:0.7 }} />}
              {critPct!=null && <div style={{ position:"absolute", left:`${critPct}%`, top:0, width:2, height:"100%", background:c.red, opacity:0.7 }} />}
            </div>
            <div style={{ textAlign:"right", fontFamily:"monospace", color:"#64748b" }}>{fmtDur(s.DurationMs)}</div>
          </div>
        );
      });
      // Waterfall summary row (4d)
      const waterfallSummary = (
        <div style={{ display:"flex", gap:16, marginTop:8, paddingTop:8, borderTop:"1px solid #e2e5ea", fontSize:11, color:"#64748b" }}>
          <span>Total: <strong style={{ color:"#1e293b" }}>{fmtDur(r.TotalDurationMs)}</strong></span>
          {flowMeta && <span>Flow warn: <strong style={{ color:c.amber }}>{fmtDur(flowMeta.DurationWarnMs)}</strong></span>}
          {flowMeta && <span>Flow crit: <strong style={{ color:c.red }}>{fmtDur(flowMeta.DurationCritMs)}</strong></span>}
        </div>
      );
      const screenshots = stepRes.filter(s=>s.ScreenshotPath).map((s,i) => {
        const fname = s.ScreenshotPath.split("/").pop().split("\\").pop();
        const url = `/api/synthetic/screenshots/${r.FlowId}/${r.ResultId}/${fname}`;
        return (
          <div key={i} style={{ marginBottom:12 }}>
            <div style={{ fontSize:10, color:"#94a3b8", marginBottom:4 }}>{s.Label||s.StepType} — step {s.StepOrder}</div>
            <img src={url} alt={`Step ${s.StepOrder}`} style={{ maxWidth:"100%", border:"1px solid #e2e5ea", borderRadius:4 }} />
          </div>
        );
      });
      const synthDiagPanel = (synthDiagnosis || synthDiagnosing || synthDiagError) ? (
        <div ref={synthDiagPanelRef} style={{ border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden", marginTop:16 }}>
          <div style={{ background:"#1E2B3C", padding:"8px 14px", display:"flex", alignItems:"center", justifyContent:"space-between" }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <span style={{ color:"#5DD4F4", fontSize:14 }}>⚡</span>
              <span style={{ color:"#F4F8FC", fontSize:12, fontWeight:600 }}>AI Failure Diagnosis</span>
            </div>
            <div style={{ display:"flex", alignItems:"center", gap:10 }}>
              <span style={{ color:"#7A9AB8", fontSize:10 }}>claude-haiku · synthetic context</span>
              {synthDiagnosis && <button onClick={() => { setSynthDiagnosis(null); setSynthDiagThread([]); setSynthDiagError(null); }} style={{ background:"none", border:"none", color:"#7A9AB8", cursor:"pointer", fontSize:12 }}>×</button>}
            </div>
          </div>
          <div style={{ padding:14, background:"#fff" }}>
            {synthDiagnosing && !synthDiagnosis && <div style={{ color:"#4A4A4A", fontSize:12 }}>⏳ Analysing failure context...</div>}
            {synthDiagError && <div style={{ color:"#D95C5C", fontSize:12 }}>{synthDiagError}</div>}
            {synthDiagThread.map((turn, ti) => (
              <div key={ti} style={{ marginBottom:12 }}>
                <div style={{ fontSize:10, fontWeight:600, color:"#7A9AB8", textTransform:"uppercase", letterSpacing:".04em", marginBottom:4 }}>
                  {turn.role === 'user' ? '▶ YOU' : '⚡ AI DIAGNOSIS'}
                </div>
                <div style={{ fontSize:12, color:"#1A1A1A", lineHeight:1.7 }}>
                  {renderSynthDiagMd(turn.content)}
                </div>
              </div>
            ))}
            {synthDiagnosis && !synthDiagnosing && (
              <div style={{ display:"flex", gap:8, marginTop:10, borderTop:"1px solid #D8CCBA", paddingTop:10 }}>
                <textarea
                  value={synthDiagFollowUp}
                  onChange={e => setSynthDiagFollowUp(e.target.value)}
                  onKeyDown={e => { if(e.key==='Enter'&&!e.shiftKey){ e.preventDefault(); if(synthDiagFollowUp.trim()){ setSynthDiagSending(true); runSynthDiagnosis(synthDiagFollowUp.trim()); } }}}
                  placeholder="Ask a follow-up question... (Enter to send)"
                  style={{ flex:1, border:"1px solid #D8CCBA", borderRadius:6, padding:"6px 10px", fontSize:11, resize:"none", height:52, fontFamily:"inherit" }}
                />
                <button
                  onClick={() => { if(synthDiagFollowUp.trim()){ setSynthDiagSending(true); runSynthDiagnosis(synthDiagFollowUp.trim()); } }}
                  disabled={!synthDiagFollowUp.trim() || synthDiagSending}
                  style={{ background:"#006D8C", color:"#fff", border:"none", borderRadius:6, padding:"6px 14px", fontSize:11, fontWeight:600, cursor:(!synthDiagFollowUp.trim()||synthDiagSending)?"not-allowed":"pointer", alignSelf:"flex-end", opacity:(!synthDiagFollowUp.trim()||synthDiagSending)?0.5:1 }}>
                  {synthDiagSending?"…":"Send"}
                </button>
              </div>
            )}
          </div>
        </div>
      ) : null;

      return (
        <div style={{ display:"grid", gridTemplateColumns:"1fr 240px", gap:16 }}>
          <div>
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16, marginBottom:16 }}>
              <div style={{ display:"flex", alignItems:"center", gap:12, marginBottom:12 }}>
                {statusBadge(r.Passed??r.passed)}
                <span style={{ fontSize:12, color:"#1e293b", fontWeight:600 }}>{fmtDate(r.StartedAt)}</span>
                <span style={{ fontSize:12, color:"#64748b", marginLeft:"auto" }}>{fmtDur(r.TotalDurationMs)}</span>
              </div>
              {r.ErrorMessage && <div style={{ background:"#FAEAEA", border:`1px solid ${c.red}`, borderRadius:4, padding:"8px 12px", fontSize:11, color:c.red, marginBottom:12 }}>{r.ErrorMessage}</div>}
              <div style={{ fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", marginBottom:8 }}>Waterfall</div>
              {waterfall}
              {waterfallSummary}
            </div>
            {screenshots.length>0 && (
              <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
                <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Screenshots</div>
                {screenshots}
              </div>
            )}
            {synthDiagPanel}
          </div>
          <div>
            <div style={{ background:"#fff", border:`1px solid ${c.border}`, borderRadius:8, padding:16 }}>
              <div style={{ fontSize:11, fontWeight:700, color:"#94a3b8", textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:12 }}>Run Summary</div>
              {[["Status", statusBadge(r.Passed??r.passed)], ["Started", fmtDate(r.StartedAt)], ["Completed", fmtDate(r.CompletedAt)], ["Total Duration", fmtDur(r.TotalDurationMs)], ["Steps", `${((r.StepResults||r.stepResults||r.steps)||[]).filter(s=>!s.Skipped).length} / ${((r.StepResults||r.stepResults||r.steps)||[]).length}`], ["Failed At Step", r.FailedAtStep?`Step ${r.FailedAtStep}`:"—"]].map(([label,val]) => (
                <div key={label} style={{ display:"flex", justifyContent:"space-between", padding:"6px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
                  <span style={{ color:"#64748b" }}>{label}</span>
                  <span style={{ fontWeight:600, color:"#1e293b" }}>{val}</span>
                </div>
              ))}
              {durationStatus!=="ok" && (
                <div style={{ display:"flex", justifyContent:"space-between", padding:"6px 0", borderBottom:"1px solid #f1f5f9", fontSize:11 }}>
                  <span style={{ color:"#64748b" }}>Duration Status</span>
                  <span style={{ display:"inline-flex", alignItems:"center", padding:"2px 8px", borderRadius:12, fontSize:10, fontWeight:700, background:durationStatus==="critical"?"#FAEAEA":"#FEF3C7", color:durationStatus==="critical"?c.red:c.amber }}>
                    {durationStatus==="critical"?"CRITICAL":"WARN"}
                  </span>
                </div>
              )}
            </div>
          </div>
        </div>
      );
    })() : <div style={{ textAlign:"center", padding:32, color:"#94a3b8", fontSize:12 }}>Select a result to view details.</div>;

    return (
      <div style={{ flex:1, display:"flex", flexDirection:"column", overflow:"hidden" }}>
        <div style={{ background:"#FFFFFF", borderBottom:`1px solid ${c.border}`, padding:"10px 20px", display:"flex", alignItems:"center", gap:12, flexShrink:0 }}>
          <button onClick={() => setView("list")} style={{ background:"transparent", border:"none", cursor:"pointer", color:c.blue, fontSize:12, padding:"4px 8px", borderRadius:4 }}>← Back</button>
          <span style={{ fontSize:14, fontWeight:700, color:"#1e293b" }}>Results — {flowName}</span>
          <div style={{ marginLeft:"auto", display:"flex", gap:6 }}>
            {selectedResult && <Btn variant="primary" style={{ fontSize:11, padding:"5px 12px" }} loading={synthDiagnosing} onClick={() => runSynthDiagnosis()}>⚡ Diagnose</Btn>}
            {canManage && <Btn variant="secondary" onClick={() => { setEditFlowId(resultsFlowId); setView("edit"); }}>Edit Flow</Btn>}
            {canManage && <Btn variant="ghost" onClick={() => runFlow(resultsFlowId)}>▶ Run Now</Btn>}
          </div>
        </div>
        <div style={{ flex:1, overflow:"hidden", display:"grid", gridTemplateColumns:"280px 1fr", gap:0 }}>
          <div style={{ borderRight:`1px solid ${c.border}`, overflowY:"auto", background:"#fff" }}>
            <div style={{ padding:"8px 12px", background:"#FAF8F4", borderBottom:`1px solid ${c.border}`, fontSize:10, fontWeight:700, color:"#94a3b8", textTransform:"uppercase" }}>Recent Runs</div>
            {resultsLoading && <div style={{ padding:16 }}><Spinner /></div>}
            {!resultsLoading && resultsErr && <ErrBox msg={resultsErr} />}
            {!resultsLoading && !resultsErr && results.length===0 && <div style={{ padding:"24px", textAlign:"center", color:"#94a3b8", fontSize:12 }}>No results yet.</div>}
            {!resultsLoading && results.map(r => (
              <div key={r.ResultId} onClick={() => setSelectedResult(r)}
                style={{ padding:"8px 12px", cursor:"pointer", borderBottom:"1px solid #f1f5f9", background:selectedResult?.ResultId===r.ResultId?"#E0F2F7":"#fff", display:"flex", alignItems:"center", gap:8 }}>
                <span style={{ display:"inline-block", width:8, height:8, borderRadius:"50%", background:(r.Passed??r.passed)?c.green:c.red, flexShrink:0 }} />
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:11, color:"#1e293b", fontWeight:500 }}>{parseUTC(r.StartedAt)?.toLocaleString() ?? "—"}</div>
                  <div style={{ fontSize:10, color:"#94a3b8" }}>{fmtDur(r.TotalDurationMs)}</div>
                </div>
              </div>
            ))}
          </div>
          <div style={{ overflowY:"auto", padding:20, background:"#FFFFFF" }}>
            {resultsLoading ? <Spinner /> : resultDetail}
          </div>
        </div>
      </div>
    );
  })();

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"100%", background:"#FFFFFF" }}>
      {view==="list"    && listView}
      {view==="edit"    && editView}
      {view==="results" && resultsView}
    </div>
  );
}

// ── Mount ──
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(React.createElement(OpsPortalApp));
