/* Inspection Review — the highest-leverage screen.
   Queue left · detail right · filters top · batch actions bottom.
   Keyboard: J/K navigate, A accept, R reject, C reclassify, G assign, [ ] toggle thermal/rgb */

function ReviewPage({ go, mic }) {
  const [rows, setRows] = useState(() => DETECTIONS);
  const [selIds, setSelIds] = useState(new Set());
  const [currentId, setCurrentId] = useState(DETECTIONS[0].id);
  const [sortKey, setSortKey] = useState("severity");
  const [flt, setFlt] = useState({ sev: "all", temp: "all", src: "all", review: "pending", minConf: 0 });
  const [imageMode, setImageMode] = useState("both"); // thermal | rgb | both
  const [overlay, setOverlay] = useState(true);
  const [reclassOpen, setReclassOpen] = useState(false);
  const [toast, setToast] = useState(null);

  const visible = useMemo(() => {
    let r = rows.filter(d => {
      if (flt.sev !== "all" && d.severity !== flt.sev) return false;
      if (flt.temp !== "all" && d.temporal !== flt.temp) return false;
      if (flt.src !== "all" && d.source !== flt.src) return false;
      if (flt.review !== "all" && d.review !== flt.review) return false;
      if (d.confidence < flt.minConf) return false;
      return true;
    });
    const SEV = { critical: 0, high: 1, medium: 2, low: 3 };
    r.sort((a, b) => {
      if (sortKey === "severity") return SEV[a.severity] - SEV[b.severity] || b.confidence - a.confidence;
      if (sortKey === "confidence") return b.confidence - a.confidence;
      if (sortKey === "temp") return b.dT - a.dT;
      if (sortKey === "id") return a.id.localeCompare(b.id);
      return 0;
    });
    return r;
  }, [rows, flt, sortKey]);

  const current = rows.find(r => r.id === currentId) || visible[0];

  // If current falls out of filters, snap to first
  useEffect(() => {
    if (visible.length && !visible.find(r => r.id === currentId)) {
      setCurrentId(visible[0].id);
    }
  }, [visible, currentId]);

  useEffect(() => { const h = (e) => setRows(rs => rs.map(r => e.detail.ids.includes(r.id) ? { ...r, review: e.detail.review } : r)); window.addEventListener("skyops:dets", h); return () => window.removeEventListener("skyops:dets", h); }, []);
  useEffect(() => { const h = (e) => setFlt(f => ({ ...f, sev: e.detail.sev, review: "all" })); window.addEventListener("skyops:review-filter", h); return () => window.removeEventListener("skyops:review-filter", h); }, []);
  const showToast = (msg) => { setToast(msg); setTimeout(() => setToast(null), 2200); };

  const updateRows = (ids, patch) => {
    setRows(rs => rs.map(r => ids.includes(r.id) ? { ...r, ...patch } : r));
  };

  const accept = (id) => { updateRows([id], { review: "accepted" }); showToast("Accepted observation"); };
  const reject = (id) => { updateRows([id], { review: "rejected" }); showToast("Rejected observation"); };
  const reclassify = (id, kind) => {
    const meta = ANOMALY_CLASS[kind];
    updateRows([id], { review: "reclassified", kind, label: meta.label, group: meta.group });
    setReclassOpen(false);
    showToast(`Reclassified as ${meta.label}`);
  };
  const assign = (id) => { updateRows([id], { assignee: "T. Aragón" }); showToast("Assigned to T. Aragón"); };

  // Batch ops
  const batchAccept = () => { updateRows([...selIds], { review: "accepted" }); showToast(`Accepted ${selIds.size}`); setSelIds(new Set()); };
  const batchReject = () => { updateRows([...selIds], { review: "rejected" }); showToast(`Rejected ${selIds.size}`); setSelIds(new Set()); };
  const batchAssign = () => { updateRows([...selIds], { assignee: "T. Aragón" }); showToast(`Assigned ${selIds.size}`); setSelIds(new Set()); };

  // Keyboard shortcuts
  useEffect(() => {
    const onKey = (e) => {
      if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || e.target.tagName === "SELECT") return;
      const idx = visible.findIndex(r => r.id === currentId);
      if (e.key === "j") { e.preventDefault(); if (visible[idx+1]) setCurrentId(visible[idx+1].id); }
      else if (e.key === "k") { e.preventDefault(); if (visible[idx-1]) setCurrentId(visible[idx-1].id); }
      else if (e.key === "a") { e.preventDefault(); if (current) accept(current.id); }
      else if (e.key === "r") { e.preventDefault(); if (current) reject(current.id); }
      else if (e.key === "c") { e.preventDefault(); setReclassOpen(true); }
      else if (e.key === "g") { e.preventDefault(); if (current) assign(current.id); }
      else if (e.key === "[") { e.preventDefault(); setImageMode("thermal"); }
      else if (e.key === "]") { e.preventDefault(); setImageMode("rgb"); }
      else if (e.key === "\\") { e.preventDefault(); setImageMode("both"); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [visible, currentId, current]);

  const counts = useMemo(() => ({
    pending: rows.filter(r => r.review === "pending").length,
    accepted: rows.filter(r => r.review === "accepted").length,
    rejected: rows.filter(r => r.review === "rejected").length,
    reclassified: rows.filter(r => r.review === "reclassified").length,
  }), [rows]);

  const toggleSel = (id) => setSelIds(s => { const n = new Set(s); n.has(id) ? n.delete(id) : n.add(id); return n; });

  return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
      {/* Sub-header */}
      <div style={{ padding: "14px 24px 0" }}>
        <div className="page-head" style={{ marginBottom: 10 }}>
          <div className="title">
            <div className="kicker">Review · MIC-2026-04-033 · Évora Plana PV</div>
            <h1>Inspection review</h1>
            <div className="sub">
              Processing complete · <span className="mono">{(window.__activeMic && window.__activeMic.detections != null ? window.__activeMic.detections : 1204).toLocaleString()}</span> detections · <span className="num">{counts.pending}</span> pending · <span className="num">{counts.accepted}</span> accepted
            </div>
          </div>
          <div className="actions">
            <div className="row" style={{ gap: 6, color: "var(--fg-muted)", fontSize: 11 }}>
              <kbd style={kbdStyle}>J/K</kbd>nav <kbd style={kbdStyle}>A</kbd>accept <kbd style={kbdStyle}>R</kbd>reject <kbd style={kbdStyle}>C</kbd>reclassify <kbd style={kbdStyle}>G</kbd>assign
            </div>
            <button className="btn btn-secondary"><Icon name="filter"/>More filters</button>
            <button className="btn btn-primary" title={counts.pending > 0 ? "Pending detections are excluded from the report until reviewed" : ""} onClick={() => { const m = mic || (window.INSPECTIONS || []).find(i => i.id === localStorage.getItem("skyops_mic")); if (m) { window.micAdvance(m, { review: 1 }, m.status === "review" || m.status === "processing" ? "reported" : m.status); m.reviewSummary = { accepted: counts.accepted, rejected: counts.rejected, pending: counts.pending, at: window.SKY_TODAY }; window.saveMicState && window.saveMicState(m); } window.skyToast && window.skyToast(counts.pending > 0 ? `Report generated · ${counts.pending} pending detections excluded` : "Review finalised · report generated"); window.__inNav ? window.__inNav.setWsStage("report") : go("workspace"); }}><Icon name="check_circle"/>{counts.pending > 0 ? `Finalize · ${counts.pending} pending excluded` : "Finalize & generate report"}</button>
          </div>
        </div>

        {window.CoreStrip && <window.CoreStrip dom="review" title="Argus triage"/>}
        {/* Filter bar */}
        <div className="card" style={{ padding: "10px 12px", display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
          <FilterSelect label="Severity" value={flt.sev} onChange={v => setFlt({...flt, sev: v})}
            options={[["all","All"],["critical","Critical"],["high","High"],["medium","Medium"],["low","Low"]]}/>
          <FilterSelect label="Temporal" value={flt.temp} onChange={v => setFlt({...flt, temp: v})}
            options={[["all","All"],["NEW","New"],["PERSISTENT","Persistent"],["DEGRADED","Degraded"],["RESOLVED","Resolved"]]}/>
          <FilterSelect label="Source" value={flt.src} onChange={v => setFlt({...flt, src: v})}
            options={[["all","All"],["INSPECTION_SYSTEM","Inspection · AI"],["INSPECTION_HUMAN","Inspection · Human"],["SENSOR_CONTINUOUS","SCADA"],["SATELLITE","Satellite"],["FIELD_REPORT","Field"]]}/>
          <FilterSelect label="Status" value={flt.review} onChange={v => setFlt({...flt, review: v})}
            options={[["all","All"],["pending","Pending"],["accepted","Accepted"],["rejected","Rejected"],["reclassified","Reclassified"]]}/>
          <div className="row" style={{ gap: 8 }}>
            <span className="sec" style={{ fontSize: 11 }}>Confidence ≥</span>
            <input type="range" min="0" max="100" step="5" value={flt.minConf} onChange={e => setFlt({...flt, minConf: +e.target.value})} style={{ width: 100 }}/>
            <span className="mono num" style={{ fontSize: 11, minWidth: 22 }}>{flt.minConf}</span>
          </div>
          <div className="grow"/>
          <div className="row" style={{ gap: 6 }}>
            <span className="sec" style={{ fontSize: 11 }}>Sort</span>
            <select className="sel" style={{ width: 140, height: 26, padding: "0 8px", fontSize: 11 }}
                    value={sortKey} onChange={e => setSortKey(e.target.value)}>
              <option value="severity">Severity ↓</option>
              <option value="confidence">Confidence ↓</option>
              <option value="temp">ΔT ↓</option>
              <option value="id">ID ↑</option>
            </select>
          </div>
          <div className="row" style={{ gap: 6, fontSize: 11, color: "var(--fg-muted)" }}>
            <span className="num">{visible.length}</span>/<span className="num">{rows.length}</span>
          </div>
        </div>
      </div>

      {/* Main split */}
      <div className="rv-split" style={{ flex: 1, display: "grid", gap: 0, padding: "14px 24px 0", minHeight: 0 }}>
        {/* Queue */}
        <div className="card" style={{ display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" }}>
          <div style={{ overflow: "auto", flex: 1 }}>
            <table className="tbl">
              <thead>
                <tr>
                  <th style={{ width: 30, padding: "8px 8px 8px 14px" }}>
                    <input type="checkbox"
                      checked={visible.length > 0 && visible.every(r => selIds.has(r.id))}
                      onChange={e => setSelIds(e.target.checked ? new Set(visible.map(r => r.id)) : new Set())}/>
                  </th>
                  <th>ID</th>
                  <th>Type</th>
                  <th>Severity</th>
                  <th>Temporal</th>
                  <th>ΔT</th>
                  <th>Confidence</th>
                  <th>Status</th>
                </tr>
              </thead>
              <tbody>
                {visible.map(d => (
                  <tr key={d.id}
                      onClick={() => setCurrentId(d.id)}
                      className={d.id === currentId ? "sel" : ""}>
                    <td onClick={e => e.stopPropagation()} style={{ padding: "0 8px 0 14px" }}>
                      <input type="checkbox" checked={selIds.has(d.id)} onChange={() => toggleSel(d.id)}/>
                    </td>
                    <td className="mono" style={{ fontSize: 11 }}>{d.id}</td>
                    <td>
                      <div style={{ display: "flex", flexDirection: "column", gap: 1 }}>
                        <span style={{ fontSize: 12 }}>{d.label}</span>
                        <span className="mono muted" style={{ fontSize: 10 }}>{d.group}</span>
                      </div>
                    </td>
                    <td><Sev level={d.severity} /></td>
                    <td><Tstat v={d.temporal} /></td>
                    <td className="mono num" style={{ fontSize: 11, color: d.dT > 20 ? "var(--sev-critical)" : d.dT > 10 ? "var(--sev-high)" : "var(--fg-secondary)" }}>
                      {d.dT > 0 ? `+${d.dT.toFixed(1)}` : d.dT.toFixed(1)}°C
                    </td>
                    <td><Confidence value={d.confidence}/></td>
                    <td>
                      {d.review === "pending" && <span className="pill"><span className="dot"/>pending</span>}
                      {d.review === "accepted" && <Pill tone="ok"><span className="dot"/>accepted</Pill>}
                      {d.review === "rejected" && <Pill tone="crit"><span className="dot"/>rejected</Pill>}
                      {d.review === "reclassified" && <Pill tone="warn"><span className="dot"/>reclassified</Pill>}
                    </td>
                  </tr>
                ))}
                {visible.length === 0 && (
                  <tr><td colSpan="8" style={{ padding: 32, textAlign: "center", color: "var(--fg-muted)" }}>
                    No detections match the current filters. Relax a filter to see more.
                  </td></tr>
                )}
              </tbody>
            </table>
          </div>

          {/* Batch bar */}
          {selIds.size > 0 && (
            <div style={{ borderTop: "1px solid var(--border-elevated)", padding: "10px 14px", display: "flex", alignItems: "center", gap: 10, background: "var(--bg-elevated)" }}>
              <span style={{ fontSize: 12, fontWeight: 500 }}>{selIds.size} selected</span>
              <span className="grow"/>
              <button className="btn btn-ghost btn-xs" onClick={() => setSelIds(new Set())}>Clear</button>
              <button className="btn btn-secondary btn-xs" onClick={batchAssign}><Icon name="users" size={12}/>Assign</button>
              <button className="btn btn-danger btn-xs" onClick={batchReject}><Icon name="x" size={12}/>Reject all</button>
              <button className="btn btn-info btn-xs" onClick={batchAccept}><Icon name="check" size={12}/>Accept all</button>
            </div>
          )}
        </div>

        {/* Detail rail */}
        <div className="card" style={{ marginLeft: 12, display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" }}>
          {current && <DetectionDetail
            key={current.id}
            det={current}
            imageMode={imageMode} setImageMode={setImageMode}
            overlay={overlay} setOverlay={setOverlay}
            onAccept={() => accept(current.id)}
            onReject={() => reject(current.id)}
            onReclassify={() => setReclassOpen(true)}
            onAssign={() => assign(current.id)}
          />}
        </div>
      </div>

      <div style={{ height: 14 }}/>

      {reclassOpen && <ReclassifyModal det={current} onClose={() => setReclassOpen(false)} onPick={(k) => reclassify(current.id, k)}/>}

      {toast && (
        <div role="status" aria-live="polite"
             style={{ position: "fixed", bottom: 24, left: "50%", transform: "translateX(-50%)", background: "var(--bg-elevated)", border: "1px solid var(--border-elevated)", borderRadius: 8, padding: "10px 14px", fontSize: 12, zIndex: 200, boxShadow: "0 10px 30px rgba(0,0,0,0.4)", display: "flex", alignItems: "center", gap: 8 }}>
          <Icon name="check_circle" size={13} style={{ color: "var(--health-ok)" }}/>
          {toast}
        </div>
      )}
    </div>
  );
}

const kbdStyle = { background: "var(--bg-inset)", border: "1px solid var(--border-base)", padding: "1px 5px", borderRadius: 3, fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-secondary)", margin: "0 2px" };

function FilterSelect({ label, value, onChange, options }) {
  return (
    <div className="row" style={{ gap: 6 }}>
      <span className="sec" style={{ fontSize: 11 }}>{label}</span>
      <select className="sel" style={{ width: "auto", height: 26, padding: "0 8px", fontSize: 11 }}
              value={value} onChange={e => onChange(e.target.value)}>
        {options.map(([v,l]) => <option key={v} value={v}>{l}</option>)}
      </select>
    </div>
  );
}

function DetectionDetail({ det, imageMode, setImageMode, overlay, setOverlay, onAccept, onReject, onReclassify, onAssign }) {
  return (
    <>
      <div style={{ padding: "12px 14px", borderBottom: "1px solid var(--border-base)", display: "flex", alignItems: "center", gap: 10 }}>
        <div>
          <div className="mono" style={{ fontSize: 11, color: "var(--fg-muted)" }}>{det.id}</div>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{det.label}</div>
        </div>
        <div className="row" style={{ gap: 6 }}>
          <Sev level={det.severity}/>
          <Tstat v={det.temporal}/>
          <Src v={det.source}/>
        </div>
        <div className="grow"/>
        <div className="seg">
          {[["thermal","Thermal"],["rgb","RGB"],["both","Both"]].map(([v,l]) => (
            <button key={v} className={imageMode===v?"on":""} onClick={()=>setImageMode(v)}>{l}</button>
          ))}
        </div>
        <label className="row" style={{ gap: 4, fontSize: 11, cursor: "pointer" }}>
          <input type="checkbox" checked={overlay} onChange={e => setOverlay(e.target.checked)}/>
          Overlay
        </label>
      </div>

      {/* Image pair */}
      <div style={{ padding: 14, display: "grid", gridTemplateColumns: imageMode === "both" ? "1fr 1fr" : "1fr", gap: 10 }}>
        {(imageMode === "thermal" || imageMode === "both") && (
          <ImageFrame kind="thermal" det={det} overlay={overlay} />
        )}
        {(imageMode === "rgb" || imageMode === "both") && (
          <ImageFrame kind="rgb" det={det} overlay={overlay} />
        )}
      </div>

      {/* Enrichment strip */}
      <div style={{ padding: "0 14px 14px" }}>
        <div className="caps" style={{ marginBottom: 6 }}>Enrichment</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(90px, 1fr))", gap: 0, border: "1px solid var(--border-base)", borderRadius: 8, overflow: "hidden" }}>
          {[
            ["Module", det.module, "mono"],
            ["String", det.string, "mono small"],
            ["ΔT", `${det.dT.toFixed(2)} °C`, "num"],
            ["Area", `${det.area_m2.toFixed(2)} m²`, "num"],
            ["Irradiance @ capture", "952 W/m²", "num"],
            ["Ambient", "26.4 °C · wind 9 km/h", ""],
          ].map(([l, v, cls], i) => (
            <div key={i} style={{ padding: "8px 10px", borderRight: i < 5 ? "1px solid var(--border-base)" : "none", background: "var(--bg-inset)" }}>
              <div className="caps" style={{ marginBottom: 2 }}>{l}</div>
              <div className={cls} style={{ fontSize: cls.includes("small") ? 10 : 12, fontWeight: 500 }}>{v}</div>
            </div>
          ))}
        </div>
      </div>

      {window.CoreExplain && <div style={{ padding: "0 14px 14px" }}><window.CoreExplain det={det}/></div>}
      {/* Lineage */}
      <div style={{ padding: "0 14px 14px" }}>
        <div className="caps" style={{ marginBottom: 6 }}>Lineage</div>
        <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, flexWrap: "wrap" }}>
          <LinChip ico="upload" label="Ingest" sub="08:12" />
          <Arrow/>
          <LinChip ico="cog" label="Process · ai-v4.2" sub="08:55"/>
          <Arrow/>
          <LinChip ico="thermometer" label="Thermal seg" sub="conf 0.91"/>
          <Arrow/>
          <LinChip ico="link" label="Asset match" sub={det.module}/>
          <Arrow/>
          <LinChip ico="history" label="Temporal diff" sub={det.temporal.toLowerCase()}/>
          {det.review !== "pending" && <>
            <Arrow/>
            <LinChip ico="check" label={det.review} sub="you" tone={det.review === "rejected" ? "crit" : "ok"}/>
          </>}
        </div>
      </div>

      {/* Actions */}
      <div data-rv-actions="" style={{ marginTop: "auto", borderTop: "1px solid var(--border-base)", padding: "10px 14px", display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", rowGap: 6 }}>
        <span className="mono muted" style={{ fontSize: 11 }}>
          {det.assignee ? <>Assigned · <span style={{ color: "var(--fg-secondary)" }}>{det.assignee}</span></> : "Unassigned"}
        </span>
        <span className="grow"/>
        <button className="btn btn-ghost btn-xs" onClick={onAssign}><Icon name="users" size={12}/>Assign<kbd style={kbdStyle}>G</kbd></button>
        <button className="btn btn-secondary btn-xs" onClick={onReclassify}><Icon name="tag" size={12}/>Reclassify<kbd style={kbdStyle}>C</kbd></button>
        <button className="btn btn-danger btn-xs" onClick={onReject}><Icon name="x" size={12}/>Reject<kbd style={kbdStyle}>R</kbd></button>
        <button className="btn btn-secondary btn-xs" onClick={() => window.dispatchEvent(new CustomEvent("skyops:new-wo",{detail:{ title: det.label + " — " + (det.severity === "critical" ? "urgent fix" : "repair"), loc: det.string + " · " + det.module, obs: det.id, mic: localStorage.getItem("skyops_mic") || "", siteId: localStorage.getItem("skyops_site") || undefined, prio: det.severity === "critical" ? "P1" : det.severity === "high" ? "P2" : "P3", type: /string|inverter/i.test(det.label) ? "Electrical" : /soil|bird/i.test(det.label) ? "Cleaning" : /vegetation/i.test(det.label) ? "Vegetation" : /tracker|racking/i.test(det.label) ? "Tracker" : "Module replace" }}))}><Icon name="flag" size={12}/>Work order</button>
        <button className="btn btn-info btn-xs" onClick={onAccept}><Icon name="check" size={12}/>Accept<kbd style={kbdStyle}>A</kbd></button>
      </div>
    </>
  );
}

function LinChip({ ico, label, sub, tone }) {
  return (
    <div style={{ display:"flex", alignItems:"center", gap:6, background: "var(--bg-inset)", border: "1px solid var(--border-base)", padding: "4px 8px", borderRadius: 6, minWidth: 0 }}>
      <Icon name={ico} size={11} style={{ color: tone === "ok" ? "var(--health-ok)" : tone === "crit" ? "var(--sev-critical)" : "var(--fg-muted)" }}/>
      <div style={{ display: "flex", flexDirection: "column" }}>
        <span style={{ fontSize: 10, fontWeight: 500 }}>{label}</span>
        <span className="mono muted" style={{ fontSize: 9 }}>{sub}</span>
      </div>
    </div>
  );
}

const Arrow = () => <span className="muted" style={{ fontSize: 10 }}>→</span>;

function ImageFrame({ kind, det, overlay }) {
  const label = kind === "thermal" ? "Thermal · H30T" : "RGB · wide";
  return (
    <div style={{ position: "relative", aspectRatio: "4 / 3", background: "var(--bg-inset)", borderRadius: 8, overflow: "hidden", border: "1px solid var(--border-base)" }}>
      <div className={kind === "thermal" ? "thermal-ph" : "rgb-ph"} style={{ position: "absolute", inset: 0 }}/>
      {overlay && kind === "thermal" && (
        <svg width="100%" height="100%" viewBox="0 0 400 300" preserveAspectRatio="none" style={{ position: "absolute", inset: 0 }}>
          <rect x="150" y="100" width="50" height="70" fill="none" stroke="#fff" strokeWidth="1.5" strokeDasharray="4 3"/>
          <text x="155" y="94" fontSize="10" fill="#fff" fontFamily="var(--font-mono)" fontWeight="600">
            {det.label.toUpperCase()} · +{det.dT.toFixed(1)}°C
          </text>
          <line x1="175" y1="135" x2="300" y2="40" stroke="#fff" strokeWidth="1" opacity="0.6"/>
          <circle cx="175" cy="135" r="3" fill="#fff"/>
        </svg>
      )}
      {overlay && kind === "rgb" && (
        <svg width="100%" height="100%" viewBox="0 0 400 300" preserveAspectRatio="none" style={{ position: "absolute", inset: 0 }}>
          <rect x="150" y="100" width="50" height="70" fill="none" stroke="#4a90d9" strokeWidth="2"/>
          <text x="155" y="94" fontSize="10" fill="#4a90d9" fontFamily="var(--font-mono)" fontWeight="600">
            {det.module}
          </text>
        </svg>
      )}
      <div style={{ position: "absolute", top: 8, left: 8, background: "rgba(15,23,42,0.85)", padding: "3px 8px", borderRadius: 4, fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--fg-secondary)" }}>
        {label}
      </div>
      <div style={{ position: "absolute", bottom: 8, right: 8, background: "rgba(15,23,42,0.85)", padding: "3px 8px", borderRadius: 4, fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--fg-secondary)" }}>
        {det.lat}, {det.lng}
      </div>
      {kind === "thermal" && (
        <div style={{ position: "absolute", bottom: 8, left: 8, display: "flex", flexDirection: "column", alignItems: "center", background: "rgba(15,23,42,0.85)", padding: 6, borderRadius: 4 }}>
          <div style={{ width: 12, height: 60, background: "linear-gradient(180deg,#ef4444,#eab308,#22d3ee,#3b82f6)", borderRadius: 2 }}/>
          <span className="mono" style={{ fontSize: 9, color: "var(--fg-muted)", marginTop: 2 }}>°C</span>
        </div>
      )}
    </div>
  );
}

function ReclassifyModal({ det, onClose, onPick }) {
  if (!det) return null;
  const byGroup = {};
  Object.entries(ANOMALY_CLASS).forEach(([k, v]) => {
    (byGroup[v.group] = byGroup[v.group] || []).push([k, v.label]);
  });
  return (
    <div style={{ position: "fixed", inset: 0, background: "rgba(4,10,25,0.7)", display: "grid", placeItems: "center", zIndex: 250 }} onClick={onClose}>
      <div className="card" style={{ width: 520, maxHeight: "80vh", overflow: "auto" }} onClick={e => e.stopPropagation()}>
        <div className="card-hd">Reclassify <span className="sub mono">{det.id}</span>
          <div className="actions"><button className="btn btn-ghost btn-xs" onClick={onClose}><Icon name="x" size={12}/></button></div>
        </div>
        <div style={{ padding: 14 }}>
          <div className="sec" style={{ fontSize: 12, marginBottom: 12 }}>
            Current: <b>{det.label}</b>. Choose a new classification — this writes to the observation lineage.
          </div>
          {Object.entries(byGroup).map(([group, items]) => (
            <div key={group} style={{ marginBottom: 12 }}>
              <div className="caps" style={{ marginBottom: 6 }}>{group}</div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
                {items.map(([k, l]) => (
                  <button key={k} className="btn btn-secondary btn-xs"
                          style={{ borderColor: k === det.kind ? "var(--accent-primary)" : "var(--border-elevated)" }}
                          onClick={() => onPick(k)}>{l}</button>
                ))}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

window.ReviewPage = ReviewPage;
