/* Cripple.AI Dashboard — war-room console */
// Lazy proxy so accesses read window.CRIPPLE_DATA after fetch resolves (see shared.jsx)
const DATA = new Proxy({}, { get: (_, k) => window.CRIPPLE_DATA?.[k] });

/* -------------- tiny chart primitives -------------- */

function AreaChart({ series, width = 800, height = 240, color = 'hsl(138 85% 55%)' }) {
  // series: [{label, data:[n...]}]
  const all = series.flatMap(s => s.data);
  const max = Math.max(...all, 1);
  const pad = 20;
  const plotH = height - pad * 2;
  const plotW = width - pad * 2;
  const steps = series[0].data.length;
  const x = i => pad + (i / (steps - 1)) * plotW;
  const y = v => pad + plotH - (v / max) * plotH;
  return (
    <svg viewBox={`0 0 ${width} ${height}`} width="100%" height={height} preserveAspectRatio="none">
      <defs>
        {series.map((s, i) => (
          <linearGradient key={i} id={`ga${i}`} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={s.color || color} stopOpacity="0.35"/>
            <stop offset="100%" stopColor={s.color || color} stopOpacity="0"/>
          </linearGradient>
        ))}
      </defs>
      {/* grid */}
      {[0,0.25,0.5,0.75,1].map((p,i) => (
        <line key={i} x1={pad} x2={width-pad} y1={pad + p*plotH} y2={pad + p*plotH} stroke="rgba(255,255,255,0.06)" strokeDasharray="2 4"/>
      ))}
      {series.map((s, i) => {
        const pts = s.data.map((v, j) => `${x(j)},${y(v)}`).join(' L ');
        const path = `M ${pts}`;
        const area = `${path} L ${x(s.data.length-1)},${height-pad} L ${x(0)},${height-pad} Z`;
        return (
          <g key={i}>
            <path d={area} fill={`url(#ga${i})`}/>
            <path d={path} fill="none" stroke={s.color || color} strokeWidth="1.5"/>
          </g>
        );
      })}
      {/* x labels */}
      {series[0].labels?.map((l, i) => i % 2 === 0 && (
        <text key={i} x={x(i)} y={height-4} textAnchor="middle" fontSize="9" fill="rgba(255,255,255,0.4)" fontFamily="JetBrains Mono">{l}</text>
      ))}
    </svg>
  );
}

function HorizBar({ label, value, max, color, critical }) {
  const w = Math.round((value / max) * 100);
  return (
    <div className="flex items-center gap-3 text-[12px]">
      <div className="w-20 shrink-0 font-mono text-white/70 truncate">{label}</div>
      <div className="flex-1 h-4 bg-white/5 rounded-sm relative overflow-hidden">
        <div className="absolute inset-y-0 left-0 rounded-sm transition-[width] duration-700" style={{ width: w + '%', background: color }}/>
        <div className="absolute inset-0 grid-bg-tight opacity-30"/>
      </div>
      <div className="w-14 text-right font-mono tabular text-white/80">{value}</div>
      {critical > 0 && <div className="w-7 text-[10px] font-mono text-blood">×{critical}</div>}
    </div>
  );
}

function Donut({ segments, size = 180, thickness = 22 }) {
  const total = segments.reduce((a, s) => a + s.count, 0);
  const r = size/2 - thickness/2;
  const c = 2 * Math.PI * r;
  let offset = 0;
  return (
    <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size}>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="rgba(255,255,255,0.05)" strokeWidth={thickness}/>
      {segments.map((s, i) => {
        const frac = s.count / total;
        const dash = frac * c;
        const gap = c - dash;
        const el = (
          <circle key={i} cx={size/2} cy={size/2} r={r} fill="none"
            stroke={s.color} strokeWidth={thickness}
            strokeDasharray={`${dash} ${gap}`}
            strokeDashoffset={-offset}
            transform={`rotate(-90 ${size/2} ${size/2})`}
          />
        );
        offset += dash;
        return el;
      })}
      <text x={size/2} y={size/2 - 4} textAnchor="middle" fontSize="28" fontFamily="Instrument Serif" fontStyle="italic" fill="white" className="tabular">{total}</text>
      <text x={size/2} y={size/2 + 14} textAnchor="middle" fontSize="9" fontFamily="JetBrains Mono" fill="rgba(255,255,255,0.5)" letterSpacing="1">TOTAL CVEs</text>
    </svg>
  );
}

/* Heatmap: weeks x days */
function Heatmap({ weeks = 26 }) {
  const cells = useMemo(() => {
    const arr = [];
    for (let w = 0; w < weeks; w++) {
      for (let d = 0; d < 7; d++) {
        // deterministic pseudo-random
        const seed = Math.sin(w * 13 + d * 7) * 10000;
        const r = seed - Math.floor(seed);
        let level = Math.floor(r * 5.5);
        // spike recently (last 4 weeks)
        if (w >= weeks - 4 && r > 0.4) level = Math.min(5, level + 2);
        arr.push(level);
      }
    }
    return arr;
  }, [weeks]);
  return (
    <div className="flex gap-[3px]">
      {Array.from({length: weeks}).map((_, w) => (
        <div key={w} className="flex flex-col gap-[3px]">
          {Array.from({length: 7}).map((_, d) => {
            const lvl = cells[w*7+d];
            return <div key={d} className={`w-[10px] h-[10px] rounded-[2px] cell-${lvl}`} />;
          })}
        </div>
      ))}
    </div>
  );
}

