﻿// ════════════════════════════════════════════════════════════════════════════
// app-monitor-form.jsx — v1.4.457
// ────────────────────────────────────────────────────────────────────────────
// Extracted from app.jsx in Session 69 to reduce the main bundle from ~15k
// lines. Contains MonitorFormPage and its form-only helpers.
//
// Loading order: this file MUST be loaded AFTER app.jsx.
// Babel standalone with data-presets="react" shares top-level lexical scope
// across <script type="text/babel"> tags — all top-level const/let from
// app.jsx (c, Input, Sel, Btn, Card, TICKER_DURATION_SEC, etc.) are already
// in scope here. No destructure or window.OP1 namespace needed.
// ════════════════════════════════════════════════════════════════════════════

const Toggle = ({ label, checked, onChange, disabled }) => (
  <label style={{ display:"flex", alignItems:"center", gap:8, cursor:disabled?"not-allowed":"pointer", userSelect:"none", opacity:disabled?0.55:1 }}>
    <div onClick={()=>{ if (!disabled) onChange(!checked); }}
      style={{ width:34, height:18, borderRadius:9, background:checked?c.blue:c.border, position:"relative", transition:"background 0.2s", cursor:disabled?"not-allowed":"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:"",
  AlertsEnabled:false, CooldownEnabled:false,
};

// ── TestResultPane — inline, no modal ───────────────────────────────────────
function TestResultPane({ runs, meta, onDiagnose }) {

  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() : "—";
  const isAgentResult = latest.Protocol === "Agent";

  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 === "AGENT")                                              protocolLabel = "Agent";
  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 || {};
  // infra-disk uses DiskWarnPct/DiskCritPct (used%, high=bad) matching CPU/Mem convention
  const warnThr   = monSubLower === "infra-disk"
    ? parseFloat(tc2.DiskWarnPct || 80)
    : parseFloat(tc2.WarnThreshold || tc2.CpuWarnPct || tc2.MemWarnPct || 80);
  const critThr   = monSubLower === "infra-disk"
    ? parseFloat(tc2.DiskCritPct || 90)
    : parseFloat(tc2.CritThreshold || tc2.CpuCritPct || tc2.MemCritPct || 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;

  // ── Agent validation grid ─────────────────────────────────────────────────
  const agentMetricsGrid = isAgentResult ? (
    <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 }}>AGENT STATUS</div>
        <div style={{ fontSize:22, fontWeight:700, color: latest._agentOnline ? c.green : c.red, lineHeight:1.2 }}>{latest._agentOnline ? "● Online" : "○ Offline"}</div>
        <div style={{ fontSize:10, color:c.textDimmer, marginTop:2 }}>{latest.Target || "—"}</div>
      </div>
      <div style={{ padding:"10px 16px" }}>
        <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, letterSpacing:"0.07em", textTransform:"uppercase", marginBottom:4 }}>LAST VALUE</div>
        <div style={{ fontSize:22, fontWeight:700, color:c.text, lineHeight:1.2 }}>
          {latest._latestValue != null
            ? (latest._latestValue.StatusCode != null ? `${(latest._latestValue.StatusCode / 10).toFixed(1)}%` : `${latest._latestValue.ResponseTimeMs}ms`)
            : "—"}
        </div>
        {latest._latestValue && <div style={{ fontSize:10, color:c.textDimmer, marginTop:2 }}>{latest._latestValue.Severity || "healthy"}</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:"" });
  }
  // Agent synthetic result: use pre-built Checks array instead of auto-generated ones
  if (latest.Checks && latest.Checks.length > 0) {
    checks.splice(0, checks.length, ...latest.Checks.map(ch => ({ dot: ch.ok ? c.green : c.red, label: ch.label, right: ch.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 ───────────────────────────────────────────────────────────
  const canDiagnosePane = (sev !== "healthy" || hasFailedCheck) && !!onDiagnose;
  const diagColorPane   = sev === "warning" ? "#E89A2E" : "#D95C5C";

  return (
    <div style={{ border:`1px solid ${c.border}`, borderRadius:8, overflow:"hidden", marginTop:2 }}>
      <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 14px", borderBottom:`1px solid ${c.border}` }}>
        <span style={{ fontSize:15, fontWeight:700, color:sevColor, lineHeight:1.3 }}>{sevIcon} {sevLabel}</span>
        <div style={{ display:"flex", alignItems:"center", gap:10 }}>
          <span style={{ fontSize:11, color:c.textDimmer, whiteSpace:"nowrap" }}>{ts}</span>
          {canDiagnosePane && (
            <button
              onClick={() => onDiagnose(latest)}
              style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${diagColorPane}`, borderRadius:4, background:"#fff", color:diagColorPane, cursor:"pointer", fontWeight:600 }}
            >🔍 Diagnose</button>
          )}
        </div>
      </div>
      {statBar}
      {isAgentResult ? agentMetricsGrid : (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", DiskWarnPct: "80", DiskCritPct: "90" },
  "infra-diskio": { CheckMethod: "WMI", ReadsWarn: "500", WritesWarn: "500", QueueDepthWarn: "10", LatencyWarnMs: "20" },
  "infra-net":    { CheckMethod: "WMI", ThroughputInWarn: "900", ThroughputOutWarn: "900", ErrorRateWarn: "1", DropRateWarn: "1" },
  "web-http":    { 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 && !!monitor.MonitorID;

  const initLeaf = () => {
    const subTypeAliases = {
      "ping":"infra-ping", "cpu":"infra-cpu", "mem":"infra-mem",
      "disk":"infra-disk", "net":"infra-net", "svc":"infra-svc",
      "win-service":"infra-svc", "tcp":"infra-tcp",
      "svcavail":"app-port", "api":"app-http", "applog":"app-log", "perfctr":"app-perfctr",
      "http":"web-http", "https":"web-http", "ssl":"web-ssl", "dns":"web-dns", "content":"web-cm",
      "dbconn":"db-conn", "sessions":"db-sess", "blocking":"db-block", "logspace":"db-txlog", "longqry":"db-longq",
    };
    const raw = monitor?.MonitorSubType || "";
    if (raw) return subTypeAliases[raw] || raw;
    const mt = monitor?.MonitorType || "";
    if (mt==="Server")         return "infra-ping";
    if (mt==="Infrastructure") 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";
    if (mt==="PowerShell")     return "agt-ps";
    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||"",
    AlertsEnabled: monitor.AlertsEnabled===true,
    CooldownEnabled: monitor.CooldownEnabled===true,
  });
  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]; });
    // If the monitor was saved with an agent host, force CheckMethod to "Agent"
    // (old monitors may lack CheckMethod in TypeConfigJson, defaulting to "WMI").
    if (monitor?.AgentHostId) { merged.CheckMethod = "Agent"; }
    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 [savedOk, setSavedOk]             = useState(false);
  const [saveAndRunning, setSaveAndRunning] = useState(false);
  const [savedMonitorId, setSavedMonitorId] = useState(null); // tracks MonitorID after a create, so re-clicks use PUT
  const effectiveMonitorId = savedMonitorId || monitor?.MonitorID || null;
  const isPersisted = !!effectiveMonitorId;
  const [directModeExpanded, setDirectModeExpanded] = useState(false);
  const [agentModeExpanded, setAgentModeExpanded]   = useState(false);
  const [typeTreeOpen, setTypeTreeOpen]             = useState(false);
  const [notifsExpanded, setNotifsExpanded]         = useState(false);
  const [investsExpanded, setInvestsExpanded]       = useState(false);
  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 [lastRunbookContext, setLastRunbookContext] = 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 [historyDeleteToast, setHistoryDeleteToast] = useState("");
  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(''); setLastRunbookContext('');
    setCurrentSessionId(null); setSessionTitle('');
    setPendingAttachments([]); setAttachmentError('');
    setDiagName(data.monitorName || "");
    setDiagSevCol(data.severity === "warning" ? "#E89A2E" : "#D95C5C");
    const runbookParts = [
      data.runbookNotes ? `Operator runbook notes: ${data.runbookNotes}` : "",
      data.runbookUrl   ? `Runbook reference URL: ${data.runbookUrl}`    : "",
    ].filter(Boolean);
    const runbookCtx = runbookParts.join("\n");
    setLastRunbookContext(runbookCtx);
    const systemPrompt = "You are an expert infrastructure monitoring assistant helping users diagnose monitor check failures in OP1 Operations Portal. Be concise, practical, and specific." +
      (runbookCtx ? "\n\n## Operator context\n" + runbookCtx + "\n\nThe above notes are written by the operator who manages this monitor. Treat them as authoritative — if the notes describe expected behaviour, known thresholds, or investigation steps, factor them directly into your diagnosis and explicitly reference them where relevant." : "") +
      "\n\nFormat 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." +
      (lastRunbookContext ? "\n\n## Operator context\n" + lastRunbookContext + "\n\nThe above operator notes are authoritative context for this monitor. Reference them when relevant to the user's question." : "");
    const threadHistory = updatedThread.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`).join('\n\n');
    const 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":  { DiskWarnPct: "80", DiskCritPct: "90" },
      "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 (!effectiveMonitorId) return;
    setMonitorAssignmentsLoading(true);
    try {
      const assignments = await api("GET", `/notifications/monitors/${effectiveMonitorId}/assignments`);
      setMonitorAssignments(Array.isArray(assignments) ? assignments : []);
    } catch { setMonitorAssignments([]); }
    finally { setMonitorAssignmentsLoading(false); }
  }, [api, effectiveMonitorId]);

  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);
    setTypeTreeOpen(false);
  };

  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 (!effectiveMonitorId) return;
    const newVal = !isEnabled;
    setIsEnabled(newVal);
    try {
      await api("PATCH", `/monitors/${effectiveMonitorId}/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 (effectiveMonitorId) {
        await api("PUT",`/monitors/${effectiveMonitorId}`,payload);
      } else {
        const created = await api("POST","/monitors",payload);
        if (created?.MonitorID) setSavedMonitorId(created.MonitorID);
      }
      setIsDirty(false);
      setRunbookDirty(false);
      onSaved();
    } catch(e) { setErr(e.message); }
    finally { setSaving(false); }
  };

  // Save without navigating away — flashes "Saved ✓" for 2s
  const handleSave = async () => {
    setErr("");
    if (!form.MonitorName.trim()) { setErr("Monitor Name is required."); return; }
    setSaving(true);
    try {
      const payload = buildPayload();
      if (effectiveMonitorId) {
        await api("PUT",`/monitors/${effectiveMonitorId}`,payload);
      } else {
        const created = await api("POST","/monitors",payload);
        if (created?.MonitorID) setSavedMonitorId(created.MonitorID);
      }
      setIsDirty(false);
      setRunbookDirty(false);
      setSavedOk(true);
      setTimeout(() => setSavedOk(false), 2000);
      loadMonitorAssignments();
    } catch(e) { setErr(e.message); }
    finally { setSaving(false); }
  };

  // Save then immediately trigger a run check — stays on edit screen
  const handleSaveAndRun = async () => {
    setErr("");
    if (!form.MonitorName.trim()) { setErr("Monitor Name is required."); return; }
    setSaving(true); setSaveAndRunning(true);
    let savedId = effectiveMonitorId;
    try {
      const payload = buildPayload();
      if (savedId) { await api("PUT",`/monitors/${savedId}`,payload); }
      else {
        const created = await api("POST","/monitors",payload);
        savedId = created?.MonitorID;
        if (savedId) setSavedMonitorId(savedId); // prevent duplicate creates on re-click
      }
      setIsDirty(false);
      setRunbookDirty(false);
    } catch(e) { setErr(e.message); setSaving(false); setSaveAndRunning(false); return; }
    setSaving(false);
    if (savedId) {
      setRunNow(true); setRunNowMsg("");
      try {
        await api("POST",`/monitors/${savedId}/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); }
    }
    setSaveAndRunning(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;
    }
    // Agent monitors — validate locally, no API call
    const isAgentMonitor = !!(agentHostId || (tc.CheckMethod || "").toLowerCase() === "agent");
    if (isAgentMonitor) {
      setErr("");
      setTesting(true);
      setBarVisible(true); setBarPct(85);
      try {
        await new Promise(r => setTimeout(r, 500));
        const agentObj    = agentHosts.find(a => String(a.AgentHostId) === String(agentHostId));
        const latestLog   = recentLogs[0];
        const agentOnline = agentObj?.LastSeenAt && (Date.now() - new Date(agentObj.LastSeenAt + (!agentObj.LastSeenAt.endsWith('Z') ? 'Z' : '')).getTime()) < 5 * 60 * 1000;
        const hasResults  = !!latestLog;
        const agentResult = {
          IsSuccess:      !!(agentOnline && hasResults),
          Severity:       agentOnline && hasResults ? "healthy" : agentOnline ? "warning" : "failed",
          MonitorName:    form.MonitorName.trim() || form.IPAddressOrUrl.trim() || selectedLeaf,
          Target:         agentObj?.Name || "Unknown Agent",
          Protocol:       "Agent",
          StatusCode:     latestLog?.StatusCode || null,
          ResponseTimeMs: latestLog?.ResponseTimeMs || 0,
          Timestamp:      new Date().toISOString(),
          ErrorMessage:   !agentObj    ? "Agent host not found — select an agent host."
            : !agentOnline             ? `Agent "${agentObj.Name}" is offline — last seen ${agentObj.LastSeenAt ? new Date(agentObj.LastSeenAt + (!agentObj.LastSeenAt.endsWith('Z') ? 'Z' : '')).toLocaleString() : 'never'}.`
            : !hasResults              ? `Agent "${agentObj.Name}" is online but no check results received yet.`
            : null,
          _agentOnline:   agentOnline,
          _latestValue:   hasResults ? latestLog : null,
          Checks: [
            { ok: !!agentObj,    label: `Agent host: ${agentObj?.Name || 'not configured'}`,                                right: agentObj ? (agentOnline ? 'online' : 'offline') : '' },
            { ok: !!agentOnline, label: agentOnline ? `Last check-in: ${Math.round((Date.now() - new Date(agentObj.LastSeenAt + (!agentObj.LastSeenAt.endsWith('Z') ? 'Z' : '')).getTime()) / 1000)}s ago` : `Agent offline${agentObj?.LastSeenAt ? ' — last seen ' + new Date(agentObj.LastSeenAt + (!agentObj.LastSeenAt.endsWith('Z') ? 'Z' : '')).toLocaleString() : ''}`, right: '' },
            { ok: hasResults,    label: hasResults ? `Latest value: ${latestLog.StatusCode != null ? (latestLog.StatusCode/10).toFixed(1)+'%' : latestLog.ResponseTimeMs+'ms'}` : 'No results received yet',  right: hasResults ? latestLog.Severity || 'healthy' : '' },
            { ok: hasResults,    label: hasResults ? `Last checked: ${new Date(latestLog.CheckTimestamp + (!latestLog.CheckTimestamp.endsWith('Z') ? 'Z' : '')).toLocaleString()}` : 'Waiting for first check', right: '' },
            { ok: true,          label: `Check frequency: ${form.CheckFrequencySeconds}s`,                                  right: '' },
          ],
        };
        setTestRuns(prev => [agentResult, ...prev].slice(0, 3));
        setBarPct(100);
        setTimeout(() => { setBarVisible(false); setBarPct(0); }, 300);
      } finally { setTesting(false); }
      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); }
  };

  // Auto-run test on load for existing monitors (skips agent monitors and disabled monitors)
  useEffect(() => {
    if (!isEdit || !monitor?.MonitorID) return;
    if (agentHostId) return;
    if (monitor?.AgentHostId) return;
    const cm = (tc.CheckMethod || "").toLowerCase();
    if (cm === "agent") return;
    if (!isEnabled) return;
    runTest();
  }, []); // fires once on mount only

  const copyMonitor = async () => {
    if (!isEdit || !monitor?.MonitorID) return;
    setCopying(true); setErr("");
    try {
      const template = {
        ...monitor,
        MonitorID: null,
        MonitorName: `${monitor.MonitorName} (copy)`,
        CreatedAt: null,
        UpdatedAt: null,
        LastCheckedAt: null,
        RawStatus: null,
        EffectiveStatus: null,
        IsSuppressed: false,
        _isCopyTemplate: true,
      };
      setIsDirty(false);
      onCopy && onCopy(template);
    } 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)} hintInline="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)} hintInline="e.g. mydb" />
      <Input label="Username" value={tc.DbUser||""} onChange={e=>t("DbUser",e.target.value)} />
      <PasswordInput label="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)} hintInline="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 }}>
      {(tc.CheckMethod||"WMI") !== "Agent"
        ? <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
        : <div style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic", display:"flex", alignItems:"center" }}>Agent monitors locally — no IP required.</div>
      }
      <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>{ t("CheckMethod",e.target.value); if(e.target.value!=="Agent"){ setAgentHostId(""); } setIsDirty(true); }} options={["WMI","SNMP","Agent"]} />
      {(tc.CheckMethod||"WMI") === "Agent" && (
        <div style={{ gridColumn:"1/-1" }}>
          <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); }}
            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="">(Select agent host…)</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>
      )}
      {(tc.CheckMethod||"WMI") !== "Agent" && 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>
      )}
      {(tc.CheckMethod||"WMI") === "Agent" && (
        <div style={{ gridColumn:"1/-1", borderLeft:'3px solid #008C6F', background:'#DCF2EA', padding: agentModeExpanded ? '10px 12px' : '6px 12px', borderRadius:6, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
            <div><strong style={{color:'#008C6F'}}>✓ Agent mode</strong> — checks run locally on the agent machine and report back.</div>
            <button onClick={()=>setAgentModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#008C6F", fontSize:11, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{agentModeExpanded ? "Less ▴" : "Details ▸"}</button>
          </div>
          {agentModeExpanded && (
            <div style={{ marginTop:6 }}>
              <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>
      )}
      {(tc.CheckMethod||"WMI") !== "Agent" && (
        <div style={{ gridColumn:"1/-1", borderLeft:'3px solid #E89A2E', background:'#FDF3E0', padding: directModeExpanded ? '10px 12px' : '6px 12px', borderRadius:6, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
            <div><strong style={{color:'#E89A2E'}}>⚠ Direct mode</strong> — the portal server runs this check directly over the network.</div>
            <button onClick={()=>setDirectModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#E89A2E", fontSize:11, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{directModeExpanded ? "Less ▴" : "Details ▸"}</button>
          </div>
          {directModeExpanded && (
            <div style={{ marginTop:6 }}>
              <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, use Agent mode above instead.</span></div>
            </div>
          )}
        </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>
      {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 identityRow = (
    <div style={{ display:"flex", flexDirection:"column", gap:8, marginBottom:8 }}>
      <Input label="Monitor Name *" value={form.MonitorName} onChange={e=>f("MonitorName",e.target.value)} hintInline="e.g. prod-web-01" tabIndex={1} />
      <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8, alignItems:"flex-end" }}>
        <GroupAutocomplete
          value={form.GroupName}
          groups={groups}
          onChange={(name) => f("GroupName", name)}
          tabIndex={2}
        />
        <Sel label="Parent Monitor" value={form.ParentMonitorID||""} onChange={e=>f("ParentMonitorID",e.target.value)}
          options={[{value:"",label:"— None —"},...allMons.filter(m=>!monitor||m.MonitorID!==monitor.MonitorID).map(m=>({value:m.MonitorID,label:m.MonitorName}))]} />
      </div>
    </div>
  );

  const basicSettings = (
    <div style={{ marginBottom:8 }}>
      {/* CONNECTION — Agent Host only for non-web/db/synthetic types */}
      {selectedLeaf && !selectedLeaf.startsWith("infra-") && !selectedLeaf.startsWith("web-") && !selectedLeaf.startsWith("db-") && selectedLeaf !== "synthetic-flow" && (
        <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","infra-svc","infra-proc"].includes(selectedLeaf) && (
        <div style={{ borderLeft:'3px solid #006D8C', background:'#E0F2F7', padding: directModeExpanded ? '8px 12px' : '4px 10px', borderRadius:6, marginTop:6, fontSize:10, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
            <div><strong style={{color:'#006D8C'}}>ⓘ Direct mode</strong> — the portal server runs this check over the network.</div>
            <button onClick={()=>setDirectModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#006D8C", fontSize:10, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{directModeExpanded ? "Less ▴" : "Details ▸"}</button>
          </div>
          {directModeExpanded && (
            <div style={{ marginTop:6, fontSize:11 }}>
              <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>
          )}
        </div>
      )}
      {!!agentHostId && !selectedLeaf?.startsWith("infra-") && (
        <div style={{ borderLeft:'3px solid #008C6F', background:'#DCF2EA', padding: agentModeExpanded ? '8px 12px' : '4px 10px', borderRadius:6, marginTop:6, fontSize:10, color:'#4A4A4A', lineHeight:'1.6' }}>
          <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
            <div><strong style={{color:'#008C6F'}}>✓ Agent mode</strong> — checks run locally on the agent machine and report back.</div>
            <button onClick={()=>setAgentModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#008C6F", fontSize:10, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{agentModeExpanded ? "Less ▴" : "Details ▸"}</button>
          </div>
          {agentModeExpanded && (
            <div style={{ marginTop:6, fontSize:11 }}>
              <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>
      )}
      <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={{ display:"flex", alignItems:"center", justifyContent:"space-between", gap:12, marginBottom:8 }}>
        <Toggle label="Enable Anomaly Detection (coming soon)" checked={false} onChange={()=>{}} disabled={true} />
      </div>
    </div>
  );

  // Type-specific field sets
  let typeFields = null;

  if (selectedLeaf === "infra-ping") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g3}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hintInline="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hintInline="e.g. 500" />
            <Input label="Packet Loss Warn (%)" type="number" value={tc.PacketLossWarnPct||""} onChange={e=>t("PacketLossWarnPct",e.target.value)} hintInline="e.g. 10" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-tcp") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hintInline="e.g. 80" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hintInline="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hintInline="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-svc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            {(tc.CheckMethod||"Agent") !== "Agent"
              ? <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
              : <div style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic", display:"flex", alignItems:"center" }}>Agent monitors locally — no IP required.</div>
            }
            <Sel label="Check Method" value={tc.CheckMethod||"Agent"} onChange={e=>{ t("CheckMethod",e.target.value); if(e.target.value!=="Agent"){ setAgentHostId(""); } setIsDirty(true); }} options={["WMI","SNMP","Agent"]} />
            {(tc.CheckMethod||"Agent") === "Agent" && (
              <div style={{ gridColumn:"1/-1" }}>
                <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); }} 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="">(Select agent host…)</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>
            )}
            {(tc.CheckMethod||"Agent") !== "Agent" && 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>
            )}
            {(tc.CheckMethod||"Agent") === "Agent" && (
              <div style={{ gridColumn:"1/-1", borderLeft:'3px solid #008C6F', background:'#DCF2EA', padding: agentModeExpanded ? '10px 12px' : '6px 12px', borderRadius:6, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
                <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
                  <div><strong style={{color:'#008C6F'}}>✓ Agent mode</strong> — checks run locally on the agent machine and report back.</div>
                  <button onClick={()=>setAgentModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#008C6F", fontSize:11, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{agentModeExpanded ? "Less ▴" : "Details ▸"}</button>
                </div>
                {agentModeExpanded && (
                  <div style={{ marginTop:6 }}>
                    <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>
            )}
            {(tc.CheckMethod||"Agent") !== "Agent" && (
              <div style={{ gridColumn:"1/-1", borderLeft:'3px solid #E89A2E', background:'#FDF3E0', padding: directModeExpanded ? '10px 12px' : '6px 12px', borderRadius:6, fontSize:11, color:'#4A4A4A', lineHeight:'1.6' }}>
                <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", gap:8 }}>
                  <div><strong style={{color:'#E89A2E'}}>⚠ Direct mode</strong> — the portal server runs this check directly over the network.</div>
                  <button onClick={()=>setDirectModeExpanded(v=>!v)} style={{ background:"none", border:"none", cursor:"pointer", color:"#E89A2E", fontSize:11, fontWeight:600, padding:0, whiteSpace:"nowrap", flexShrink:0 }}>{directModeExpanded ? "Less ▴" : "Details ▸"}</button>
                </div>
                {directModeExpanded && (
                  <div style={{ marginTop:6 }}>
                    <div>• Valid Windows credentials configured in the Credential Vault</div>
                    <div>• WMI remoting enabled (TCP 135 + dynamic RPC ports open in firewall)</div>
                    <div><span style={{color:'#006D8C'}}>→ For firewalled or remote machines, use Agent mode above instead.</span></div>
                  </div>
                )}
              </div>
            )}
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <WmiCombo label="Service Name / Pattern" value={tc.ServiceName||""} onChange={v=>t("ServiceName",v)} hintInline="e.g. Spooler, MSSQLSERVER" discoveryType="services" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <Input label="Restart Count Warn (per hour)" type="number" value={tc.RestartCountWarn||""} onChange={e=>t("RestartCountWarn",e.target.value)} hintInline="e.g. 3" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-cpu") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="CPU Warn (%)" type="number" value={tc.CpuWarnPct||""} onChange={e=>t("CpuWarnPct",e.target.value)} hintInline="e.g. 80" />
            <Input label="CPU Crit (%)" type="number" value={tc.CpuCritPct||""} onChange={e=>t("CpuCritPct",e.target.value)} hintInline="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} hideTitle={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g3}>
            <Input label="Memory Warn (%)" type="number" value={tc.MemWarnPct||""} onChange={e=>t("MemWarnPct",e.target.value)} hintInline="e.g. 80" />
            <Input label="Memory Crit (%)" type="number" value={tc.MemCritPct||""} onChange={e=>t("MemCritPct",e.target.value)} hintInline="e.g. 95" />
            <Input label="Paging Warn (%)" type="number" value={tc.PagingWarnPct||""} onChange={e=>t("PagingWarnPct",e.target.value)} hintInline="e.g. 50" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-disk") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <WmiCombo label="Drive / Mount Point" value={tc.DrivePath||""} onChange={v=>t("DrivePath",v)} hintInline="e.g. C: or D:" discoveryType="drives" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Disk Used Warn (%)" type="number" value={tc.DiskWarnPct||""} onChange={e=>t("DiskWarnPct",e.target.value)} hintInline="e.g. 80" />
            <Input label="Disk Used Crit (%)" type="number" value={tc.DiskCritPct||""} onChange={e=>t("DiskCritPct",e.target.value)} hintInline="e.g. 90" />
            <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} hideTitle={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Reads/sec Warn" type="number" value={tc.ReadsWarn||""} onChange={e=>t("ReadsWarn",e.target.value)} hintInline="e.g. 500" />
            <Input label="Writes/sec Warn" type="number" value={tc.WritesWarn||""} onChange={e=>t("WritesWarn",e.target.value)} hintInline="e.g. 500" />
            <Input label="Queue Depth Warn" type="number" value={tc.QueueDepthWarn||""} onChange={e=>t("QueueDepthWarn",e.target.value)} hintInline="e.g. 10" />
            <Input label="Latency Warn (ms)" type="number" value={tc.LatencyWarnMs||""} onChange={e=>t("LatencyWarnMs",e.target.value)} hintInline="e.g. 20" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-net") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            {(tc.CheckMethod||"WMI") !== "Agent"
              ? <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
              : <div style={{ fontSize:11, color:"#4A4A4A", fontStyle:"italic", display:"flex", alignItems:"center" }}>Agent monitors locally — no IP required.</div>
            }
            <Sel label="Check Method" value={tc.CheckMethod||"WMI"} onChange={e=>{ t("CheckMethod",e.target.value); if(e.target.value!=="Agent"){ setAgentHostId(""); } setIsDirty(true); }} options={["WMI","SNMP","Agent"]} />
            {(tc.CheckMethod||"WMI") === "Agent" && (
              <div style={{ gridColumn:"1/-1" }}>
                <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); }}
                  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="">(Select agent host…)</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>
            )}
            {(tc.CheckMethod||"WMI") !== "Agent" && 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} hideTitle={true}>
          <div style={g2}>
            <Input label="Throughput In Warn (Mbps)" type="number" value={tc.ThroughputInWarn||""} onChange={e=>t("ThroughputInWarn",e.target.value)} hintInline="e.g. 900" />
            <Input label="Throughput Out Warn (Mbps)" type="number" value={tc.ThroughputOutWarn||""} onChange={e=>t("ThroughputOutWarn",e.target.value)} hintInline="e.g. 900" />
            <Input label="Error Rate Warn (%)" type="number" value={tc.ErrorRateWarn||""} onChange={e=>t("ErrorRateWarn",e.target.value)} hintInline="e.g. 1" />
            <Input label="Drop Rate Warn (%)" type="number" value={tc.DropRateWarn||""} onChange={e=>t("DropRateWarn",e.target.value)} hintInline="e.g. 1" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-proc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {infraHostFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <WmiCombo label="Process Name / Pattern" value={tc.ProcessName||""} onChange={v=>t("ProcessName",v)} hintInline="e.g. nginx, svchost, java" discoveryType="processes" host={form.IPAddressOrUrl} credentialId={credentialId} api={api} />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="CPU Warn (%)" type="number" value={tc.ProcCpuWarn||""} onChange={e=>t("ProcCpuWarn",e.target.value)} hintInline="e.g. 80" />
            <Input label="Memory Warn (MB)" type="number" value={tc.ProcMemWarnMb||""} onChange={e=>t("ProcMemWarnMb",e.target.value)} hintInline="e.g. 512" />
            <Input label="Instance Count Min" type="number" value={tc.InstanceMin||""} onChange={e=>t("InstanceMin",e.target.value)} hintInline="e.g. 1" />
            <Input label="Instance Count Max" type="number" value={tc.InstanceMax||""} onChange={e=>t("InstanceMax",e.target.value)} hintInline="e.g. 10" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-log") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={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)} hintInline="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} hideTitle={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)} hintInline="e.g. disk, failed, access denied" />
            </div>
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Warn threshold (events)" type="number" value={tc.WarnThreshold||"1"} onChange={e=>t("WarnThreshold",e.target.value)} hintInline="e.g. 1" />
            <Input label="Crit threshold (events)" type="number" value={tc.CritThreshold||"5"} onChange={e=>t("CritThreshold",e.target.value)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "infra-hw") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={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)} hintInline="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} hideTitle={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} hideTitle={true}>
          <div style={g2}>
            <Input label="Temp Warn (°C)" type="number" value={tc.TempWarn||"70"} onChange={e=>t("TempWarn",e.target.value)} hintInline="e.g. 70" />
            <Input label="Temp Crit (°C)" type="number" value={tc.TempCrit||"85"} onChange={e=>t("TempCrit",e.target.value)} hintInline="e.g. 85" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-svc") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g3}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hintInline="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} hideTitle={true}>
          <div style={g2}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hintInline="e.g. 200" />
            <Input label="RT Crit (ms)" type="number" value={tc.RtCritMs||""} onChange={e=>t("RtCritMs",e.target.value)} hintInline="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-api") {
    const httpMethod = tc.HttpMethod || "GET";
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <div style={{ gridColumn:"1/-1" }}>
              <Input label="Endpoint URL" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="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)} hintInline="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)} hintInline="e.g. ok" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-port") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="e.g. 192.168.1.1" />
            <Input label="Port" type="number" value={form.Port||""} onChange={e=>f("Port",e.target.value)} hintInline="e.g. 8080" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g1}>
            <Input label="RT Warn (ms)" type="number" value={tc.RtWarnMs||""} onChange={e=>t("RtWarnMs",e.target.value)} hintInline="e.g. 200" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-jvm") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={g2}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="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} hideTitle={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)} hintInline="e.g. 500" />
            <Input label="Thread Count Warn" type="number" value={tc.ThreadCountWarn||""} onChange={e=>t("ThreadCountWarn",e.target.value)} hintInline="e.g. 200" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-thread") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={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} hideTitle={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)} hintInline="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-queue") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={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} hideTitle={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)} hintInline="e.g. 500" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-cache") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={true}>
          <div style={g1}>
            <Input label="Occurrences Warn (per interval)" type="number" value={tc.OccurrencesWarn||""} onChange={e=>t("OccurrencesWarn",e.target.value)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "app-perfctr") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          <div style={agentHostId ? g2 : g3}>
            <Input label="Host / IP Address" value={form.IPAddressOrUrl||""} onChange={e=>f("IPAddressOrUrl",e.target.value)} hintInline="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} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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)} hintInline="e.g. 200" />
            <Input label="Blocked Sessions Warn" type="number" value={tc.BlockedSessWarn||""} onChange={e=>t("BlockedSessWarn",e.target.value)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-block") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={true}>
          <div style={g3}>
            <Input label="Blocking Chain Length Warn" type="number" value={tc.BlockingChainWarn||""} onChange={e=>t("BlockingChainWarn",e.target.value)} hintInline="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)} hintInline="e.g. 1" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-wait") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
      </div>
    );
  } else if (selectedLeaf === "db-perfctr") {
    typeFields = (
      <div>
        <CollapsibleSection title="Connection" alwaysOpen={true} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={true}>
          <div style={g3}>
            <Input label="Disk Used Warn (%)" type="number" value={tc.DiskWarnPct||""} onChange={e=>t("DiskWarnPct",e.target.value)} hintInline="e.g. 80" />
            <Input label="Disk Used Crit (%)" type="number" value={tc.DiskCritPct||""} onChange={e=>t("DiskCritPct",e.target.value)} hintInline="e.g. 90" />
            <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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={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)} hintInline="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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={true}>
          <div style={g2}>
            <Input label="Resolution Time Warn (ms)" type="number" value={tc.ResTimeWarnMs||""} onChange={e=>t("ResTimeWarnMs",e.target.value)} hintInline="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} hideTitle={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)} hintInline="e.g. 5" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Thresholds" alwaysOpen={true} hideTitle={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} hideTitle={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)} hintInline="e.g. 200" />
          </div>
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={true}>
          <div style={g2}>
            <Input label="TTFB Warn (ms)" type="number" value={tc.TtfbWarnMs||""} onChange={e=>t("TtfbWarnMs",e.target.value)} hintInline="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} hideTitle={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} hideTitle={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} hideTitle={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} hideTitle={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)} hintInline="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} hideTitle={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} hideTitle={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} hideTitle={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" />
            <PasswordInput label="Test 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} hideTitle={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} hideTitle={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)} hintInline="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} hideTitle={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} hideTitle={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)} hintInline="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} hideTitle={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} hideTitle={true}>
          {dbConnFields}
        </CollapsibleSection>
        <CollapsibleSection title="Config" alwaysOpen={true} hideTitle={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} hideTitle={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} hideTitle={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)} />
            <PasswordInput label="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} hideTitle={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} hideTitle={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" />
            <PasswordInput label="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} hideTitle={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:"#1E2B3C", border:"1px solid #2a3a4f", borderRadius:6, outline:"none", resize:"vertical", color:"#E8EDF2", caretColor:"#5DD4F4" }}
            />
          </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 fieldHeader = (
    <div className="op1-dense-form" style={{ padding:"20px 24px 14px", background:"#FFFFFF", borderBottom:"1px solid #C0D8E8", flexShrink:0 }}>
      <div style={{ fontSize:13, fontWeight:700, color:"#1A1A1A", marginBottom:12 }}>Monitor Details</div>
      {identityRow}
      {/* Inline type selector */}
      <div style={{ position:"relative", marginTop:10 }}>
        <div
          onClick={() => setTypeTreeOpen(v => !v)}
          style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"6px 12px", border:"1px solid #C0D8E8", borderRadius: typeTreeOpen ? "6px 6px 0 0" : 6, background: typeTreeOpen ? "#F0F7FB" : "#FAFCFE", cursor:"pointer", fontSize:12, userSelect:"none" }}
        >
          {selectedLeaf && catObj && grpObj && leafObj ? (
            <span>
              <span style={{ color:"#7A9AB8" }}>{catObj.label}</span>
              <span style={{ color:"#7A9AB8", margin:"0 4px" }}>›</span>
              <span style={{ color:"#7A9AB8" }}>{grpObj.label}</span>
              <span style={{ color:"#7A9AB8", margin:"0 4px" }}>›</span>
              <span style={{ color:"#1A1A1A", fontWeight:600 }}>{leafObj.label}</span>
            </span>
          ) : (
            <span style={{ color:"#9CA3AF" }}>Select monitor type…</span>
          )}
          <span style={{ fontSize:10, color:"#4A4A4A", flexShrink:0, marginLeft:8 }}>{typeTreeOpen ? "▴" : "▾"}</span>
        </div>
        {typeTreeOpen && (
          <div onClick={() => setTypeTreeOpen(false)} style={{ position:"fixed", inset:0, zIndex:99 }} />
        )}
        {typeTreeOpen && (
          <div style={{ position:"absolute", left:0, right:0, zIndex:100, border:"1px solid #C0D8E8", borderTop:"none", borderRadius:"0 0 6px 6px", background:"#FFFFFF", maxHeight:300, overflowY:"auto", boxShadow:"0 8px 16px rgba(0,0,0,0.08)" }}>
            {treePane}
          </div>
        )}
      </div>
    </div>
  );

  const fieldBody = (
    <div className="dp op1-dense-form" style={{ flex:1, overflowY:"auto", overscrollBehavior:"contain", padding:"16px 24px 20px", background:"#FFFFFF" }}>
      {basicSettings}
      {typeFields}
    </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
          onClick={() => setNotifsExpanded(v => !v)}
          style={{ width:"100%", display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 20px", cursor:"pointer", userSelect:"none", transition:"background 120ms", background:"#F5F0E8" }}
          onMouseEnter={e => e.currentTarget.style.background = "#EDE5D8"}
          onMouseLeave={e => e.currentTarget.style.background = "#F5F0E8"}
        >
          <div style={{ display:"flex", alignItems:"center", gap:8, minWidth:0, overflow:"hidden" }}>
            <span style={{ fontSize:11, fontWeight:700, color:c.blue, letterSpacing:"0.08em", textTransform:"uppercase" }}>Notifications & Runbook</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>
            }
            {(form.RunbookUrl||form.RunbookNotes) && <span style={{ fontSize:10, borderRadius:10, padding:"1px 8px", background:c.greenLight, color:c.green, fontWeight:600 }}>✓ Runbook</span>}
          </div>
          <span style={{ color:c.textMuted, fontSize:10, transform: notifsExpanded ? "rotate(180deg)" : "rotate(0deg)", transition:"transform 180ms", display:"inline-block", flexShrink:0 }}>▾</span>
        </div>
        {notifsExpanded && <div style={{ padding:"0 20px 14px" }}>
            <div style={{ display:"flex", flexDirection:"column", gap:6, padding:"10px 0 14px", borderBottom:`1px dashed ${c.border}`, marginBottom:12 }}>
              <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                <SmallToggle checked={form.AlertsEnabled===true} onClick={()=>{ const next=form.AlertsEnabled!==true; f("AlertsEnabled",next); if(!next) f("CooldownEnabled",false); }} />
                <span style={{ fontSize:12, color:c.text }}>Send notifications when this monitor breaches its threshold</span>
              </div>
              <div style={{ display:"flex", alignItems:"center", gap:8, marginLeft:20, opacity: form.AlertsEnabled===true ? 1 : 0.4, pointerEvents: form.AlertsEnabled===true ? 'auto' : 'none' }}>
                <SmallToggle checked={form.CooldownEnabled===true} onClick={()=>f("CooldownEnabled",form.CooldownEnabled!==true)} />
                <span style={{ fontSize:12, color:c.text }}>Apply cooldown window between repeated notifications</span>
              </div>
            </div>
            {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={()=>{ showConfirm('Remove this recipient from monitor?', async () => { 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
                disabled={!isPersisted}
                title={!isPersisted ? "Save the monitor first to assign recipients" : undefined}
                onClick={()=>{ setShowAssignPicker(p=>!p); setAssignPickerRecipient(''); }}
                style={{ fontSize:11, padding:"4px 10px", border:`1px solid ${c.blue}`, borderRadius:5, background:"#fff", color:c.blue, opacity: isPersisted ? 1 : 0.5, cursor: isPersisted ? 'pointer' : 'not-allowed' }}>
                + 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 || !isPersisted} onClick={async()=>{ await api("POST","/notifications/assignments",{MonitorID:effectiveMonitorId,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 style={{ borderTop:"1px solid #f1f5f9", marginTop:12, paddingTop:12 }}>
            <div style={{ fontSize:10, fontWeight:700, color:c.textMuted, textTransform:"uppercase", letterSpacing:"0.06em", marginBottom:8 }}>Runbook</div>
            <div style={{ 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. Shown in the 'What to do' section of alert emails.</div>
              </div>
            </div>
          </div>
          </div>}
      </div>
      {/* ── Investigation History rail ── */}
      {isEdit && (
        <div style={{ borderTop:"1px solid #C0D8E8" }}>
          <div
            onClick={() => setInvestsExpanded(v => !v)}
            style={{ width:"100%", display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 20px", cursor:"pointer", userSelect:"none", transition:"background 120ms", background:"#F5F0E8" }}
            onMouseEnter={e => e.currentTarget.style.background = "#EDE5D8"}
            onMouseLeave={e => e.currentTarget.style.background = "#F5F0E8"}
          >
            <div style={{ display:"flex", alignItems:"center", gap:8, minWidth:0, overflow:"hidden" }}>
              <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>
            <span style={{ color:c.textMuted, fontSize:10, transform: investsExpanded ? "rotate(180deg)" : "rotate(0deg)", transition:"transform 180ms", display:"inline-block", flexShrink:0 }}>▾</span>
          </div>
          {investsExpanded && (
            <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={(e) => {
                        e.stopPropagation();
                        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>
                      <button onClick={(e) => {
                        e.stopPropagation();
                        if (!window.confirm("Delete this investigation session? This cannot be undone.")) return;
                        const prev = investigationHistory;
                        const wasOpenInDiagnose = currentSessionId === s.SessionId;
                        setInvestigationHistory(h => h.filter(x => x.SessionId !== s.SessionId));
                        api("DELETE", `/investigations/${s.SessionId}`)
                          .then(() => {
                            setHistoryDeleteToast("Session deleted.");
                            setTimeout(() => setHistoryDeleteToast(""), 3000);
                            if (wasOpenInDiagnose) {
                              setDiagOpen(false);
                              setDiagResult(null);
                              setDiagError(null);
                              setDiagTs(null);
                              setInvestigationThread([]);
                              setFollowUpText('');
                              setLastDiagnosisContext('');
                              setLastRunbookContext('');
                              setCurrentSessionId(null);
                              setSessionTitle('');
                            }
                          })
                          .catch((err) => {
                            setInvestigationHistory(prev);
                            setHistoryDeleteToast("Could not delete session. Please try again.");
                            setTimeout(() => setHistoryDeleteToast(""), 4000);
                          });
                      }} style={{ fontSize:11, padding:"3px 7px", borderRadius:5, border:`1px solid ${c.red}40`, background:`${c.red}0D`, color:c.red, cursor:"pointer", flexShrink:0, fontWeight:500 }}>
                        ✕
                      </button>
                    </div>
                  ))}
                  {investigationHistory.length > 5 && (
                    <button onClick={(e) => { e.stopPropagation(); 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, hasSecondaryLine: false };
    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; 0-based Y with threshold-aware max
        const vals1 = sparkPts.map(l=>l.StatusCode);
        const max1  = Math.max(...vals1, 0);
        const toX   = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
        // Parse thresholds before toY so yMax can include them
        let warnThresh=null, critThresh=null;
        // ── Sparkline threshold lines ──
        // Dual-read pattern `tc.X || cfg.X` is intentional and NOT a ThresholdResolver migration target.
        // `tc` = live form state (reflects in-flight user edits); `cfg` = JSON.parse(monitor.TypeConfigJson).
        // Resolver is authoritative on server-side alarm/notification paths. This is a client-side preview
        // of what the threshold lines would look like with the currently-typed values, so it MUST read form
        // state locally, not via API. The `|| cfg.X` fallback is unreachable in practice because
        // LEAF_TC_DEFAULTS seeds tc fields on mount and on subtype reset. Session 61 audit: see OP1_Backlog.md.
        try { const cfg=JSON.parse(monitor.TypeConfigJson||"{}"); warnThresh=parseFloat(tc.ValueWarn||cfg.ValueWarn)||null; critThresh=parseFloat(tc.ValueCrit||cfg.ValueCrit)||null; } catch {}
        sparkInfo.warnThresh=warnThresh; sparkInfo.critThresh=critThresh;
        const yMax1 = Math.max(max1, critThresh ?? warnThresh ?? max1, 1) * 1.2;
        const toY1  = v => pad + (1 - Math.min(v, yMax1) / yMax1) * (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);
        const warnY = warnThresh!=null ? toY1(warnThresh) : null;
        const critY = critThresh!=null ? toY1(critThresh) : null;
        // Line 2: response time — secondary, subordinate style
        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(" ");
        }
        sparkInfo.hasSecondaryLine = !!pts2;
        return (
          <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#FFFFFF", 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="#C8D8E8" strokeWidth="1" strokeLinejoin="round" opacity="0.5" />}
            <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, 0-based Y with threshold-aware max
        const vals = sparkPts.map(l=>l.StatusCode/10);
        const maxV = Math.max(...vals, 0);
        const toX  = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
        // Parse thresholds before toY so yMax can include them
        let warnThresh=null, critThresh=null;
        try {
          const cfg = JSON.parse(monitor.TypeConfigJson||"{}");
          if (subtype==="infra-net") {
            warnThresh = parseFloat(tc.ThroughputInWarn||cfg.ThroughputInWarn)||null;
          } else if (subtype==="infra-cpu") {
            warnThresh = parseFloat(tc.CpuWarnPct||cfg.CpuWarn)||null; critThresh = parseFloat(tc.CpuCritPct||cfg.CpuCrit)||null;
          } else if (subtype==="infra-mem") {
            warnThresh = parseFloat(tc.MemWarnPct||cfg.MemWarn)||null; critThresh = parseFloat(tc.MemCritPct||cfg.MemCrit)||null;
          } else if (subtype==="infra-disk") {
            const dw = parseFloat(tc.DiskWarnPct||cfg.DiskWarnPct); if (!isNaN(dw) && dw>0) warnThresh = dw;
            const dc = parseFloat(tc.DiskCritPct||cfg.DiskCritPct); if (!isNaN(dc) && dc>0) critThresh = dc;
          } else if (subtype==="web-ssl") {
            // For SSL, warn/crit are days-remaining thresholds — normalize so crit < warn
            const rw = parseFloat(tc.ExpiryWarnDays||cfg.ExpiryWarnDays)||30;
            const rc = parseFloat(tc.ExpiryCritDays||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 yMax = Math.max(maxV, critThresh ?? warnThresh ?? maxV, 1) * 1.2;
        const toY  = v => pad + (1 - Math.min(v, yMax) / yMax) * (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);
        const warnY = warnThresh!=null ? toY(warnThresh) : null;
        const critY = critThresh!=null ? toY(critThresh) : null;
        return (
          <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#FFFFFF", 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, 0-based Y with threshold-aware max
      const vals = sparkPts.map(l=>l.ResponseTimeMs);
      const maxV = Math.max(...vals, 0);
      const toX  = i => pad + (i/(sparkPts.length-1))*(VW-pad*2);
      // Parse thresholds before toY so yMax can include them
      let warnThresh=null, critThresh=null;
      try { const cfg=JSON.parse(monitor.TypeConfigJson||"{}"); warnThresh=parseInt(tc.RtWarnMs||cfg.RtWarnMs)||null; critThresh=parseInt(tc.RtCritMs||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 yMax = Math.max(maxV, critThresh ?? warnThresh ?? maxV, 1) * 1.2;
      const toY  = v => pad + (1 - Math.min(v, yMax) / yMax) * (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);
      const warnY = warnThresh!=null ? toY(warnThresh) : null;
      const critY = critThresh!=null ? toY(critThresh) : null;
      return (
        <svg viewBox={`0 0 ${VW} ${VH}`} preserveAspectRatio="none" style={{ width:"100%", height:80, display:"block", background:"#FFFFFF", 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 — key includes all threshold tc fields so changing a threshold forces a remount */}
            {sparkline && <div key={[tc.RtWarnMs,tc.RtCritMs,tc.ValueWarn,tc.ValueCrit,tc.CpuWarnPct,tc.CpuCritPct,tc.MemWarnPct,tc.MemCritPct,tc.DiskWarnPct,tc.DiskCritPct,tc.ExpiryWarnDays,tc.ExpiryCritDays,tc.ThroughputInWarn].join('|')} style={{ marginBottom:2, flexShrink:0, borderRadius:4, overflow:"hidden", border:"1px solid #E8F0F5" }}>{sparkline}</div>}
            {/* X axis timestamps */}
            {sparkline && recentLogs.length > 0 && (() => {
              const oldest = recentLogs.length ? parseUTC(recentLogs[recentLogs.length-1].CheckTimestamp) : null;
              const newest = recentLogs.length ? parseUTC(recentLogs[0].CheckTimestamp) : null;
              const fmtTs = d => { if (!d) return ""; return d.toLocaleString('en-US', { month:'short', day:'numeric', hour:'numeric', minute:'2-digit', hour12:true }); };
              return (
                <div style={{ display:"flex", justifyContent:"space-between", fontSize:10, color:"#9CA3AF", marginBottom:2, flexShrink:0 }}>
                  <span>{fmtTs(oldest)}</span>
                  <span>{fmtTs(newest)}</span>
                </div>
              );
            })()}
            {/* Legend — primary line + optional secondary + threshold lines */}
            {sparkline && (() => {
              const legendItems = [
                { color:"#006D8C", dash:null, width:2, label: pm.kpiLabel || pm.label },
                sparkInfo.hasSecondaryLine ? { color:"#C8D8E8", dash:null, width:1, label:"Check duration (ms)" } : null,
                sparkInfo.warnThresh!=null  ? { color:"#E89A2E", dash:"4,2", width:1, label:"Warn threshold" }   : null,
                sparkInfo.critThresh!=null  ? { color:"#D95C5C", dash:"2,2", width:1, label:"Crit threshold" }   : null,
              ].filter(Boolean);
              return (
                <div style={{ display:"flex", gap:16, flexWrap:"wrap", fontSize:10, color:"#4A4A4A", marginBottom:6, flexShrink:0 }}>
                  {legendItems.map((item, i) => (
                    <span key={i} style={{ display:"flex", alignItems:"center", gap:4 }}>
                      <svg width="24" height="10" style={{ flexShrink:0 }}>
                        <line x1="0" y1="5" x2="24" y2="5" stroke={item.color} strokeWidth={item.width} strokeDasharray={item.dash || undefined} opacity={item.color==="#C8D8E8" ? 0.8 : 1} />
                      </svg>
                      <span>{item.label}</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.4.457</span>
              {isDirty && <span style={{ fontSize:12, fontWeight:700, borderRadius:5, padding:"3px 9px", background:"#fef08a", color:"#713f12", border:"1px solid #eab308", letterSpacing:"0.02em" }}>UNSAVED</span>}
            </div>
          </div>
        </div>
        <div style={{ display:"flex", alignItems:"center", gap:8 }}>
          {isPersisted && (
            <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>}
          <div style={{ width:"0.5px", height:18, background:"#e2e5ea", flexShrink:0 }} />
          <button
            onClick={(!isEdit && !selectedLeaf) ? undefined : runTest}
            disabled={testing || (!isEdit && !selectedLeaf)}
            style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#ffffff", color: (!isEdit && !selectedLeaf) ? "#aaa" : "#006D8C", cursor: (!isEdit && !selectedLeaf) ? "not-allowed" : "pointer", whiteSpace:"nowrap", fontFamily:"monospace" }}>
            {testing ? `${SPINNER_CHARS[spinnerFrame]} Testing…` : "▶ Test Monitor"}
          </button>
          <button
            disabled={saving || runNow}
            onClick={handleSaveAndRun}
            style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #006D8C", borderRadius:5, background:"#ffffff", color:"#006D8C", cursor:(saving||runNow)?"not-allowed":"pointer", whiteSpace:"nowrap", fontFamily:"monospace" }}>
            {saving && saveAndRunning ? "Saving…" : (runNow && saveAndRunning ? `${SPINNER_CHARS[runNowSpinnerFrame]} Running…` : "Save & Run")}
          </button>
          <button
            disabled={saving}
            onClick={handleSave}
            style={{ fontSize:11, padding:"3px 12px", border:"none", borderRadius:5, background: savedOk ? "#008C6F" : "#006D8C", color:"#ffffff", cursor:saving?"not-allowed":"pointer", fontWeight:600, whiteSpace:"nowrap", transition:"background 0.3s" }}>
            {saving && !saveAndRunning ? "Saving…" : savedOk ? "Saved ✓" : "Save"}
          </button>
          <button onClick={()=>{ if (isDirty) setConfirmLeave(true); else onClose(); }} style={{ fontSize:11, padding:"3px 10px", border:"0.5px solid #e2e5ea", borderRadius:5, background:"#ffffff", color:"#374151", cursor:"pointer" }}>Close</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" }}>
          {/* Fixed header — Monitor Details + Identity + Type selector */}
          {fieldHeader}
          {/* Scrolling config fields */}
          <div style={{ flex:1, minHeight:0, overflow:"hidden", display:"flex" }}>
            {fieldBody}
          </div>
          {/* Pinned rails — Notifications & Runbook + Investigation History */}
          {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:"8px 20px 16px", background:"#FFFFFF", overflowY:"auto", flex:"0 0 auto", maxHeight:"62%", 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,
                            runbookUrl:     form.RunbookUrl   || "",
                            runbookNotes:   form.RunbookNotes || "",
                          })}
                          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>
              );
            })() : (() => {
              return (
                <div>
                  {runNowMsg && <div style={{ textAlign:"center", fontSize:11, color: runNowMsg.startsWith("Run failed") ? "#D95C5C" : "#008C6F", marginBottom:6 }}>{runNowMsg}</div>}
                  {barVisible && (
                    <div style={{ position:"relative", height:3, background:"#E5E7EB", overflow:"hidden", borderRadius:"2px 2px 0 0", marginBottom:4 }}>
                      <div style={{ height:"100%", width: barPct + "%", background:"#006D8C", transition: barPct === 85 ? "width 3s ease-out" : barPct === 100 ? "width 0.05s linear" : "none" }} />
                    </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 onClick={runTest} style={{ color:"#7A9AB8", fontSize:12, textAlign:"center", padding:"16px 0", cursor:"pointer" }}
                onMouseEnter={e=>{ e.currentTarget.style.color="#006D8C"; e.currentTarget.style.textDecoration="underline"; }}
                onMouseLeave={e=>{ e.currentTarget.style.color="#7A9AB8"; e.currentTarget.style.textDecoration="none"; }}>
                Click <strong style={{ color:"inherit" }}>▶ 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 }}
                onDiagnose={(latest) => runDiagnosis({
                  monitorName:    form.MonitorName || monitor?.MonitorName || "",
                  monitorType:    form.MonitorType || "",
                  monitorSubType: selectedLeaf || form.MonitorSubType || "",
                  target:         latest.Target || "",
                  protocol:       latest.Protocol || "",
                  statusCode:     latest.StatusCode != null ? String(latest.StatusCode) : "N/A",
                  responseTime:   latest.ResponseTimeMs != null ? `${latest.ResponseTimeMs}ms` : "N/A",
                  errorMessage:   latest.ErrorMessage || "",
                  severity:       (latest.Severity || (latest.IsSuccess ? "healthy" : "failed")).toLowerCase(),
                  timeoutMs:      parseInt(form.TimeoutMs) || 5000,
                  runbookUrl:     form.RunbookUrl   || "",
                  runbookNotes:   form.RunbookNotes || "",
                })}
              />
            )}
          </div>
          {/* Recent logs (edit only) */}
          <div style={{ flex:1, minHeight:0, display:"flex", flexDirection:"column", overflow:"hidden", background:"#FFFFFF", marginTop:12 }}>{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 ${TICKER_DURATION_SEC}s linear infinite`, whiteSpace:"nowrap" }}>
            {[...recentAlerts, ...recentAlerts].map((item, i) => (
              <span key={i} style={{ padding:"0 24px", fontSize:11, color:"rgba(255,255,255,0.85)", borderRight:"1px solid rgba(255,255,255,0.1)", display:"inline-flex", alignItems:"center", gap:5 }}>
                <span style={{ color:(item.Status||"").toLowerCase()==="critical"?"#ef4444":(item.Status||"").toLowerCase()==="warning"?"#f59e0b":"#22c55e", fontSize:8 }}>&#9679;</span>
                {(item.Status||"").toUpperCase()} &middot; {item.MonitorName}{item.Message ? ` \u00b7 ${item.Message}` : ""}
                {item.CreatedAt && <span style={{ color:"rgba(255,255,255,0.4)", fontSize:10, marginLeft:4 }}>{parseUTC(item.CreatedAt)?.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}</span>}
              </span>
            ))}
          </div>
        ) : (
          <span style={{ fontSize:11, color:"rgba(255,255,255,0.35)", paddingLeft:16, letterSpacing:"0.04em" }}>No recent alerts</span>
        )}
      </div>

      {/* 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. Close anyway?</div>
            <div style={{ display:"flex",gap:10,justifyContent:"flex-end" }}>
              <Btn variant="secondary" onClick={()=>{ setConfirmLeave(false); setPendingNavTab(null); }}>Go back</Btn>
              <Btn variant="danger"    onClick={()=>{ setIsDirty(false); setConfirmLeave(false); if (pendingNavTab) { onNavigate(pendingNavTab); setPendingNavTab(null); } onClose(); }}>Close anyway</Btn>
            </div>
          </Card>
        </div>
      )}
      {/* ── Investigation session deleted toast ── */}
      {historyDeleteToast && (
        <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)" }}>
          {historyDeleteToast}
        </div>
      )}
    </div>
  );
}