const { useState, useEffect, useRef } = React;


// Auth via API key
// Inject spinner keyframe
if (!document.getElementById("wwyw-spin")) {
  const s = document.createElement("style");
  s.id = "wwyw-spin";
  s.textContent = "@keyframes spin { to { transform: rotate(360deg) } } @keyframes pulse { 0%,100%{opacity:.4} 50%{opacity:1} }";
  document.head.appendChild(s);
}

const API = "https://analyzer.wewatchyourwebsite.com/js";
function apiHeaders(extra={}) {
  const s = getSession();
  const h = { ...extra };
  if (s && s.apiKey) h["X-API-Key"] = s.apiKey;
  return h;
}

const PLANS = [
  { id:"basic",  name:"Basic",        price:49,  scans:1, followups:0, fixes:5,   color:"#38bdf8", desc:"Single scan + report" },
  { id:"standard", name:"Standard",   price:99,  scans:1, followups:3, fixes:20,  color:"#a78bfa", desc:"Full scan + 3 follow-ups", popular:true },
  { id:"pro",    name:"Professional", price:149, scans:1, followups:4, fixes:999, color:"#f97316", desc:"Full scan + 4 follow-ups + unlimited fixes" },
];

const SEVERITY_COLOR = { critical:"#ef4444", high:"#f97316", medium:"#eab308", low:"#22c55e", informational: "#94a3b8" };
const SEVERITY_BG    = { critical:"#fef2f2", high:"#fff7ed", medium:"#fefce8", low:"#f0fdf4", informational:"#f8fafc" };
const SOURCE_LABEL   = { secrets:"Secrets", semgrep:"AST Analysis", dep_audit:"Dependency Audit", llm_review:"LLM Review", ast_scan:"Bundle AST" };

// ── Session helpers ───────────────────────────────────────────────────────────
function getSession() {
  return JSON.parse(localStorage.getItem("wwyw_session") || "null");
}
function setSession(data) {
  localStorage.setItem("wwyw_session", JSON.stringify(data));
}

// ── Auth / Plan Selection ─────────────────────────────────────────────────────
function Login({ onAuth }) {
  const [key, setKey] = useState(""); const [err, setErr] = useState(""); const [checking, setChecking] = useState(false);
  const session = getSession();
  if (session) {
    onAuth(session);
    return null;
  }
  const doLogin = async () => {
    if (!key.trim()) { setErr("Please enter your API key"); return; }
    setChecking(true); setErr("");
    try {
      const res = await fetch(API + "/jobs/auth-check", { headers: { "X-API-Key": key.trim() } });
      if (res.status === 403 || res.status === 401) { setErr("Invalid API key"); setChecking(false); return; }
      const plan = { plan:"api", price:0, scansRemaining:999, fixesRemaining:999, apiKey:key.trim(), purchasedAt:new Date().toISOString() };
      setSession(plan);
      onAuth(plan);
    } catch(e) { setErr("Connection error"); }
    setChecking(false);
  };
  return (
    <div style={{ minHeight:"100vh", background:"#0f172a", display:"flex", alignItems:"center", justifyContent:"center" }}>
      <div style={{ background:"#1e293b", borderRadius:12, padding:40, width:380, boxShadow:"0 4px 32px #0008" }}>
        <div style={{ color:"#38bdf8", fontWeight:700, fontSize:22, marginBottom:2 }}>WWYW</div>
        <div style={{ color:"#94a3b8", fontSize:13, marginBottom:28 }}>JavaScript Security Analyzer</div>
        <label style={{ color:"#64748b", fontSize:11, marginBottom:6, display:"block" }}>API Key</label>
        <input type="password" placeholder="Enter your API key" value={key}
          onChange={e=>{ setKey(e.target.value); setErr(""); }}
          onKeyDown={e=>e.key==="Enter"&&doLogin()}
          style={{ width:"100%", padding:"10px 12px", borderRadius:8, outline:"none", boxSizing:"border-box",
            border:err?"1px solid #ef4444":"1px solid #334155", background:"#0f172a", color:"#f1f5f9", fontSize:14 }} />
        {err && <div style={{ color:"#ef4444", fontSize:12, marginTop:6 }}>{err}</div>}
        <button onClick={doLogin} disabled={checking}
          style={{ marginTop:14, width:"100%", padding:"10px 0", background:checking?"#334155":"#38bdf8", color:"#0f172a",
            fontWeight:700, border:"none", borderRadius:8, cursor:checking?"not-allowed":"pointer", fontSize:14 }}>
          {checking?"Verifying...":"Connect"}
        </button>
        <div style={{ marginTop:20, textAlign:"center", color:"#475569", fontSize:11 }}>
          Contact us to receive an API key
        </div>
      </div>
    </div>
  );
}

function PlanSelector({ onSelect, onBack }) {
  return (
    <div style={{ minHeight:"100vh", background:"#0f172a", display:"flex", flexDirection:"column",
      alignItems:"center", justifyContent:"center", padding:24 }}>
      <div style={{ color:"#38bdf8", fontWeight:800, fontSize:24, marginBottom:6 }}>WWYW Security Analysis</div>
      <div style={{ color:"#94a3b8", fontSize:14, marginBottom:40, textAlign:"center" }}>
        One-time payment. No subscription. Scan your AI-generated app before it goes live.
      </div>
      <div style={{ display:"flex", gap:20, flexWrap:"wrap", justifyContent:"center", maxWidth:900 }}>
        {PLANS.map(plan => (
          <div key={plan.id} style={{ background:"#1e293b", borderRadius:14, padding:28, width:240,
            border: plan.popular ? `2px solid ${plan.color}` : "2px solid #334155",
            position:"relative", boxShadow: plan.popular ? `0 0 24px ${plan.color}33` : "none" }}>
            {plan.popular && (
              <div style={{ position:"absolute", top:-12, left:"50%", transform:"translateX(-50%)",
                background:plan.color, color:"#0f172a", fontSize:10, fontWeight:800,
                padding:"2px 12px", borderRadius:20 }}>MOST POPULAR</div>
            )}
            <div style={{ color:plan.color, fontWeight:700, fontSize:16, marginBottom:4 }}>{plan.name}</div>
            <div style={{ color:"#f1f5f9", fontWeight:800, fontSize:36, marginBottom:2 }}>
              ${plan.price}
              <span style={{ fontSize:14, color:"#64748b", fontWeight:400 }}> once</span>
            </div>
            <div style={{ color:"#64748b", fontSize:12, marginBottom:20 }}>{plan.desc}</div>
            <div style={{ marginBottom:20 }}>
              {[
                `1 full security scan`,
                plan.followups > 0 ? `${plan.followups} follow-up scans` : null,
                `${plan.fixes === 999 ? "Unlimited" : plan.fixes} AI code fixes`,
                "Full findings report",
                "Remediation guidance",
                plan.id === "pro" ? "Priority support" : null,
              ].filter(Boolean).map((item, i) => (
                <div key={i} style={{ display:"flex", gap:8, alignItems:"center", marginBottom:8 }}>
                  <span style={{ color:plan.color, fontSize:14 }}>✓</span>
                  <span style={{ color:"#cbd5e1", fontSize:13 }}>{item}</span>
                </div>
              ))}
            </div>
            <button onClick={() => {
              const session = { plan: plan.id, price: plan.price, scansRemaining: plan.scans + plan.followups,
                fixesRemaining: plan.fixes, purchasedAt: new Date().toISOString() };
              setSession(session);
              onSelect(session);
            }}
              style={{ width:"100%", padding:"10px 0", background:plan.color, color:"#0f172a",
                fontWeight:700, border:"none", borderRadius:8, cursor:"pointer", fontSize:14 }}>
              Get Started
            </button>
          </div>
        ))}
      </div>
      {onBack && (
        <div onClick={onBack} style={{ marginTop:24, color:"#475569", fontSize:13, cursor:"pointer" }}>
          ← Back
        </div>
      )}
      <div style={{ marginTop:16, color:"#334155", fontSize:11, textAlign:"center" }}>
        Secure payment via Stripe · traef@wewatchyourwebsite.com
      </div>
    </div>
  );
}