/* Radial gauge for "Threat level" */
function ThreatGauge({ level = 82 }) {
  const r = 70, cx = 90, cy = 90;
  const arc = (start, end, rr = r) => {
    const sa = (start - 90) * Math.PI / 180;
    const ea = (end - 90) * Math.PI / 180;
    const large = end - start > 180 ? 1 : 0;
    return `M ${cx + rr*Math.cos(sa)} ${cy + rr*Math.sin(sa)} A ${rr} ${rr} 0 ${large} 1 ${cx + rr*Math.cos(ea)} ${cy + rr*Math.sin(ea)}`;
  };
  const pct = level / 100;
  return (
    <div className="relative">
      <svg viewBox="0 0 180 180" width="180" height="180">
        <path d={arc(-120, 120)} fill="none" stroke="rgba(255,255,255,0.06)" strokeWidth="10" strokeLinecap="round"/>
        <path d={arc(-120, -120 + 240 * pct)} fill="none"
          stroke={level > 75 ? 'hsl(358 80% 58%)' : level > 50 ? 'hsl(38 95% 60%)' : 'hsl(138 85% 55%)'}
          strokeWidth="10" strokeLinecap="round" />
        {/* tick marks */}
        {Array.from({length: 25}).map((_, i) => {
          const a = (-120 + (240 / 24) * i - 90) * Math.PI / 180;
          const rr1 = 80, rr2 = 88;
          return <line key={i}
            x1={cx + rr1*Math.cos(a)} y1={cy + rr1*Math.sin(a)}
            x2={cx + rr2*Math.cos(a)} y2={cy + rr2*Math.sin(a)}
            stroke="rgba(255,255,255,0.2)" strokeWidth={i % 6 === 0 ? 1.5 : 0.5}/>;
        })}
      </svg>
      <div className="absolute inset-0 flex flex-col items-center justify-center pt-6">
        <div className="font-heading italic text-[56px] leading-none tabular">{level}</div>
        <div className="text-[10px] font-mono tracking-[0.2em] uppercase text-blood mt-1">Elevated</div>
      </div>
    </div>
  );
}

/* Simplified world map with dots at disclosure coords */
function WorldMap({ dots = [] }) {
  // coarse land outline via polygons (approximated) — small SVG data url would be cleanest; we'll use a procedural dotmap
  return (
    <svg viewBox="0 0 400 180" width="100%" className="block">
      <defs>
        <pattern id="dotmap" x="0" y="0" width="4" height="4" patternUnits="userSpaceOnUse">
          <circle cx="1" cy="1" r="0.6" fill="rgba(255,255,255,0.15)"/>
        </pattern>
        <clipPath id="land">
          {/* rough continent blobs */}
          <path d="M 30 60 Q 50 35 90 40 Q 130 50 140 80 Q 130 110 100 115 Q 55 115 40 95 Z"/>
          <path d="M 155 55 Q 180 35 220 40 Q 260 55 275 80 Q 260 115 215 115 Q 175 110 160 90 Z"/>
          <path d="M 285 55 Q 315 35 345 45 Q 365 65 360 95 Q 340 120 310 115 Q 290 100 290 80 Z"/>
          <path d="M 80 125 Q 100 120 120 135 Q 125 160 100 165 Q 75 160 75 145 Z"/>
          <path d="M 190 130 Q 215 125 235 140 Q 235 165 210 168 Q 185 160 185 148 Z"/>
          <path d="M 305 130 Q 330 128 345 145 Q 340 165 320 165 Q 300 155 300 145 Z"/>
        </clipPath>
      </defs>
      <rect width="400" height="180" fill="url(#dotmap)" clipPath="url(#land)"/>
      {/* meridians */}
      {[0, 100, 200, 300, 400].map(x => (
        <line key={x} x1={x} y1="0" x2={x} y2="180" stroke="rgba(255,255,255,0.03)"/>
      ))}
      {/* disclosure dots */}
      {dots.map((d, i) => (
        <g key={i}>
          <circle cx={d.x} cy={d.y} r="8" fill={d.color} opacity="0.15">
            <animate attributeName="r" from="4" to="14" dur="2.4s" begin={`${i*0.3}s`} repeatCount="indefinite"/>
            <animate attributeName="opacity" from="0.5" to="0" dur="2.4s" begin={`${i*0.3}s`} repeatCount="indefinite"/>
          </circle>
          <circle cx={d.x} cy={d.y} r="2.5" fill={d.color}/>
          <text x={d.x + 6} y={d.y - 6} fontSize="7" fontFamily="JetBrains Mono" fill="rgba(255,255,255,0.6)">{d.label}</text>
        </g>
      ))}
    </svg>
  );
}

/* Kill chain diagram: stages connected by arrows */
function KillChain({ steps }) {
  return (
    <div className="overflow-x-auto no-bar">
      <div className="flex items-stretch gap-0 min-w-[780px]">
        {steps.map((s, i) => (
          <React.Fragment key={i}>
            <div className="flex-1 glass rounded-xl p-3.5 relative">
              <div className="flex items-center gap-2 mb-2">
                <div className="h-5 w-5 rounded-full bg-ink-700 border border-white/10 flex items-center justify-center text-[10px] font-mono">{i+1}</div>
                <div className="text-[10px] font-mono uppercase tracking-widest" style={{color: s.color}}>{s.phase}</div>
              </div>
              <div className="text-[12px] font-semibold leading-tight">{s.title}</div>
              <div className="mt-1.5 text-[11px] text-white/55 leading-snug">{s.detail}</div>
              <div className="mt-2 text-[10px] font-mono text-white/35">{s.evidence}</div>
            </div>
            {i < steps.length - 1 && (
              <div className="flex items-center px-1">
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="text-white/30">
                  <path d="M5 12h14M13 6l6 6-6 6" strokeLinecap="round"/>
                </svg>
              </div>
            )}
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

/* --------- Live firehose (ticking feed) --------- */
function useLiveFeed(seed, intervalMs = 3500) {
  const [items, setItems] = useState(() => seed.slice(0, 8));
  useEffect(() => {
    let cur = 8;
    const t = setInterval(() => {
      setItems(prev => [seed[cur % seed.length], ...prev].slice(0, 12));
      cur++;
    }, intervalMs);
    return () => clearInterval(t);
  }, []);
  return items;
}

function Firehose() {
  const seed = useMemo(() => [
    { sev: 'critical', txt: 'ShinyHunters posts sample dump — Vercel env vars', t: '02s' },
    { sev: 'high',     txt: 'LangChain 0.3.28 patch-commit lands for CVE-2025-68664', t: '14s' },
    { sev: 'info',     txt: 'NIST queues analysis for Context.ai OAuth token misuse', t: '31s' },
    { sev: 'high',     txt: 'npm registry flags 3 new typosquats of @vercel/analytics', t: '47s' },
    { sev: 'medium',   txt: 'Mistral pushes tool-use prefix fix to mistral-large-2512', t: '1m' },
    { sev: 'critical', txt: 'Copilot RCE PoC reproduced on gpt-5-turbo, reported', t: '1m' },
    { sev: 'info',     txt: 'MITRE ATLAS adds T0087 — cross-tenant OAuth lateral', t: '2m' },
    { sev: 'high',     txt: 'CrowdStrike IOCs published for Context.ai infostealer chain', t: '2m' },
    { sev: 'medium',   txt: 'Meta Llama-4 many-shot bypass re-tested, still works ≥256k', t: '3m' },
    { sev: 'high',     txt: 'Hudson Rock confirms Lumma Stealer feb-26 infection vector', t: '3m' },
    { sev: 'info',     txt: 'Google Workspace admin API surfaces new OAuth audit event', t: '4m' },
    { sev: 'critical', txt: 'Drift exchange front-end rotates 14 Vercel-hosted keys', t: '4m' },
    { sev: 'medium',   txt: 'GitHub adds workflow-level OIDC enforcement warning', t: '5m' },
    { sev: 'high',     txt: 'Cripple.ai reproduces EchoLeak variant on Teams Copilot', t: '5m' },
    { sev: 'info',     txt: 'Vercel enterprise customers receive batched credential rotation', t: '6m' },
  ], []);
  const items = useLiveFeed(seed);
  const color = { critical: 'text-blood', high: 'text-amber', medium: 'text-acid', info: 'text-white/50' };
  const dot = { critical: 'bg-blood', high: 'bg-amber', medium: 'bg-acid', info: 'bg-white/40' };
  return (
    <div className="space-y-2">
      {items.map((it, i) => (
        <div key={`${it.txt}-${i}`} className={`flex items-start gap-3 text-[11.5px] ${i === 0 ? 'tick-in' : ''}`}>
          <span className={`h-1.5 w-1.5 rounded-full mt-1.5 shrink-0 ${dot[it.sev]} ${it.sev === 'critical' ? 'pulse-dot-blood' : ''}`}/>
          <div className="flex-1 leading-snug">
            <span className="text-white/85">{it.txt}</span>
          </div>
          <span className="font-mono text-[10px] text-white/30 shrink-0 tabular">{it.t}</span>
        </div>
      ))}
    </div>
  );
}

/* --------- Command palette / top bar --------- */
function CommandBar({ query, onQuery, filters, onFilter }) {
  const severities = ['All', 'Critical', 'High', 'Medium'];
  return (
    <div className="sticky top-14 z-30 bg-ink-900/80 backdrop-blur-xl border-b border-white/5">
      <div className="max-w-[1700px] mx-auto px-5 lg:px-8 py-3 flex items-center gap-3 flex-wrap">
        <div className="flex items-center gap-2 glass rounded-lg px-3 py-2 flex-1 min-w-[280px]">
          <Search className="h-3.5 w-3.5 text-white/50"/>
          <input
            value={query} onChange={e => onQuery(e.target.value)}
            placeholder="search CVE, vendor, attack class…"
            className="bg-transparent outline-none flex-1 text-[12px] font-mono placeholder:text-white/30"
          />
          <kbd className="text-[10px] font-mono text-white/30 border border-white/10 rounded px-1.5 py-0.5">⌘ K</kbd>
        </div>
        <div className="flex items-center gap-1 glass rounded-lg p-1">
          {severities.map(s => (
            <button key={s} onClick={() => onFilter({ ...filters, severity: s })}
              className={`text-[11px] font-mono px-2.5 py-1 rounded ${filters.severity === s ? 'bg-white/10 text-white' : 'text-white/50 hover:text-white'}`}>
              {s}
            </button>
          ))}
        </div>
        <div className="flex items-center gap-1 glass rounded-lg p-1">
          {['24h', '7d', '30d', '90d', 'YTD'].map(r => (
            <button key={r} onClick={() => onFilter({ ...filters, range: r })}
              className={`text-[11px] font-mono px-2.5 py-1 rounded ${filters.range === r ? 'bg-white/10 text-white' : 'text-white/50 hover:text-white'}`}>
              {r}
            </button>
          ))}
        </div>
        <div className="flex items-center gap-2 text-[11px] font-mono text-white/50">
          <span className="pulse-dot"/> <span className="text-acid">LIVE</span>
          <span className="text-white/30 ml-1">·</span>
          <span className="text-white/50">Last sync <span className="text-white/80 tabular">00:02s ago</span></span>
        </div>
      </div>
    </div>
  );
}

/* --------- Top KPI bar --------- */
function Kpi({ label, value, delta, spark, color, suffix, critical }) {
  const n = useCountUp(value);
  return (
    <div className={`glass rounded-xl px-4 py-3 ${critical ? 'ring-1 ring-blood/50' : ''}`}>
      <div className="flex items-start justify-between">
        <div className="text-[10px] font-mono uppercase tracking-[0.15em] text-white/50 leading-tight">{label}</div>
        {critical && <span className="pulse-dot-blood"/>}
      </div>
      <div className="flex items-end justify-between mt-2 gap-2">
        <div className="font-heading italic text-[34px] leading-none tabular">{n.toLocaleString()}{suffix}</div>
        <Sparkline data={spark} color={color} width={64} height={24}/>
      </div>
      <div className="mt-1 flex items-center gap-2 text-[10px] font-mono">
        <span className={delta >= 0 ? 'text-acid' : 'text-blood'}>{delta >= 0 ? '▲' : '▼'} {Math.abs(delta)}%</span>
        <span className="text-white/40">vs 30d</span>
      </div>
    </div>
  );
}

/* --------- CVE detail drawer --------- */
function Drawer({ cve, onClose }) {
  if (!cve) return null;
  return (
    <div className="fixed inset-0 z-50 flex">
      <div className="flex-1 bg-black/50 backdrop-blur-sm" onClick={onClose}/>
      <div className="w-full max-w-[560px] bg-ink-800 border-l border-white/10 overflow-y-auto">
        <div className="sticky top-0 bg-ink-800/90 backdrop-blur-xl border-b border-white/5 px-6 py-4 flex items-center justify-between">
          <div className="flex items-center gap-2">
            <span className={`h-1.5 w-1.5 rounded-full ${cve.severity === 'Critical' ? 'bg-blood pulse-dot-blood' : cve.severity === 'High' ? 'bg-amber pulse-dot-amber' : 'bg-acid pulse-dot'}`}/>
            <span className="text-[11px] font-mono uppercase tracking-wider text-white/60">{cve.severity}</span>
            <span className="text-white/20">·</span>
            <span className="text-[11px] font-mono text-white/50">{cve.id}</span>
          </div>
          <button onClick={onClose} className="text-white/50 hover:text-white text-xl leading-none">×</button>
        </div>
        <div className="p-6 space-y-6">
          <h3 className="font-heading italic text-3xl leading-tight">{cve.title}</h3>
          <div className="grid grid-cols-2 gap-3 text-[12px]">
            {[
              ['Vendor', cve.vendor], ['Model/System', cve.model],
              ['CVSS', cve.cvss ?? '—'], ['Class', cve.class],
              ['Disclosed', cve.date], ['Status', cve.status],
              ['In the wild', cve.itw ? 'Yes' : 'No'], ['Authored by', cve.author],
            ].map(([k,v]) => (
              <div key={k} className="glass rounded-lg px-3 py-2">
                <div className="text-[10px] font-mono uppercase tracking-wider text-white/40">{k}</div>
                <div className={`mt-0.5 font-mono text-[13px] ${k === 'In the wild' && v === 'Yes' ? 'text-blood' : 'text-white/90'}`}>{v}</div>
              </div>
            ))}
          </div>
          <div>
            <div className="text-[10px] font-mono uppercase tracking-[0.15em] text-white/40 mb-2">Summary</div>
            <p className="text-[13.5px] text-white/75 leading-relaxed">
              Full write-up available in the disclosure archive. This entry aggregates primary-source reporting
              (vendor bulletin, security advisory, and independent reproduction) into a single reference card.
              Where indicators of compromise have been shared, they are mirrored on the Cripple.AI disclosure repo.
            </p>
          </div>
          <div className="flex gap-2">
            <a href="blog.html" className="flex-1 glass rounded-lg py-2.5 text-center text-[12px] font-mono hover:bg-white/10">Read write-up</a>
            <a href="disclosures.html" className="flex-1 glass rounded-lg py-2.5 text-center text-[12px] font-mono hover:bg-white/10">Timeline</a>
          </div>
        </div>
      </div>
    </div>
  );
}

/* --------- Main dashboard --------- */
function Dashboard() {
  const [query, setQuery] = useState('');
  const [filters, setFilters] = useState({ severity: 'All', range: '30d' });
  const [selected, setSelected] = useState(null);
  const [now, setNow] = useState(new Date());

  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(t);
  }, []);

  const cves = useMemo(() => {
    return DATA.cves.filter(c => {
      if (filters.severity !== 'All' && c.severity !== filters.severity) return false;
      if (query) {
        const q = query.toLowerCase();
        return c.title.toLowerCase().includes(q) || c.vendor.toLowerCase().includes(q)
          || c.id.toLowerCase().includes(q) || c.class.toLowerCase().includes(q);
      }
      return true;
    });
  }, [query, filters]);

  const classSegs = DATA.classes;

  const vendorMax = Math.max(...DATA.vendors.map(v => v.count));

  const trend = useMemo(() => {
    const labels = ['Nov','Dec','Jan','Feb','Mar','Apr'];
    return [
      { label: 'Critical', color: 'hsl(358 80% 58%)', data: [2,3,4,3,5,7], labels },
      { label: 'High',     color: 'hsl(38 95% 60%)',  data: [6,8,9,11,14,17], labels },
      { label: 'Medium',   color: 'hsl(138 85% 55%)', data: [9,12,14,13,16,18], labels },
    ];
  }, []);

  const mapDots = [
    { x: 75,  y: 72,  label: 'SF',     color: 'hsl(358 80% 58%)' },  // Vercel
    { x: 95,  y: 65,  label: 'NYC',    color: 'hsl(38 95% 60%)' },   // Microsoft
    { x: 180, y: 68,  label: 'LON',    color: 'hsl(138 85% 55%)' },  // Oxford / Anthropic-London
    { x: 205, y: 80,  label: 'PAR',    color: 'hsl(38 95% 60%)' },   // Mistral
    { x: 320, y: 78,  label: 'TOK',    color: 'hsl(138 85% 55%)' },  // Sakana
    { x: 300, y: 100, label: 'SG',     color: 'hsl(200 75% 60%)' },
    { x: 225, y: 155, label: 'SAO',    color: 'hsl(280 70% 65%)' },
    { x: 195, y: 98,  label: 'TLV',    color: 'hsl(358 80% 58%)' },
  ];

  const killChain = [
    { phase: 'Initial', color: 'hsl(358 80% 58%)', title: 'Lumma Stealer', detail: 'Context.ai employee infected, corporate creds harvested', evidence: 'Feb 2026 · Hudson Rock' },
    { phase: 'Pivot', color: 'hsl(25 90% 60%)', title: 'OAuth token theft', detail: 'support@context.ai account compromised, escalates privileges', evidence: 'March 2026' },
    { phase: 'Access', color: 'hsl(38 95% 60%)', title: 'Google Workspace', detail: 'Context.ai OAuth app assumes Vercel employee identity', evidence: 'OAuth App 11067…vqj' },
    { phase: 'Exploit', color: 'hsl(138 85% 55%)', title: 'Env var exfil', detail: 'Reads any Vercel env var not flagged "sensitive"', evidence: 'Apr 19, 2026' },
    { phase: 'Monetize', color: 'hsl(280 70% 65%)', title: 'ShinyHunters sale', detail: 'Dump listed on BreachForums for $2M', evidence: 'Apr 19, 2026' },
  ];

  const researchers = [
    { name: 'Priya Subramanian',  initials: 'PS', c: '#a78bfa', cves: 34, streak: 12, active: 'EchoLeak reproduction' },
    { name: 'Marcus Weil',        initials: 'MW', c: '#34d399', cves: 28, streak: 8,  active: 'LangGrinch variants in LangGraph' },
    { name: 'Lena Oduya',         initials: 'LO', c: '#fbbf24', cves: 22, streak: 15, active: 'Copilot RCE replication' },
    { name: 'Jun Tanaka',         initials: 'JT', c: '#60a5fa', cves: 19, streak: 4,  active: 'Gemini long-context drift' },
    { name: 'Rafael Kowalski',    initials: 'RK', c: '#fb7185', cves: 17, streak: 7,  active: 'DeepSeek suffix transferability' },
  ];

  return (
    <PageShell current="dashboard">
      <CommandBar query={query} onQuery={setQuery} filters={filters} onFilter={setFilters}/>

      {/* Title strip */}
      <div className="max-w-[1700px] mx-auto px-5 lg:px-8 pt-6 pb-4 flex items-end justify-between flex-wrap gap-4 relative">
        <div>
          <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-acid mb-2 flex items-center gap-2">
            <span className="pulse-dot"/> Console · Cripple.AI · Tactical view
          </div>
          <h1 className="font-heading italic text-4xl md:text-5xl leading-[0.95] tracking-[-0.02em]">
            Every adversarial AI disclosure, one glass pane.
          </h1>
        </div>
        <div className="flex items-center gap-3 text-[11px] font-mono text-white/50">
          <div className="glass rounded-lg px-3 py-2">
            <div className="text-[9px] uppercase tracking-wider text-white/40">UTC</div>
            <div className="text-white/90 tabular">{now.toISOString().slice(11,19)}</div>
          </div>
          <div className="glass rounded-lg px-3 py-2">
            <div className="text-[9px] uppercase tracking-wider text-white/40">Data window</div>
            <div className="text-white/90">Apr 2026 · week 16</div>
          </div>
        </div>
      </div>

      {/* KPI strip */}
      <div className="max-w-[1700px] mx-auto px-5 lg:px-8 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3 mb-4">
        <Kpi label="CVEs tracked · YTD" value={318} delta={43} spark={[4,6,8,14,22,34,48,68,92,128,180,240,290,318]} color="hsl(358 80% 58%)" critical/>
        <Kpi label="Critical · 30d"     value={23}  delta={28} spark={[2,3,4,3,5,6,8,9,11,14,17,19,21,23]} color="hsl(358 80% 58%)"/>
        <Kpi label="Patched · 30d"      value={87}  delta={12} spark={[40,46,52,55,58,62,67,71,74,78,81,84,86,87]} color="hsl(138 85% 55%)"/>
        <Kpi label="Exploits in wild"   value={14}  delta={56} spark={[2,3,4,5,6,7,8,9,10,11,12,13,14,14]} color="hsl(38 95% 60%)"/>
        <Kpi label="Vendors affected"   value={41}  delta={9}  spark={[18,20,22,24,26,28,30,33,35,37,39,40,41,41]} color="hsl(200 75% 60%)"/>
        <Kpi label="Researchers · live" value={2408} suffix="+" delta={18} spark={[800,900,1050,1200,1380,1550,1720,1880,2040,2180,2280,2340,2380,2408]} color="hsl(138 85% 55%)"/>
      </div>

      {/* Featured breach banner */}
      <div className="max-w-[1700px] mx-auto px-5 lg:px-8 mb-4">
        <div className="glass rounded-2xl overflow-hidden relative">
          <div className="absolute inset-0 radial-glow-red pointer-events-none"/>
          <div className="relative p-5 flex items-center gap-4 flex-wrap">
            <div className="flex items-center gap-2 glass rounded-full px-3 py-1.5">
              <span className="pulse-dot-blood"/>
              <span className="text-[10px] font-mono uppercase tracking-[0.2em] text-blood font-bold">Active · Breaking</span>
            </div>
            <div className="font-heading italic text-2xl flex-1 min-w-[260px] leading-tight">
              Vercel → Context.ai OAuth supply chain · customer env vars exposed
            </div>
            <div className="flex items-center gap-5 text-[11px] font-mono text-white/60">
              <div><div className="text-white/40 text-[9px] uppercase tracking-wider">Disclosed</div>Apr 19, 2026</div>
              <div><div className="text-white/40 text-[9px] uppercase tracking-wider">Actor</div>ShinyHunters</div>
              <div><div className="text-white/40 text-[9px] uppercase tracking-wider">Asking price</div><span className="text-blood">$2.0M</span></div>
              <div><div className="text-white/40 text-[9px] uppercase tracking-wider">Scope</div>Limited subset</div>
            </div>
            <button onClick={() => setSelected(DATA.cves[0])} className="glass rounded-full px-3.5 py-2 text-[11px] font-mono hover:bg-white/10 inline-flex items-center gap-1.5">
              Open chain <Arrow className="h-3 w-3"/>
            </button>
          </div>
        </div>
      </div>

      {/* Main grid */}
      <div className="max-w-[1700px] mx-auto px-5 lg:px-8 grid grid-cols-12 gap-3">

        {/* LEFT: Firehose (3 cols) */}
        <div className="col-span-12 lg:col-span-3 space-y-3">
          <div className="glass rounded-2xl p-4 relative overflow-hidden">
            <div className="absolute inset-0 scanlines"/>
            <div className="flex items-center justify-between mb-3 relative">
              <div className="flex items-center gap-2">
                <span className="pulse-dot"/>
                <span className="text-[10px] font-mono uppercase tracking-[0.2em] text-acid">Firehose · Live</span>
              </div>
              <span className="text-[10px] font-mono text-white/40">T-0</span>
            </div>
            <div className="max-h-[540px] overflow-y-auto no-bar pr-1 relative">
              <Firehose/>
            </div>
            <div className="mt-3 flex items-center justify-between text-[10px] font-mono text-white/40 border-t border-white/5 pt-3">
              <span>15 events/min avg</span>
              <a href="disclosures.html" className="text-acid hover:underline">All events →</a>
            </div>
          </div>

          <div className="glass rounded-2xl p-4">
            <div className="flex items-center justify-between mb-3">
              <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50">Threat level</div>
              <span className="text-[10px] font-mono text-blood">ELEVATED</span>
            </div>
            <div className="flex items-center justify-center">
              <ThreatGauge level={82}/>
            </div>
            <div className="grid grid-cols-3 gap-2 mt-2 text-center">
              {[['Supply','HIGH','text-blood'], ['Models','MED','text-amber'], ['Infra','HIGH','text-blood']].map(([k,v,c],i) => (
                <div key={i} className="glass rounded-lg py-2">
                  <div className="text-[9px] font-mono text-white/40 uppercase tracking-wider">{k}</div>
                  <div className={`text-[11px] font-mono ${c}`}>{v}</div>
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* MIDDLE: trend + heatmap + vendors (6 cols) */}
        <div className="col-span-12 lg:col-span-6 space-y-3">
          {/* Severity trend */}
          <div className="glass rounded-2xl p-4">
            <div className="flex items-center justify-between mb-3 flex-wrap gap-2">
              <div>
                <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-0.5">Disclosure volume by severity</div>
                <div className="font-heading italic text-xl">Weekly cadence, last 6 months</div>
              </div>
              <div className="flex gap-2">
                {trend.map((s, i) => (
                  <div key={i} className="flex items-center gap-1.5 text-[10px] font-mono">
                    <span className="h-1.5 w-1.5 rounded-full" style={{background: s.color}}/>
                    <span className="text-white/60">{s.label}</span>
                  </div>
                ))}
              </div>
            </div>
            <AreaChart series={trend} height={220}/>
          </div>

          {/* Kill chain */}
          <div className="glass rounded-2xl p-4">
            <div className="flex items-center justify-between mb-3">
              <div>
                <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-0.5">Kill chain · featured</div>
                <div className="font-heading italic text-xl">Vercel / Context.ai · Lumma Stealer → $2M dump</div>
              </div>
              <span className="text-[10px] font-mono glass rounded-full px-2 py-1 text-blood">5 stages</span>
            </div>
            <KillChain steps={killChain}/>
          </div>

          {/* Vendor leaderboard */}
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            <div className="glass rounded-2xl p-4">
              <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-3">Vendor leaderboard · CVEs YTD</div>
              <div className="space-y-2.5">
                {DATA.vendors.sort((a,b)=>b.count-a.count).map((v, i) => (
                  <HorizBar key={i} label={v.name} value={v.count} max={vendorMax}
                    color={i === 0 ? 'hsl(358 80% 58%)' : i < 3 ? 'hsl(38 95% 60%)' : 'hsl(138 85% 55%)'}
                    critical={v.critical}/>
                ))}
              </div>
            </div>

            {/* Activity heatmap */}
            <div className="glass rounded-2xl p-4">
              <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-3">Disclosure heatmap · 26 weeks</div>
              <div className="flex items-start justify-between gap-3">
                <div className="flex flex-col gap-[3px] text-[9px] font-mono text-white/40 pt-[2px]">
                  {['S','M','T','W','T','F','S'].map((d,i) => <div key={i} className="h-[10px] leading-[10px]">{d}</div>)}
                </div>
                <div className="flex-1 overflow-x-auto no-bar">
                  <Heatmap weeks={26}/>
                </div>
              </div>
              <div className="mt-3 flex items-center justify-between text-[10px] font-mono text-white/40">
                <span>Oct 2025</span>
                <div className="flex items-center gap-1">
                  <span>less</span>
                  {[0,1,2,3,4,5].map(i => <div key={i} className={`w-[10px] h-[10px] rounded-[2px] cell-${i}`}/>)}
                  <span>more</span>
                </div>
                <span>Apr 2026</span>
              </div>
            </div>
          </div>
        </div>

        {/* RIGHT: attack classes + world map (3 cols) */}
        <div className="col-span-12 lg:col-span-3 space-y-3">
          <div className="glass rounded-2xl p-4">
            <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-3">Attack class breakdown</div>
            <div className="flex items-center justify-center">
              <Donut segments={classSegs} size={170} thickness={20}/>
            </div>
            <div className="mt-3 space-y-1.5">
              {classSegs.sort((a,b)=>b.count-a.count).map((s, i) => (
                <div key={i} className="flex items-center gap-2 text-[11px]">
                  <span className="h-1.5 w-1.5 rounded-full" style={{background: s.color}}/>
                  <span className="text-white/70 flex-1 truncate">{s.name}</span>
                  <span className="font-mono tabular text-white/50">{s.count}</span>
                </div>
              ))}
            </div>
          </div>

          <div className="glass rounded-2xl p-4">
            <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-2">Global disclosures · 24h</div>
            <WorldMap dots={mapDots}/>
            <div className="mt-2 grid grid-cols-2 gap-2 text-[10px] font-mono">
              <div className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full bg-blood"/><span className="text-white/60">Critical</span><span className="ml-auto text-white/40 tabular">3</span></div>
              <div className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full bg-amber"/><span className="text-white/60">High</span><span className="ml-auto text-white/40 tabular">5</span></div>
              <div className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full bg-acid"/><span className="text-white/60">Medium</span><span className="ml-auto text-white/40 tabular">8</span></div>
              <div className="flex items-center gap-1.5"><span className="h-1.5 w-1.5 rounded-full bg-white/40"/><span className="text-white/60">Info</span><span className="ml-auto text-white/40 tabular">11</span></div>
            </div>
          </div>

          <div className="glass rounded-2xl p-4">
            <div className="flex items-center justify-between mb-3">
              <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50">Top researchers · 30d</div>
              <span className="text-[10px] font-mono text-white/30">by cred</span>
            </div>
            <div className="space-y-2.5">
              {researchers.map((r, i) => (
                <div key={i} className="flex items-center gap-2.5 text-[11px]">
                  <div className="h-7 w-7 rounded-full flex items-center justify-center text-[10px] font-mono font-bold shrink-0"
                    style={{background: r.c, color: '#0a0d18'}}>{r.initials}</div>
                  <div className="flex-1 min-w-0">
                    <div className="text-white/90 truncate">{r.name}</div>
                    <div className="text-[10px] font-mono text-white/40 truncate">{r.active}</div>
                  </div>
                  <div className="text-right shrink-0">
                    <div className="font-mono tabular text-white/80">{r.cves}</div>
                    <div className="text-[9px] font-mono text-acid">🔥 {r.streak}</div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* CVE TABLE (full width) */}
        <div className="col-span-12 glass rounded-2xl overflow-hidden">
          <div className="flex items-center justify-between p-4 border-b border-white/5">
            <div>
              <div className="text-[10px] font-mono uppercase tracking-[0.2em] text-white/50 mb-0.5">CVE directory</div>
              <div className="font-heading italic text-xl">{cves.length} matching · click any row</div>
            </div>
            <div className="flex items-center gap-2 text-[11px] font-mono text-white/40">
              <span>sort:</span>
              <span className="text-white/80">date ▼</span>
            </div>
          </div>
          <div className="overflow-x-auto">
            <table className="w-full text-[12px]">
              <thead className="text-[10px] font-mono uppercase tracking-wider text-white/40 bg-white/[0.02]">
                <tr>
                  <th className="text-left px-4 py-2.5 font-medium">Severity</th>
                  <th className="text-left px-4 py-2.5 font-medium">CVE</th>
                  <th className="text-left px-4 py-2.5 font-medium">Title</th>
                  <th className="text-left px-4 py-2.5 font-medium">Vendor</th>
                  <th className="text-left px-4 py-2.5 font-medium">Class</th>
                  <th className="text-right px-4 py-2.5 font-medium">CVSS</th>
                  <th className="text-left px-4 py-2.5 font-medium">Status</th>
                  <th className="text-center px-4 py-2.5 font-medium">ITW</th>
                  <th className="text-right px-4 py-2.5 font-medium">Date</th>
                </tr>
              </thead>
              <tbody>
                {cves.map((c, i) => {
                  const sevCls = c.severity === 'Critical' ? 'text-blood' : c.severity === 'High' ? 'text-amber' : 'text-acid';
                  const sevDot = c.severity === 'Critical' ? 'bg-blood' : c.severity === 'High' ? 'bg-amber' : 'bg-acid';
                  return (
                    <tr key={i} onClick={() => setSelected(c)}
                      className="border-t border-white/5 hover:bg-white/[0.03] cursor-pointer transition">
                      <td className="px-4 py-2.5">
                        <div className="flex items-center gap-1.5">
                          <span className={`h-1.5 w-1.5 rounded-full ${sevDot}`}/>
                          <span className={`font-mono ${sevCls}`}>{c.severity}</span>
                        </div>
                      </td>
                      <td className="px-4 py-2.5 font-mono text-white/80">{c.id}</td>
                      <td className="px-4 py-2.5 text-white/90 max-w-[420px] truncate">{c.title}</td>
                      <td className="px-4 py-2.5 font-mono text-white/70">{c.vendor}</td>
                      <td className="px-4 py-2.5">
                        <span className="glass rounded px-2 py-0.5 text-[10px] font-mono text-white/70">{c.class}</span>
                      </td>
                      <td className="px-4 py-2.5 text-right font-mono tabular text-white/80">{c.cvss ?? '—'}</td>
                      <td className="px-4 py-2.5 font-mono text-white/60">{c.status}</td>
                      <td className="px-4 py-2.5 text-center">
                        {c.itw ? <span className="text-blood font-mono">●</span> : <span className="text-white/20">—</span>}
                      </td>
                      <td className="px-4 py-2.5 text-right font-mono text-white/50 tabular">{c.date}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
          <div className="p-3 border-t border-white/5 text-center text-[10px] font-mono text-white/40">
            showing {cves.length} of {DATA.cves.length} · <a href="disclosures.html" className="text-acid hover:underline">open full directory →</a>
          </div>
        </div>
      </div>

      <Drawer cve={selected} onClose={() => setSelected(null)}/>
    </PageShell>
  );
}

window.CRIPPLE_READY.then(() => ReactDOM.createRoot(document.getElementById('root')).render(<Dashboard/>));