// ── Status Badge ──────────────────────────────────────────────────────────────
function StatusBadge({ status, overall }) {
  const val = (overall||status||"").toLowerCase();
  const cfg = {
    block:{ bg:"#fef2f2", color:"#ef4444", label:"BLOCK" }, blocked:{ bg:"#fef2f2", color:"#ef4444", label:"BLOCK" },
    warn:{ bg:"#fff7ed", color:"#f97316", label:"WARN" }, pass:{ bg:"#f0fdf4", color:"#22c55e", label:"PASS" },
    running:{ bg:"#eff6ff", color:"#3b82f6", label:"RUNNING" }, queued:{ bg:"#f8fafc", color:"#94a3b8", label:"QUEUED" },
    failed:{ bg:"#fef2f2", color:"#ef4444", label:"FAILED" }, complete:{ bg:"#f0fdf4", color:"#22c55e", label:"DONE" },
  }[val] || { bg:"#f8fafc", color:"#94a3b8", label:val?.toUpperCase()||"?" };
  return <span style={{ background:cfg.bg, color:cfg.color, padding:"2px 10px", borderRadius:20, fontSize:11, fontWeight:700 }}>{cfg.label}</span>;
}

// ── Submit Panel ──────────────────────────────────────────────────────────────
function SubmitPanel({ onSubmit, loading, session }) {
  const [tab, setTab] = useState("url");
  const [url, setUrl] = useState("");
  const [bulk, setBulk] = useState("");
  const [file, setFile] = useState(null);
  const fileRef = useRef();
  const canScan = session.scansRemaining > 0;

  const submit = () => {
    if (!canScan) return;
    if (tab==="url" && url.trim()) onSubmit({ type:"url", value:url.trim() });
    if (tab==="bulk") { const urls=bulk.split("\n").map(s=>s.trim()).filter(Boolean); if(urls.length) onSubmit({type:"bulk",value:urls}); }
    if (tab==="zip" && file) onSubmit({ type:"zip", value:file });
  };

  const tabs = [{ id:"url", label:"URL" }, { id:"bulk", label:"Bulk" }, { id:"zip", label:"ZIP" }];

  return (
    <div style={{ background:"#1e293b", borderRadius:10, padding:16, marginBottom:16 }}>
      {/* Scan credits */}
      <div style={{ display:"flex", justifyContent:"space-between", marginBottom:12,
        background:"#0f172a", borderRadius:8, padding:"8px 12px" }}>
        <div style={{ fontSize:11, color:"#64748b" }}>
          Scans remaining: <span style={{ color: session.scansRemaining===0?"#ef4444":"#38bdf8", fontWeight:700 }}>
            {session.scansRemaining}
          </span>
        </div>
        <div style={{ fontSize:11, color:"#64748b" }}>
          Fixes remaining: <span style={{ color: session.fixesRemaining===0?"#ef4444":"#a78bfa", fontWeight:700 }}>
            {session.fixesRemaining===999?"∞":session.fixesRemaining}
          </span>
        </div>
      </div>

      {!canScan && (
        <div style={{ background:"#fef2f2", borderRadius:8, padding:"10px 12px", marginBottom:12,
          color:"#ef4444", fontSize:12, textAlign:"center" }}>
          No scans remaining.{" "}
          <span style={{ textDecoration:"underline", cursor:"pointer" }}
            onClick={() => { localStorage.removeItem("wwyw_session"); window.location.reload(); }}>
            Upgrade plan
          </span>
        </div>
      )}

      <div style={{ display:"flex", gap:4, marginBottom:12 }}>
        {tabs.map(t=>(
          <button key={t.id} onClick={()=>setTab(t.id)}
            style={{ padding:"4px 12px", borderRadius:6, border:"none", cursor:"pointer", fontSize:11,
              background:tab===t.id?"#38bdf8":"#0f172a", color:tab===t.id?"#0f172a":"#94a3b8", fontWeight:tab===t.id?700:400 }}>
            {t.label}
          </button>
        ))}
      </div>

      {tab==="url" && (
        <div style={{ display:"flex", gap:8 }}>
          <input value={url} onChange={e=>setUrl(e.target.value)} onKeyDown={e=>e.key==="Enter"&&submit()}
            placeholder="github.com/user/repo or https://site.com"
            style={{ flex:1, padding:"8px 11px", borderRadius:8, border:"1px solid #334155",
              background:"#0f172a", color:"#f1f5f9", fontSize:12, outline:"none" }} />
          <button onClick={submit} disabled={loading||!url.trim()||!canScan}
            style={{ padding:"8px 16px", background:(!canScan||loading)?"#334155":"#38bdf8", color:"#0f172a",
              fontWeight:700, border:"none", borderRadius:8, cursor:(!canScan||loading)?"not-allowed":"pointer", fontSize:12, whiteSpace:"nowrap" }}>
            {loading?"Scanning...":"Scan"}
          </button>
        </div>
      )}
      {tab==="bulk" && (
        <div>
          <textarea value={bulk} onChange={e=>setBulk(e.target.value)} rows={4}
            placeholder={"https://github.com/user/repo1\nhttps://github.com/user/repo2"}
            style={{ width:"100%", padding:"8px 11px", borderRadius:8, border:"1px solid #334155",
              background:"#0f172a", color:"#f1f5f9", fontSize:11, outline:"none", resize:"vertical", boxSizing:"border-box" }} />
          <button onClick={submit} disabled={loading||!bulk.trim()||!canScan}
            style={{ marginTop:8, padding:"8px 16px", background:(!canScan||loading)?"#334155":"#38bdf8",
              color:"#0f172a", fontWeight:700, border:"none", borderRadius:8, cursor:(!canScan||loading)?"not-allowed":"pointer", fontSize:12 }}>
            {loading?"Queuing...":`Scan ${bulk.split("\n").filter(s=>s.trim()).length} URLs`}
          </button>
        </div>
      )}
      {tab==="zip" && (
        <div style={{ display:"flex", gap:8, alignItems:"center" }}>
          <input type="file" accept=".zip" ref={fileRef} style={{ display:"none" }} onChange={e=>setFile(e.target.files[0])} />
          <button onClick={()=>fileRef.current.click()}
            style={{ padding:"8px 14px", background:"#0f172a", color:"#94a3b8",
              border:"1px solid #334155", borderRadius:8, cursor:"pointer", fontSize:12 }}>
            {file?file.name:"Choose ZIP"}
          </button>
          <button onClick={submit} disabled={loading||!file||!canScan}
            style={{ padding:"8px 14px", background:(!file||loading||!canScan)?"#334155":"#38bdf8",
              color:"#0f172a", fontWeight:700, border:"none", borderRadius:8,
              cursor:(!file||loading||!canScan)?"not-allowed":"pointer", fontSize:12 }}>
            {loading?"Uploading...":"Scan"}
          </button>
        </div>
      )}
    </div>
  );
}

// ── Job Card ──────────────────────────────────────────────────────────────────
function JobCard({ job, onSelect, selected }) {
  const src = job.source?.split("/").slice(-1)[0]||job.source||"Unknown";
  const s = job.summary||{};
  return (
    <div onClick={()=>onSelect(job)}
      style={{ background:selected?"#1e3a5f":"#1e293b", borderRadius:9, padding:"11px 14px",
        cursor:"pointer", marginBottom:8, border:selected?"1px solid #38bdf8":"1px solid transparent" }}>
      <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center" }}>
        <div style={{ color:"#f1f5f9", fontWeight:600, fontSize:13, flex:1, overflow:"hidden",
          textOverflow:"ellipsis", whiteSpace:"nowrap", marginRight:8 }}>{src}</div>
        <StatusBadge status={job.status} overall={job.overall} />
      </div>
      <div style={{ color:"#94a3b8", fontSize:10, marginTop:2, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>
        {job.source}
      </div>
      <div style={{ color:"#64748b", fontSize:9, marginTop:2, fontFamily:"monospace", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}
        onClick={(e)=>{ e.stopPropagation(); navigator.clipboard.writeText(job.job_id); }}
        title="Click to copy Job ID">
        {job.job_id}
      </div>
      {s.total>0 && (
        <div style={{ display:"flex", gap:8, marginTop:6 }}>
          {s.critical>0&&<span style={{ color:"#ef4444", fontSize:10, fontWeight:700 }}>● {s.critical} Crit</span>}
          {s.high>0&&<span style={{ color:"#f97316", fontSize:10, fontWeight:700 }}>● {s.high} High</span>}
          {s.medium>0&&<span style={{ color:"#eab308", fontSize:10 }}>● {s.medium} Med</span>}
          {s.low>0&&<span style={{ color:"#22c55e", fontSize:10 }}>● {s.low} Low</span>}
        </div>
      )}
      {(job.status==="running"||job.status==="queued")&&(
        <div style={{ marginTop:7, height:2, background:"#0f172a", borderRadius:2, overflow:"hidden" }}>
          <div style={{ height:"100%", width:job.status==="running"?"60%":"10%",
            background:"#38bdf8", borderRadius:2, animation:"pulse 1.5s infinite" }} />
        </div>
      )}
    </div>
  );
}

// ── Fix Confirm ───────────────────────────────────────────────────────────────
function FixConfirm({ finding, remaining, onConfirm, onCancel }) {
  return (
    <div style={{ position:"fixed", inset:0, background:"#0009", zIndex:200,
      display:"flex", alignItems:"center", justifyContent:"center" }}>
      <div style={{ background:"#1e293b", borderRadius:12, padding:26, width:360, boxShadow:"0 8px 40px #0008" }}>
        <div style={{ color:"#f1f5f9", fontWeight:700, fontSize:15, marginBottom:8 }}>Generate Code Fix</div>
        <div style={{ color:"#94a3b8", fontSize:13, lineHeight:1.6, marginBottom:14 }}>
          AI will generate a specific code fix for:<br/>
          <span style={{ color:"#f97316", fontWeight:600 }}>
            {finding.severity?.toUpperCase()} -- {(finding.message||finding.type||"").substring(0,55)}
          </span>
        </div>
        <div style={{ background:"#0f172a", borderRadius:8, padding:"10px 14px", marginBottom:16 }}>
          <div style={{ display:"flex", justifyContent:"space-between", marginBottom:4 }}>
            <span style={{ color:"#64748b", fontSize:12 }}>Fixes remaining on your plan</span>
            <span style={{ color:remaining<=2?"#ef4444":"#a78bfa", fontSize:12, fontWeight:700 }}>
              {remaining===999?"Unlimited":remaining}
            </span>
          </div>
          <div style={{ display:"flex", justifyContent:"space-between" }}>
            <span style={{ color:"#64748b", fontSize:12 }}>API cost</span>
            <span style={{ color:"#38bdf8", fontSize:12, fontWeight:700 }}>~$0.004</span>
          </div>
        </div>
        {remaining===0 ? (
          <div style={{ color:"#ef4444", fontSize:12, textAlign:"center", marginBottom:12 }}>
            No fixes remaining on your plan.{" "}
            <span style={{ textDecoration:"underline", cursor:"pointer" }}
              onClick={()=>{ localStorage.removeItem("wwyw_session"); window.location.reload(); }}>
              Upgrade
            </span>
          </div>
        ) : (
          <div style={{ display:"flex", gap:10 }}>
            <button onClick={onCancel}
              style={{ flex:1, padding:"9px 0", background:"#0f172a", color:"#94a3b8",
                border:"1px solid #334155", borderRadius:8, cursor:"pointer", fontSize:13 }}>Cancel</button>
            <button onClick={onConfirm}
              style={{ flex:2, padding:"9px 0", background:"#38bdf8", color:"#0f172a",
                fontWeight:700, border:"none", borderRadius:8, cursor:"pointer", fontSize:13 }}>Generate Fix</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ── Finding Row ───────────────────────────────────────────────────────────────
function FindingRow({ f, onFix }) {
  const [open, setOpen] = useState(false);
  const sev = f.severity?.toLowerCase();
  return (
    <div style={{ borderRadius:8, border:`1px solid ${SEVERITY_COLOR[sev]}33`,
      background:SEVERITY_BG[sev]||"#f8fafc", marginBottom:7, overflow:"hidden" }}>
      <div onClick={()=>setOpen(!open)} style={{ padding:"9px 13px", cursor:"pointer",
        display:"flex", justifyContent:"space-between", alignItems:"center" }}>
        <div style={{ display:"flex", gap:8, alignItems:"center", flex:1, minWidth:0 }}>
          <span style={{ background:SEVERITY_COLOR[sev], color:"#fff", padding:"1px 7px",
            borderRadius:10, fontSize:10, fontWeight:700, textTransform:"uppercase", whiteSpace:"nowrap" }}>{sev}</span>
          <span style={{ fontSize:11, color:"#64748b", whiteSpace:"nowrap" }}>{SOURCE_LABEL[f.source]||f.source}</span>
          <span style={{ fontSize:12, color:"#1e293b", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>
            {f.message||f.title||f.description||f.type}
          </span>
        </div>
        <span style={{ color:"#94a3b8", fontSize:11, marginLeft:8 }}>{open?"▲":"▼"}</span>
      </div>
      {open && (
        <div style={{ padding:"0 13px 13px", borderTop:"1px solid #e2e8f0" }}>
          {f.file&&<div style={{ fontSize:11, color:"#64748b", marginTop:7 }}>
            <b>File:</b> {f.file.split("/").slice(-3).join("/")} {f.line?`(line ${f.line})`:""}
          </div>}
          {(f.detail||f.description)&&<div style={{ fontSize:12, color:"#334155", marginTop:5, lineHeight:1.5 }}>{f.detail||f.description}</div>}
          {f.fix&&<div style={{ fontSize:12, color:"#166534", background:"#f0fdf4", padding:"6px 10px", borderRadius:6, marginTop:7 }}>
            <b>Fix:</b> {f.fix}
          </div>}
          {(f.preview||f.snippet)&&(
            <pre style={{ background:"#0f172a", color:"#7dd3fc", padding:"7px 11px", borderRadius:6,
              fontSize:10, marginTop:7, overflow:"auto", whiteSpace:"pre-wrap" }}>{f.preview||f.snippet}</pre>
          )}
          <button onClick={()=>onFix(f)}
            style={{ marginTop:9, padding:"5px 13px", background:"#38bdf8", color:"#0f172a",
              fontWeight:700, border:"none", borderRadius:6, cursor:"pointer", fontSize:11 }}>
            ✦ Generate Fix
          </button>
        </div>
      )}
    </div>
  );
}


// ── Reviewed & Dismissed (False Positives) ───────────────────────────────────
function ReviewedDismissed({ items }) {
  const [open, setOpen] = useState(false);
  if (!items || items.length === 0) return null;
  return (
    <div style={{ marginTop:20 }}>
      <div onClick={()=>setOpen(!open)}
        style={{ display:"flex", alignItems:"center", gap:8, cursor:"pointer",
          padding:"10px 14px", background:"#0f172a", borderRadius:8,
          border:"1px solid #1e293b" }}>
        <span style={{ color:"#22c55e", fontSize:14, fontWeight:600 }}>✓</span>
        <span style={{ color:"#94a3b8", fontSize:13, fontWeight:500, flex:1 }}>
          Reviewed & Dismissed — {items.length} finding{items.length!==1?"s":""} analyzed and determined to be false positive{items.length!==1?"s":""}
        </span>
        <span style={{ color:"#64748b", fontSize:11, transform:open?"rotate(90deg)":"none",
          transition:"transform 0.2s" }}>▶</span>
      </div>
      {open && (
        <div style={{ marginTop:8, display:"flex", flexDirection:"column", gap:6 }}>
          {items.map((fp, i) => (
            <div key={i} style={{ background:"#0f172a", borderRadius:8, padding:"12px 14px",
              border:"1px solid #1e293b", borderLeft:"3px solid #22c55e" }}>
              <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:6 }}>
                <span style={{ background:"#14532d", color:"#4ade80", padding:"2px 8px",
                  borderRadius:10, fontSize:10, fontWeight:600 }}>FALSE POSITIVE</span>
                <span style={{ background:"#1e293b", color:"#64748b", padding:"2px 8px",
                  borderRadius:10, fontSize:10 }}>{fp.original_severity?.toUpperCase()}</span>
                <span style={{ color:"#94a3b8", fontSize:12, fontFamily:"monospace" }}>
                  {fp.file}:{fp.line}
                </span>
              </div>
              <div style={{ color:"#cbd5e1", fontSize:12, marginBottom:6 }}>{fp.description}</div>
              <div style={{ display:"flex", gap:6, alignItems:"flex-start" }}>
                <span style={{ color:"#22c55e", fontSize:11, fontWeight:600, whiteSpace:"nowrap",
                  marginTop:1 }}>Why FP:</span>
                <span style={{ color:"#94a3b8", fontSize:11, lineHeight:"1.5" }}>{fp.fp_reason}</span>
              </div>
              <div style={{ marginTop:4 }}>
                <span style={{ background:"#1a2332", color:"#64748b", padding:"2px 8px",
                  borderRadius:4, fontSize:10 }}>{fp.fp_category?.replace(/_/g, " ")}</span>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Attack Chain Step ─────────────────────────────────────────────────────────
function ChainStep({ step, isLast }) {
  const [open, setOpen] = useState(false);
  return (
    <div style={{ display:"flex", gap:12 }}>
      <div style={{ display:"flex", flexDirection:"column", alignItems:"center", flexShrink:0 }}>
        <div style={{ width:28, height:28, borderRadius:"50%", background:"#1e3a5f",
          border:"2px solid #38bdf8", display:"flex", alignItems:"center", justifyContent:"center",
          color:"#38bdf8", fontWeight:700, fontSize:12, zIndex:1 }}>
          {step.step}
        </div>
        {!isLast && <div style={{ width:2, flex:1, background:"#1e293b", minHeight:24, marginTop:4 }} />}
      </div>
      <div style={{ flex:1, paddingBottom: isLast ? 0 : 20 }}>
        <div onClick={() => setOpen(!open)}
          style={{ background:"#0f172a", borderRadius:8, padding:"10px 14px",
            cursor:"pointer", border:"1px solid #1e293b" }}>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center" }}>
            <span style={{ color:"#e2e8f0", fontSize:13, fontWeight:600 }}>{step.action}</span>
            <span style={{ color:"#475569", fontSize:11 }}>{open ? "▲" : "▼"}</span>
          </div>
          {open && (
            <div style={{ marginTop:10 }}>
              {step.technical_detail && (
                <div style={{ marginBottom:8 }}>
                  <div style={{ color:"#64748b", fontSize:10, fontWeight:700,
                    textTransform:"uppercase", marginBottom:4 }}>Technical Detail</div>
                  <div style={{ color:"#94a3b8", fontSize:12, lineHeight:1.6 }}>
                    {step.technical_detail}
                  </div>
                </div>
              )}
              {step.payload_example && (
                <div style={{ marginBottom:8 }}>
                  <div style={{ color:"#64748b", fontSize:10, fontWeight:700,
                    textTransform:"uppercase", marginBottom:4 }}>Payload / PoC</div>
                  <pre style={{ background:"#0a0f1a", color:"#7dd3fc", padding:"8px 12px",
                    borderRadius:6, fontSize:11, overflowX:"auto", whiteSpace:"pre-wrap",
                    margin:0, border:"1px solid #1e3a5f" }}>
                    {step.payload_example}
                  </pre>
                </div>
              )}
              {step.connects_to && !isLast && (
                <div style={{ marginTop:6, display:"flex", alignItems:"center", gap:6 }}>
                  <span style={{ color:"#38bdf8", fontSize:11 }}>&#8594;</span>
                  <span style={{ color:"#38bdf8", fontSize:11, fontStyle:"italic" }}>
                    {step.connects_to}
                  </span>
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}


// ── Attack Chain Card ─────────────────────────────────────────────────────────
function ChainCard({ chain }) {
  const [open, setOpen] = useState(false);
  const sevColor = SEVERITY_COLOR[chain.severity] || "#94a3b8";
  const likelihoodColor = { high:"#ef4444", medium:"#eab308", low:"#22c55e" }[chain.likelihood] || "#94a3b8";

  return (
    <div style={{ background:"#1e293b", borderRadius:10, marginBottom:16,
      border:"1px solid #334155", borderLeft:"4px solid " + sevColor,
      overflow:"hidden" }}>
      <div onClick={() => setOpen(!open)}
        style={{ padding:"14px 16px", cursor:"pointer",
          display:"flex", justifyContent:"space-between", alignItems:"flex-start" }}>
        <div style={{ flex:1, marginRight:12 }}>
          <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:6 }}>
            <span style={{ background:sevColor, color:"#fff", padding:"2px 8px",
              borderRadius:10, fontSize:10, fontWeight:700,
              textTransform:"uppercase" }}>{chain.severity}</span>
            <span style={{ background:"#0f172a", color:likelihoodColor,
              padding:"2px 8px", borderRadius:10, fontSize:10, border:"1px solid " + likelihoodColor }}>
              {chain.likelihood} likelihood
            </span>
            <span style={{ background:"#0f172a", color:"#64748b",
              padding:"2px 8px", borderRadius:10, fontSize:10 }}>
              {chain.steps ? chain.steps.length : 0} steps
            </span>
          </div>
          <div style={{ color:"#f1f5f9", fontWeight:700, fontSize:15,
            marginBottom:6 }}>{chain.title}</div>
          <div style={{ color:"#94a3b8", fontSize:12, lineHeight:1.6 }}>
            {chain.attacker_goal}
          </div>
        </div>
        <span style={{ color:"#475569", fontSize:12, flexShrink:0 }}>{open ? "▲" : "▼"}</span>
      </div>

      {open && (
        <div style={{ borderTop:"1px solid #334155", padding:"16px" }}>
          <div style={{ background:"#0f172a", borderRadius:8, padding:"12px 14px",
            marginBottom:16, borderLeft:"3px solid #a78bfa" }}>
            <div style={{ color:"#a78bfa", fontSize:10, fontWeight:700,
              textTransform:"uppercase", marginBottom:6 }}>Executive Summary</div>
            <div style={{ color:"#cbd5e1", fontSize:13, lineHeight:1.7 }}>
              {chain.executive_summary}
            </div>
          </div>

          <div style={{ color:"#64748b", fontSize:10, fontWeight:700,
            textTransform:"uppercase", marginBottom:12 }}>Attack Steps</div>
          <div>
            {(chain.steps || []).map((step, i) => (
              <ChainStep key={i} step={step} isLast={i === chain.steps.length - 1} />
            ))}
          </div>

          <div style={{ display:"flex", gap:10, marginTop:16, flexWrap:"wrap" }}>
            {chain.combined_impact && (
              <div style={{ flex:1, minWidth:200, background:"#fef2f2",
                borderRadius:8, padding:"10px 14px" }}>
                <div style={{ color:"#ef4444", fontSize:10, fontWeight:700,
                  textTransform:"uppercase", marginBottom:4 }}>Business Impact</div>
                <div style={{ color:"#334155", fontSize:12, lineHeight:1.5 }}>
                  {chain.combined_impact}
                </div>
              </div>
            )}
            {chain.remediation_priority && (
              <div style={{ flex:1, minWidth:200, background:"#f0fdf4",
                borderRadius:8, padding:"10px 14px" }}>
                <div style={{ color:"#22c55e", fontSize:10, fontWeight:700,
                  textTransform:"uppercase", marginBottom:4 }}>Fix This First</div>
                <div style={{ color:"#334155", fontSize:12, lineHeight:1.5 }}>
                  {chain.remediation_priority}
                </div>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}


// ── Attack Chains Panel ───────────────────────────────────────────────────────
function AttackChainsPanel({ data }) {
  if (!data) return (
    <div style={{ color:"#64748b", fontSize:13, textAlign:"center", padding:40 }}>
      No attack chain data available.
    </div>
  );

  if (data.stage8_skipped) return (
    <div style={{ color:"#64748b", fontSize:13, textAlign:"center", padding:40 }}>
      {data.attack_surface_summary || "No chains constructed."}
    </div>
  );

  const chains = data.chains || [];
  const unchained = data.unchained_criticals || [];

  return (
    <div>
      {data.attack_surface_summary && (
        <div style={{ background:"#1e293b", borderRadius:8, padding:"12px 16px",
          marginBottom:20, borderLeft:"3px solid #f97316" }}>
          <div style={{ color:"#f97316", fontSize:10, fontWeight:700,
            textTransform:"uppercase", marginBottom:6 }}>Attack Surface Summary</div>
          <div style={{ color:"#94a3b8", fontSize:13, lineHeight:1.6 }}>
            {data.attack_surface_summary}
          </div>
        </div>
      )}

      {chains.length === 0 && (
        <div style={{ color:"#64748b", fontSize:13, textAlign:"center", padding:20 }}>
          No multi-step chains identified.
        </div>
      )}
      {chains.map((chain, i) => <ChainCard key={i} chain={chain} />)}

      {unchained.length > 0 && (
        <div style={{ marginTop:8 }}>
          <div style={{ color:"#64748b", fontSize:11, fontWeight:700,
            textTransform:"uppercase", marginBottom:10 }}>
            Standalone Critical Findings
          </div>
          {unchained.map((u, i) => (
            <div key={i} style={{ background:"#1e293b", borderRadius:8,
              padding:"12px 14px", marginBottom:8,
              borderLeft:"4px solid #ef4444" }}>
              <div style={{ color:"#f1f5f9", fontSize:13,
                fontWeight:600, marginBottom:4 }}>{u.description}</div>
              <div style={{ color:"#94a3b8", fontSize:12 }}>
                {u.standalone_impact}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Report Panel ──────────────────────────────────────────────────────────────
function ReportPanel({ job, onFix }) {
  const [filter, setFilter] = useState("all");
  const [view, setView] = useState("findings");
  const r = job.report;
  if (!r) return (
    <div style={{ color:"#64748b", fontSize:13, padding:20, textAlign:"center" }}>
      {job.status==="running"?"Analysis in progress...":
       job.status==="queued"?"Waiting to start...":
       job.status==="failed"?`Failed: ${job.error}`:"No report available"}
    </div>
  );
  const s=r.summary||{}; const findings=r.findings||[];
  const filtered = filter==="all"?findings:findings.filter(f=>f.severity?.toLowerCase()===filter);
  const sevCounts = ["critical","high","medium","low"].filter(sv=>(s[sv]||0)>0);
  return (
    <div>
      <div style={{ display:"flex", gap:10, marginBottom:16, flexWrap:"wrap" }}>
        {[{label:"Files",val:r.files_scanned,color:"#38bdf8"},{label:"Framework",val:r.framework?.replace("Framework.",""),color:"#a78bfa"},
			{label:"Critical",val:s.critical||0,color:"#ef4444"},{label:"High",val:s.high||0,color:"#f97316"},
			{label:"Medium",val:s.medium||0,color:"#eab308"},{label:"Low",val:s.low||0,color:"#22c55e"},
			{label:"Chains",val:(r.attack_chains?.chains||[]).length||0,color:"#f472b6"},
		].map(item=>(
          <div key={item.label} style={{ background:"#1e293b", borderRadius:8, padding:"9px 13px", minWidth:62, textAlign:"center" }}>
            <div style={{ color:item.color, fontWeight:700, fontSize:17 }}>{item.val??"-"}</div>
            <div style={{ color:"#64748b", fontSize:10 }}>{item.label}</div>
          </div>
        ))}
      </div>
      {findings.length>0&&(
        <div style={{ display:"flex", gap:8, marginBottom:14 }}>
          {["findings","compliance","attack chains"].map(v=>(
            <button key={v} onClick={()=>setView(v)}
              style={{ padding:"5px 14px", borderRadius:6, border:view===v?"1px solid #38bdf8":"1px solid #334155",
                background:view===v?"#1e3a5f":"#0f172a", color:view===v?"#38bdf8":"#64748b",
                cursor:"pointer", fontSize:12, fontWeight:view===v?700:400, textTransform:"capitalize" }}>
              {v}
            </button>
          ))}
        </div>
      )}
      {view==="compliance"&&findings.length>0&&(()=>{
        const owaspGroups = {};
        findings.forEach(fi=>{
          const owasp = fi.owasp || "Unmapped";
          if (!owaspGroups[owasp]) owaspGroups[owasp] = [];
          owaspGroups[owasp].push(fi);
        });
        const sorted = Object.entries(owaspGroups).sort((a,b)=>{
          if (a[0]==="Unmapped") return 1;
          if (b[0]==="Unmapped") return -1;
          return b[1].length - a[1].length;
        });
        const owaspColors = {
          "A01:2021 Broken Access Control":"#ef4444",
          "A02:2021 Cryptographic Failures":"#f97316",
          "A03:2021 Injection":"#eab308",
          "A04:2021 Insecure Design":"#a78bfa",
          "A07:2021 Identification and Auth Failures":"#38bdf8",
          "A08:2021 Software and Data Integrity Failures":"#f472b6",
          "A10:2021 SSRF":"#22c55e",
        };
        return (
          <div>
            <div style={{ background:"#1e293b", borderRadius:10, padding:16, marginBottom:16 }}>
              <div style={{ color:"#f1f5f9", fontWeight:700, fontSize:14, marginBottom:12 }}>OWASP Top 10 Coverage</div>
              <div style={{ display:"flex", gap:6, flexWrap:"wrap" }}>
                {sorted.filter(([k])=>k!=="Unmapped").map(([owasp, items])=>(
                  <div key={owasp} style={{ background:"#0f172a", borderRadius:8, padding:"10px 14px",
                    borderLeft:`3px solid ${owaspColors[owasp]||"#64748b"}`, flex:"1 1 200px" }}>
                    <div style={{ color:owaspColors[owasp]||"#64748b", fontSize:11, fontWeight:700 }}>{owasp}</div>
                    <div style={{ color:"#f1f5f9", fontSize:22, fontWeight:800, margin:"4px 0" }}>{items.length}</div>
                    <div style={{ color:"#64748b", fontSize:10 }}>
                      {items.filter(x=>x.severity==="critical").length} critical, {items.filter(x=>x.severity==="high").length} high
                    </div>
                  </div>
                ))}
              </div>
              {sorted.some(([k])=>k==="Unmapped")&&(
                <div style={{ color:"#475569", fontSize:11, marginTop:10 }}>
                  {sorted.find(([k])=>k==="Unmapped")[1].length} findings not yet mapped to OWASP (dependency advisories)
                </div>
              )}
            </div>
            {sorted.map(([owasp, items])=>(
              <div key={owasp} style={{ marginBottom:16 }}>
                <div style={{ color:owaspColors[owasp]||"#64748b", fontWeight:700, fontSize:13, marginBottom:8,
                  display:"flex", alignItems:"center", gap:8 }}>
                  <span>{owasp}</span>
                  <span style={{ background:"#1e293b", padding:"2px 8px", borderRadius:10, fontSize:11,
                    color:"#94a3b8", fontWeight:400 }}>{items.length} finding{items.length!==1?"s":""}</span>
                </div>
                {items.map((fi,i)=>(
                  <div key={i} style={{ background:"#1e293b", borderRadius:6, padding:"8px 12px", marginBottom:4,
                    display:"flex", justifyContent:"space-between", alignItems:"center" }}>
                    <div style={{ flex:1 }}>
                      <span style={{ color:SEVERITY_COLOR[fi.severity]||"#94a3b8", fontSize:10, fontWeight:700,
                        marginRight:8 }}>{fi.severity?.toUpperCase()}</span>
                      <span style={{ color:"#e2e8f0", fontSize:12 }}>{(fi.message||fi.title||fi.type||"").substring(0,70)}</span>
                      {fi.cwe&&<span style={{ color:"#475569", fontSize:10, marginLeft:8 }}>{fi.cwe}</span>}
                    </div>
                    <span style={{ color:"#475569", fontSize:10 }}>{fi.source}</span>
                  </div>
                ))}
              </div>
            ))}
          </div>
        );
      })()}
	  {view==="attack chains"&&(
		<AttackChainsPanel data={r.attack_chains} />
	  )}
      {view==="findings"&&<>
      {findings.length>0&&(
        <div style={{ display:"flex", gap:5, marginBottom:11, flexWrap:"wrap" }}>
          {["all",...sevCounts].map(sv=>(
            <button key={sv} onClick={()=>setFilter(sv)}
              style={{ padding:"3px 11px", borderRadius:20, border:"none", cursor:"pointer", fontSize:11,
                background:filter===sv?(SEVERITY_COLOR[sv]||"#38bdf8"):"#1e293b",
                color:filter===sv?"#fff":"#94a3b8", fontWeight:filter===sv?700:400 }}>
              {sv==="all"?`All (${findings.length})`:`${sv} (${s[sv]||0})`}
            </button>
          ))}
        </div>
      )}
      {filtered.length===0&&<div style={{ color:"#64748b", fontSize:13, textAlign:"center", padding:20 }}>No findings for this filter.</div>}
      {filtered.map((f,i)=><FindingRow key={i} f={f} onFix={onFix} />)}
      <ReviewedDismissed items={r.reviewed_and_dismissed} />
      </>}
    </div>
  );
}

// ── Fix Slide-over ────────────────────────────────────────────────────────────
function FixPanel({ finding, onClose }) {
  const [fix, setFix] = useState(""); const [loading, setLoading] = useState(false);
  useEffect(() => {
    if (!finding) return;
    setFix(""); setLoading(true);
    fetch("https://analyzer.wewatchyourwebsite.com/js/remediate", {
      method:"POST", headers: apiHeaders({"Content-Type":"application/json"}),
      body: JSON.stringify({
        model:"claude-sonnet-4-20250514", max_tokens:1000,
        messages:[{role:"user", content:
          `You are a senior security engineer. Generate a specific code fix for this vulnerability.
Severity: ${finding.severity}
Source: ${finding.source||"unknown"}
Type: ${finding.type||finding.rule_id||finding.pattern||"unknown"}
${finding.source==="dep_audit" ? `Package: ${finding.package||"unknown"}
Vulnerable versions: ${finding.version||"unknown"}
Patched in: ${finding.patched_in||"unknown"}
CVSS: ${finding.cvss||"unknown"}
Vuln ID: ${finding.vuln_id||"unknown"}
Detector: ${finding.detector||"unknown"}` : `File: ${finding.file||"unknown"} Line: ${finding.line||"unknown"}
Code: ${finding.preview||finding.snippet||"not available"}`}
Issue: ${finding.message||finding.title||finding.description}
Detail: ${finding.detail||finding.description||""}
Fix guidance: ${finding.fix||"none"}

${finding.source==="dep_audit" ?
"IMPORTANT: Sanity-check the patched_in version against the vulnerable range. If patched_in is LOWER than the vulnerable range (e.g. vulnerable <=7.5.0 but patched_in says 3.20.2), flag this as suspicious advisory data and recommend the user verify the correct patched version on GitHub/npm before upgrading. Provide: 1) Why this specific version is vulnerable (cite the CVE/GHSA if known, 2-3 sentences) 2) Exact upgrade command (npm/yarn) — but ONLY if the patched version makes sense relative to the vulnerable range 3) Breaking changes to watch for when upgrading 4) If a major version jump, mention migration steps." :
"Provide: 1) Why this is vulnerable (2-3 sentences) 2) Corrected code with comments 3) Additional steps needed."}`}]
      })
    })
    .then(r=>r.json()).then(d=>setFix(d.content?.[0]?.text||"No response"))
    .catch(e=>setFix(`Error: ${e.message}`)).finally(()=>setLoading(false));
  }, [finding]);
  if (!finding) return null;
  return (
    <div style={{ position:"fixed", top:0, right:0, bottom:0, width:480,
      background:"#1e293b", boxShadow:"-4px 0 24px #0008", zIndex:150, display:"flex", flexDirection:"column" }}>
      <div style={{ padding:"15px 18px", borderBottom:"1px solid #334155",
        display:"flex", justifyContent:"space-between", alignItems:"flex-start" }}>
        <div>
          <div style={{ color:"#f1f5f9", fontWeight:700, fontSize:14 }}>Generated Fix</div>
          <div style={{ color:"#64748b", fontSize:11, marginTop:2 }}>
            {finding.severity?.toUpperCase()} -- {finding.file?.split("/").slice(-1)[0]}
          </div>
        </div>
        <button onClick={onClose} style={{ background:"none", border:"none", color:"#64748b", fontSize:18, cursor:"pointer" }}>✕</button>
      </div>
      <div style={{ flex:1, overflow:"auto", padding:18 }}>
        {loading?<div style={{ color:"#38bdf8", fontSize:13, display:"flex", alignItems:"center", gap:10 }}><span style={{ display:"inline-block", width:16, height:16, border:"2px solid #334155", borderTop:"2px solid #38bdf8", borderRadius:"50%", animation:"spin 0.8s linear infinite" }}></span>Generating fix...</div>:
          <pre style={{ color:"#e2e8f0", fontSize:12, whiteSpace:"pre-wrap", lineHeight:1.7, margin:0 }}>{fix}</pre>}
      </div>
      {!loading&&fix&&(
        <div style={{ padding:"11px 18px", borderTop:"1px solid #334155", display:"flex", gap:8 }}>
          <button onClick={()=>navigator.clipboard.writeText(fix)}
            style={{ flex:1, padding:"8px 0", background:"#38bdf8", color:"#0f172a",
              fontWeight:700, border:"none", borderRadius:6, cursor:"pointer", fontSize:12 }}>Copy</button>
          <button onClick={onClose}
            style={{ padding:"8px 14px", background:"#0f172a", color:"#94a3b8",
              border:"1px solid #334155", borderRadius:6, cursor:"pointer", fontSize:12 }}>Close</button>
        </div>
      )}
    </div>
  );
}

// ── Main ──────────────────────────────────────────────────────────────────────
function App() {
  const [session, setSession] = useState(null);
  const [jobs, setJobs] = useState(() => {
    try {
      const saved = localStorage.getItem('wwyw_jobs');
      return saved ? JSON.parse(saved) : [];
    } catch { return []; }
  });

  // Persist jobs to localStorage whenever they change
  useEffect(() => {
    try {
      // Only save completed jobs to avoid storing stale running states
      const toSave = jobs.filter(j => ['complete','blocked','failed'].includes(j.status));
      localStorage.setItem('wwyw_jobs', JSON.stringify(toSave.slice(0, 20)));
    } catch(e) {}
  }, [jobs]);
  const [selected, setSelected] = useState(null);
  const [confirmFinding, setConfirmFinding] = useState(null);
  const [fixFinding, setFixFinding] = useState(null);
  const [loading, setLoading] = useState(false);
  const [sess, setSess] = useState(session);

  const handleAuth = (plan) => { setSession(plan); setSess(plan); };

  const handleSubmit = async ({ type, value }) => {
    if (!sess || sess.scansRemaining <= 0) return;
    setLoading(true);


    const sources = Array.isArray(value) ? value : [value];

    for (let i = 0; i < sources.length; i++) {
      const src = sources[i];
      try {
        let res, data;

        if (type === "zip") {
          const form = new FormData();
          form.append("file", src);
          res = await fetch(`${API}/analyze/upload`, { method:"POST", headers: apiHeaders(), body:form });
        } else {
          res = await fetch(`${API}/analyze`, {
            method:"POST",
            headers: apiHeaders({ "Content-Type":"application/json" }),
            body: JSON.stringify({ source: src, history_scan: false }),
          });
        }

        data = await res.json();
        const jobId = data.job_id;
        if (!jobId) throw new Error(data.detail || "No job ID returned");

        setJobs(prev => [{ job_id:jobId, source:src, status:"queued", overall:null, summary:null, report:null }, ...prev]);

        // Poll until complete
        const poll = setInterval(async () => {
          try {
            const sr = await fetch(`${API}/jobs/${jobId}`, { headers: apiHeaders() });
            const status = await sr.json();
            const done = ["complete","blocked","failed"].includes(status.status);

            if (done && (status.overall === "block" || status.overall === "warn" || status.overall === "pass")) {
              // Fetch full report
              const rr = await fetch(`${API}/jobs/${jobId}/report`, { headers: apiHeaders() });
              const report = await rr.json();
              setJobs(prev => prev.map(j => j.job_id === jobId ? {
                ...status, report, source: src
              } : j));
              // Decrement scan only on success
              setSess(prev => {
                const u = { ...prev, scansRemaining: prev.scansRemaining - 1 };
                localStorage.setItem("wwyw_session", JSON.stringify(u));
                setSession(u);
                return u;
              });
            } else {
              setJobs(prev => prev.map(j => j.job_id === jobId ? { ...j, ...status, source: src } : j));
            }

            if (done) clearInterval(poll);
          } catch(e) {
            clearInterval(poll);
          }
        }, 5000);

      } catch(e) {
        setJobs(prev => [{ job_id:`err-${Date.now()}`, source:src, status:"failed",
          error:e.message, overall:null, summary:null, report:null }, ...prev]);
      }
    }
    setLoading(false);
  };

  const handleFixConfirm = () => {
    if (!sess || sess.fixesRemaining === 0) return;
    const updated = { ...sess, fixesRemaining: sess.fixesRemaining===999?999:sess.fixesRemaining-1 };
    setSession(updated); setSess(updated);
    localStorage.setItem("wwyw_session", JSON.stringify(updated));
    setFixFinding(confirmFinding);
    setConfirmFinding(null);
  };

  const selectedJob = jobs.find(j=>j.job_id===selected?.job_id)||selected;
  const planInfo = PLANS.find(p=>p.id===sess?.plan);

  if (!sess) return <Login onAuth={handleAuth} />;

  return (
    <div style={{ minHeight:"100vh", background:"#0f172a", color:"#f1f5f9", fontFamily:"system-ui,sans-serif" }}>
      <style>{`@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}`}</style>
      <div style={{ background:"#1e293b", borderBottom:"1px solid #334155", padding:"11px 20px",
        display:"flex", alignItems:"center", justifyContent:"space-between" }}>
        <div style={{ display:"flex", alignItems:"center", gap:10 }}>
          <div style={{ color:"#38bdf8", fontWeight:800, fontSize:16 }}>WWYW</div>
          <div style={{ color:"#475569", fontSize:11 }}>JavaScript Security Analyzer</div>
        </div>
        <div style={{ display:"flex", gap:16, alignItems:"center" }}>
          {planInfo && <span style={{ background:"#0f172a", color:planInfo.color, padding:"2px 10px",
            borderRadius:20, fontSize:11, fontWeight:700 }}>{planInfo.name}</span>}
          <div style={{ color:"#475569", fontSize:11 }}>
            Scans: <span style={{ color:"#38bdf8", fontWeight:700 }}>{sess.scansRemaining}</span>
          </div>
          <div style={{ color:"#475569", fontSize:11 }}>
            Fixes: <span style={{ color:"#a78bfa", fontWeight:700 }}>{sess.fixesRemaining===999?"∞":sess.fixesRemaining}</span>
          </div>
          <span onClick={()=>{ localStorage.removeItem("wwyw_session"); window.location.reload(); }}
            style={{ color:"#334155", fontSize:11, cursor:"pointer" }}>Change plan</span>
        </div>
      </div>

      <div style={{ display:"flex", height:"calc(100vh - 47px)" }}>
        <div style={{ width:310, borderRight:"1px solid #1e293b", padding:14, overflow:"auto", flexShrink:0 }}>
          <SubmitPanel onSubmit={handleSubmit} loading={loading} session={sess} />
          {jobs.length===0&&(
            <div style={{ color:"#475569", fontSize:12, textAlign:"center", marginTop:28, lineHeight:1.8 }}>
              Submit a GitHub URL, live site,<br/>or ZIP to begin your scan.
            </div>
          )}
          {jobs.length>0&&(
            <div style={{ display:"flex", justifyContent:"flex-end", marginBottom:6, marginTop:4 }}>
              <span onClick={()=>{ if(confirm("Clear all scan history?")){ setJobs([]); setSelected(null); localStorage.removeItem("wwyw_jobs"); }}}
                style={{ color:"#475569", fontSize:10, cursor:"pointer", padding:"2px 6px",
                  borderRadius:4, border:"1px solid #334155" }}>Clear history</span>
            </div>
          )}
          {jobs.map(j=><JobCard key={j.job_id} job={j} selected={selectedJob?.job_id===j.job_id} onSelect={setSelected} />)}
        </div>
        <div style={{ flex:1, padding:22, overflow:"auto" }}>
          {!selectedJob?(
            <div style={{ display:"flex", flexDirection:"column", alignItems:"center",
              justifyContent:"center", height:"100%", color:"#475569" }}>
              <div style={{ fontSize:40, marginBottom:12 }}>🔍</div>
              <div style={{ fontSize:15, fontWeight:600 }}>Select a scan to view results</div>
              <div style={{ fontSize:12, marginTop:6 }}>Submit a repository or URL to begin</div>
            </div>
          ):(
            <div>
              <div style={{ display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:16 }}>
                <div>
                  <div style={{ fontSize:15, fontWeight:700 }}>{selectedJob.source?.split("/").slice(-1)[0]||selectedJob.source}</div>
                  <div style={{ fontSize:11, color:"#64748b", marginTop:2 }}>{selectedJob.source}</div>
                </div>
                <StatusBadge status={selectedJob.status} overall={selectedJob.overall} />
              </div>
              <ReportPanel job={selectedJob} onFix={setConfirmFinding} />
            </div>
          )}
        </div>
      </div>

      {confirmFinding&&<FixConfirm finding={confirmFinding} remaining={sess.fixesRemaining}
        onConfirm={handleFixConfirm} onCancel={()=>setConfirmFinding(null)} />}
      {fixFinding&&<FixPanel finding={fixFinding} onClose={()=>setFixFinding(null)} />}
    </div>
  );
}


ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));