// Report state — hero, layer table, heatmap, suspicious passages.
const { useState: useStateRpt, useMemo: useMemoRpt } = React;

function HeroStrip({ verdict }){
  const score = verdict.final_score;
  const tone = verdictTone(score);
  const verdictColor = tone.tone === 'red' ? 'var(--red)' : tone.tone === 'green' ? 'var(--green)' : 'var(--amber)';
  return (
    <div className="fade-up" style={{
      borderBottom:'1px solid var(--border)',
      background: tone.bg,
      padding:'28px 32px',
      display:'grid', gridTemplateColumns:'1fr auto', alignItems:'center', gap:24,
    }}>
      <div style={{display:'flex', flexDirection:'column', gap:10}}>
        <div style={{display:'flex', alignItems:'center', gap:12}}>
          <span style={{fontSize:28}}>{verdict.verdict_icon}</span>
          <h1 className="mono" style={{margin:0, fontSize:22, fontWeight:600, letterSpacing:'-0.01em'}}>
            {verdict.verdict}
          </h1>
        </div>
        <p className="sans" style={{margin:0, color:'var(--text-muted)', fontSize:14, maxWidth:'70ch', lineHeight:1.5}}>
          {verdict.verdict_description}
        </p>
        <div className="mono" style={{display:'flex', gap:24, marginTop:8, fontSize:11, color:'var(--text-muted)', flexWrap:'wrap'}}>
          <span>
            <span className="upper" style={{color:'var(--text-dim)', marginRight:8}}>confidence</span>
            {verdict.confidence}
          </span>
        </div>
        <div className="mono" style={{
          marginTop:6, padding:'8px 12px', borderLeft:`2px solid ${verdictColor}`,
          color: 'var(--text)', fontSize:12,
        }}>
          → {verdict.recommended_action}
        </div>
      </div>
      <div style={{display:'flex', flexDirection:'column', alignItems:'flex-end', gap:6}}>
        <div className="mono upper" style={{color:'var(--text-dim)', fontSize:10}}>final score</div>
        <div className="mono tnum" style={{
          fontSize:64, fontWeight:600, lineHeight:1, color: verdictColor, letterSpacing:'-0.03em',
        }}>{score.toFixed(3)}</div>
        <div className="mono" style={{color:'var(--text-dim)', fontSize:10}}>weighted across 10 layers</div>
      </div>
    </div>
  );
}

function ConflictBanner({ conflicts }){
  if (!conflicts || !conflicts.length) return null;
  return (
    <div style={{
      borderBottom:'1px solid rgba(245,158,11,.4)',
      background: 'rgba(245,158,11,.08)',
      padding:'12px 32px',
      display:'flex', alignItems:'flex-start', gap:14,
    }}>
      <span style={{color:'var(--amber)', fontSize:16, lineHeight:'20px'}}>⚠</span>
      <div style={{display:'flex', flexDirection:'column', gap:4, flex:1}}>
        <div className="mono upper" style={{color:'var(--amber)', fontSize:11, letterSpacing:'0.1em'}}>
          signals are conflicted · human review strongly recommended
        </div>
        <div style={{display:'flex', flexDirection:'column', gap:3, fontSize:12}}>
          {conflicts.map((c, i) => {
            const a = layerMeta(c.layers[0]).short, b = layerMeta(c.layers[1]).short;
            return (
              <div key={i} className="mono" style={{color:'var(--text-muted)'}}>
                <span style={{color:'var(--text)'}}>{a} ↔ {b}</span>
                <span style={{color:'var(--text-dim)'}}> · Δ {c.delta.toFixed(2)} · </span>
                <span>{c.note}</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

function DocumentPanel({ doc, analysisTime, onDownloadJson, onDownloadPdf, onReset }){
  return (
    <div style={{
      borderRight:'1px solid var(--border)',
      padding:'20px 22px',
      display:'flex', flexDirection:'column', gap:20,
    }}>
      <div>
        <div className="section-label" style={{marginBottom:10}}>▸ document</div>
        <dl className="kvp">
          <dt>filename</dt><dd style={{wordBreak:'break-all'}}>{doc.filename}</dd>
          <dt>type</dt><dd>{doc.file_type}</dd>
          <dt>size</dt><dd>{commas(doc.file_size_bytes)} bytes</dd>
          <dt>words</dt><dd>{commas(doc.word_count)}</dd>
          <dt>analyzed in</dt><dd>{analysisTime.toFixed(1)}s</dd>
        </dl>
      </div>

      <div>
        <div className="section-label" style={{marginBottom:10}}>▸ engine</div>
        <dl className="kvp">
          <dt>version</dt><dd>0.4.2</dd>
          <dt>chunker</dt><dd>tiktoken-cl100k</dd>
          <dt>models</dt><dd>3 (ensemble)</dd>
          <dt>seed</dt><dd>0xa3f1</dd>
        </dl>
      </div>

      <div style={{display:'flex', flexDirection:'column', gap:6, marginTop:'auto'}}>
        <button className="btn btn-block" onClick={onDownloadJson}>↓ Download JSON</button>
        <button className="btn btn-block" onClick={onDownloadPdf}>↓ Download PDF report</button>
        <button className="btn btn-block btn-ghost" onClick={onReset} style={{marginTop:6, color:'var(--text-muted)'}}>← New analysis</button>
      </div>
    </div>
  );
}

function LayerTable({ rows, onRowClick }){
  const [sortKey, setSortKey] = useStateRpt('weight');
  const [sortDir, setSortDir] = useStateRpt('desc');
  const sorted = useMemoRpt(() => {
    const arr = [...rows];
    arr.sort((a, b) => {
      let av, bv;
      if (sortKey === 'layer') { av = layerMeta(a.layer).short; bv = layerMeta(b.layer).short; }
      else if (sortKey === 'signal') { av = a.signal; bv = b.signal; }
      else { av = a[sortKey]; bv = b[sortKey]; }
      if (av < bv) return sortDir === 'asc' ? -1 : 1;
      if (av > bv) return sortDir === 'asc' ? 1 : -1;
      return 0;
    });
    return arr;
  }, [rows, sortKey, sortDir]);

  const setSort = k => {
    if (sortKey === k) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortKey(k); setSortDir(k === 'layer' ? 'asc' : 'desc'); }
  };
  const arrow = k => sortKey === k ? (sortDir === 'asc' ? ' ↑' : ' ↓') : '';

  return (
    <div style={{padding:'20px 22px', overflow:'auto'}}>
      <div style={{display:'flex', alignItems:'baseline', justifyContent:'space-between', marginBottom:14}}>
        <div className="section-label">▸ layer breakdown · 10 forensic signals</div>
        <div className="mono" style={{fontSize:10, color:'var(--text-dim)'}}>
          click row for full evidence
        </div>
      </div>
      <table style={{width:'100%', borderCollapse:'collapse', fontFamily:'var(--mono)', fontSize:12}}>
        <thead>
          <tr style={{borderBottom:'1px solid var(--border-2)'}}>
            <th className="sortable upper" onClick={()=>setSort('layer')}  style={thStyle('left',30)}>Layer{arrow('layer')}</th>
            <th className="sortable upper" onClick={()=>setSort('weight')} style={thStyle('right',70)}>Weight{arrow('weight')}</th>
            <th className="sortable upper" onClick={()=>setSort('ai_probability')} style={thStyle('right',150)}>AI prob{arrow('ai_probability')}</th>
            <th className="sortable upper" onClick={()=>setSort('signal')} style={thStyle('left',90)}>Signal{arrow('signal')}</th>
            <th className="upper" style={thStyle('left')}>Evidence</th>
            <th style={thStyle('right',24)}></th>
          </tr>
        </thead>
        <tbody>
          {sorted.map((row) => {
            const meta = layerMeta(row.layer);
            const color = signalColor(row.ai_probability);
            const probColor = color==='red' ? 'var(--red)' : color==='green' ? 'var(--green)' : 'var(--amber)';
            return (
              <tr key={row.layer}
                  onClick={() => onRowClick(row.layer)}
                  className="layer-row"
                  style={{borderBottom:'1px solid var(--border)', cursor:'default'}}
                  onMouseOver={(e)=>e.currentTarget.style.background='var(--surface-2)'}
                  onMouseOut={(e)=>e.currentTarget.style.background='transparent'}>
                <td style={tdStyle('left')}>
                  <span style={{color:'var(--text-dim)', marginRight:10, display:'inline-block', width:26}}>{meta.short}</span>
                  <span>{meta.name}</span>
                </td>
                <td style={{...tdStyle('right'), color:'var(--text-muted)'}}>{pct0(row.weight)}</td>
                <td style={tdStyle('right')}>
                  <div style={{display:'flex', alignItems:'center', gap:10, justifyContent:'flex-end'}}>
                    <span className="tnum" style={{color: probColor, minWidth: 38, textAlign:'right'}}>{fmt3(row.ai_probability)}</span>
                    <ProbBar value={row.ai_probability} cells={9}/>
                  </div>
                </td>
                <td style={tdStyle('left')}><SignalPill signal={row.signal}/></td>
                <td style={{...tdStyle('left'), color:'var(--text-muted)', fontSize:12}}>{row.evidence}</td>
                <td style={{...tdStyle('right'), color:'var(--text-dim)'}}>›</td>
              </tr>
            );
          })}
        </tbody>
      </table>

      {/* Weighted sum visualization */}
      <div style={{marginTop:18, padding:'12px 14px', border:'1px solid var(--border)', background:'var(--surface-2)'}}>
        <div className="mono upper" style={{color:'var(--text-dim)', fontSize:10, letterSpacing:'0.1em', marginBottom:8}}>
          weighted aggregation
        </div>
        <div style={{display:'flex', height:18, border:'1px solid var(--border)'}}>
          {sorted
            .slice() // don't reorder for the bar — by layer
            .sort((a,b)=>{
              const oa = window.LAYER_META.findIndex(m=>m.key===a.layer);
              const ob = window.LAYER_META.findIndex(m=>m.key===b.layer);
              return oa - ob;
            })
            .map(r => {
              const c = signalColor(r.ai_probability);
              const col = c==='red' ? 'var(--red)' : c==='green' ? 'var(--green)' : 'var(--amber)';
              return (
                <div key={r.layer}
                  title={`${layerMeta(r.layer).short} · w=${pct0(r.weight)} · p=${fmt3(r.ai_probability)}`}
                  style={{
                    flex: r.weight,
                    background: col, opacity: 0.4 + r.ai_probability*0.55,
                    borderRight:'1px solid var(--bg)',
                }}/>
              );
            })}
        </div>
        <div className="mono" style={{display:'flex', justifyContent:'space-between', marginTop:8, fontSize:10, color:'var(--text-dim)'}}>
          <span>← human signal</span><span>per-layer width = weight  ·  intensity = AI probability</span><span>AI signal →</span>
        </div>
      </div>
    </div>
  );
}
function thStyle(align, width){ return { textAlign: align, padding:'10px 12px', fontSize:10, color:'var(--text-dim)', fontWeight:500, letterSpacing:'0.1em', width: width || 'auto' }; }
function tdStyle(align){ return { textAlign: align, padding:'12px 12px', verticalAlign:'middle' }; }


function SentenceHeatmap({ heatmap }){
  const [selected, setSelected] = useStateRpt(null);
  const [hover, setHover] = useStateRpt(null);

  const buckets = useMemoRpt(() => {
    const b = [0,0,0,0,0,0,0,0,0,0];
    heatmap.forEach(s => { b[Math.min(9, Math.floor(s.ai_probability*10))]++; });
    return b;
  }, [heatmap]);

  return (
    <div style={{padding:'18px 22px', borderTop:'1px solid var(--border)'}}>
      <div style={{display:'flex', alignItems:'baseline', justifyContent:'space-between', marginBottom:12, gap:24}}>
        <div className="section-label">▸ sentence heatmap · {heatmap.length} sentences</div>
        <div style={{display:'flex', alignItems:'center', gap:14}}>
          <div className="mono" style={{display:'flex', alignItems:'center', gap:8, fontSize:10, color:'var(--text-dim)'}}>
            <span>0.0</span>
            <span style={{display:'inline-block', width:120, height:8, background:'linear-gradient(90deg, #10b981 0%, #f59e0b 50%, #ef4444 100%)'}}></span>
            <span>1.0</span>
          </div>
        </div>
      </div>

      {/* histogram */}
      <div style={{display:'flex', alignItems:'flex-end', gap:14, marginBottom:14}}>
        <div style={{flex:'0 0 auto'}}>
          <div className="mono upper" style={{fontSize:10, color:'var(--text-dim)', marginBottom:4}}>distribution</div>
          <MiniHistogram buckets={buckets} width={280} height={50} />
          <div className="mono" style={{display:'flex', justifyContent:'space-between', fontSize:9, color:'var(--text-dim)', marginTop:2, width:280}}>
            <span>0</span><span>0.5</span><span>1</span>
          </div>
        </div>
        <div style={{flex:1, display:'flex', flexDirection:'column', gap:6}}>
          <div className="mono upper" style={{fontSize:10, color:'var(--text-dim)'}}>summary</div>
          <div className="mono" style={{fontSize:11, color:'var(--text-muted)', display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8}}>
            <span><span style={{color:'var(--green)'}}>●</span> human-leaning <span style={{color:'var(--text)'}}>{heatmap.filter(s=>s.ai_probability<=0.4).length}</span></span>
            <span><span style={{color:'var(--amber)'}}>●</span> mixed <span style={{color:'var(--text)'}}>{heatmap.filter(s=>s.ai_probability>0.4 && s.ai_probability<0.6).length}</span></span>
            <span><span style={{color:'var(--red)'}}>●</span> AI-leaning <span style={{color:'var(--text)'}}>{heatmap.filter(s=>s.ai_probability>=0.6).length}</span></span>
          </div>
        </div>
      </div>

      {/* heat row */}
      <div className="heat-row" style={{position:'relative'}}>
        {heatmap.map((s, i) => (
          <div key={i}
            className="heat-cell"
            onMouseEnter={() => setHover(i)}
            onMouseLeave={() => setHover(h => h===i ? null : h)}
            onClick={() => setSelected(i)}
            style={{ background: HeatColor(s.ai_probability), border: selected===i ? '1px solid var(--text)' : '1px solid transparent' }}
          />
        ))}
      </div>

      {hover != null && hover !== selected && (
        <div className="mono" style={{
          marginTop:10, padding:'8px 10px', border:'1px solid var(--border)', background:'var(--surface-2)',
          fontSize:11, color:'var(--text-muted)',
        }}>
          <span className="upper" style={{color:'var(--text-dim)', marginRight:8}}>#{heatmap[hover].index}</span>
          <span className="tnum" style={{color: HeatColor(heatmap[hover].ai_probability), marginRight:10}}>{fmt3(heatmap[hover].ai_probability)}</span>
          <span style={{color:'var(--text)'}}>{heatmap[hover].sentence}</span>
        </div>
      )}

      {selected != null && (
        <div className="fade-up" style={{
          marginTop:14, border:'1px solid var(--border)', background:'var(--surface-2)',
          padding:'14px 16px',
        }}>
          <div className="mono" style={{display:'flex', alignItems:'center', gap:14, marginBottom:10, fontSize:11}}>
            <span className="upper" style={{color:'var(--text-dim)'}}>sentence #{heatmap[selected].index}</span>
            <span className="tnum" style={{color: HeatColor(heatmap[selected].ai_probability)}}>p = {fmt3(heatmap[selected].ai_probability)}</span>
            <SignalPill signal={signalLabel(heatmap[selected].ai_probability)} />
            <div style={{flex:1}}></div>
            <button className="btn-ghost" onClick={()=>setSelected(null)} style={{background:'transparent', border:0, color:'var(--text-dim)', cursor:'default', fontSize:11}}>× close</button>
          </div>
          <div style={{lineHeight:1.7, fontSize:14, color:'var(--text-muted)'}}>
            {/* show sentence in context: prev + selected highlighted + next */}
            {selected > 0 && <span>{heatmap[selected-1].sentence + ' '}</span>}
            <span style={{
              background: HeatColor(heatmap[selected].ai_probability) + '33',
              borderBottom: `2px solid ${HeatColor(heatmap[selected].ai_probability)}`,
              padding: '0 2px',
              color:'var(--text)',
            }}>{heatmap[selected].sentence}</span>
            {selected < heatmap.length-1 && <span>{' ' + heatmap[selected+1].sentence}</span>}
          </div>
        </div>
      )}
    </div>
  );
}

function SuspiciousPassages({ passages }){
  if (!passages || passages.length === 0){
    return (
      <div style={{padding:'18px 22px', borderTop:'1px solid var(--border)'}}>
        <div className="section-label" style={{marginBottom:10}}>▸ top suspicious passages</div>
        <div className="mono" style={{color:'var(--text-dim)', fontSize:12, padding:'14px 0'}}>
          ◇ none — no passages crossed the suspicion threshold (p ≥ 0.6)
        </div>
      </div>
    );
  }
  return (
    <div style={{padding:'18px 22px', borderTop:'1px solid var(--border)'}}>
      <div className="section-label" style={{marginBottom:14}}>▸ top suspicious passages · ranked</div>
      <div style={{display:'flex', flexDirection:'column', gap:12}}>
        {passages.map(p => {
          const color = signalColor(p.ai_probability);
          const colorVar = color==='red' ? 'var(--red)' : color==='green' ? 'var(--green)' : 'var(--amber)';
          return (
            <div key={p.rank} style={{
              display:'grid', gridTemplateColumns:'40px 1fr', gap:18,
              padding:'14px 16px',
              borderLeft: `3px solid ${colorVar}`, background: 'var(--surface-2)', border:'1px solid var(--border)', borderLeftWidth:3,
            }}>
              <div className="mono" style={{color:'var(--text-dim)', fontSize:18, fontWeight:600}}>#{p.rank}</div>
              <div style={{display:'flex', flexDirection:'column', gap:8}}>
                <div className="quote">"{p.quote}"</div>
                <div className="mono" style={{display:'flex', gap:18, fontSize:11, color:'var(--text-muted)', flexWrap:'wrap'}}>
                  <span><span className="upper" style={{color:'var(--text-dim)', marginRight:6}}>p</span><span style={{color: colorVar}} className="tnum">{fmt3(p.ai_probability)}</span></span>
                  <span><span className="upper" style={{color:'var(--text-dim)', marginRight:6}}>flagged by</span>{p.flagged_by.map(l => layerMeta(l).short).join(', ')}</span>
                  <span><span className="upper" style={{color:'var(--text-dim)', marginRight:6}}>location</span>{p.location}</span>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}


// ── Per-layer drawer ──────────────────────────────────────────────────────────
function LayerDrawer({ layerKey, payload, onClose }){
  if (!layerKey) return null;
  const meta = layerMeta(layerKey);
  const data = payload.layers[layerKey];
  const row = payload.verdict.layer_breakdown.find(r => r.layer === layerKey);
  const color = signalColor(row.ai_probability);
  const colorVar = color==='red' ? 'var(--red)' : color==='green' ? 'var(--green)' : 'var(--amber)';

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose}></div>
      <div className="drawer">
        <div style={{padding:'18px 22px', borderBottom:'1px solid var(--border)', display:'flex', alignItems:'flex-start', gap:14}}>
          <div style={{flex:1}}>
            <div className="mono" style={{color:'var(--text-dim)', fontSize:11, marginBottom:6}}>{meta.short} · weight {pct0(meta.weight)}</div>
            <div className="sans" style={{fontSize:18, fontWeight:600, marginBottom:8}}>{meta.name}</div>
            <div style={{display:'flex', alignItems:'center', gap:14}}>
              <span className="mono tnum" style={{color: colorVar, fontSize:22, fontWeight:600}}>{fmt3(row.ai_probability)}</span>
              <SignalPill signal={row.signal} />
            </div>
          </div>
          <button className="btn-ghost" onClick={onClose} style={{background:'transparent', border:0, color:'var(--text-dim)', fontSize:14, cursor:'default', padding:6}}>× close</button>
        </div>
        <div style={{flex:1, overflow:'auto', padding:'18px 22px'}}>
          <LayerDetailBody layerKey={layerKey} data={data} payload={payload} />
        </div>
      </div>
    </>
  );
}

function LayerDetailBody({ layerKey, data, payload }){
  switch (layerKey){
    case 'l1_perplexity': return <L1Detail data={data}/>;
    case 'l2_burstiness': return <L2Detail data={data}/>;
    case 'l3_stylometrics': return <L3Detail data={data}/>;
    case 'l4_semantic': return <L4Detail data={data}/>;
    case 'l5_unicode': return <L5Detail data={data}/>;
    case 'l6_metadata': return <L6Detail data={data}/>;
    case 'l7_fonts': return <L7Detail data={data}/>;
    case 'l8_ensemble': return <L8Detail data={data}/>;
    case 'l9_hallucination': return <L9Detail data={data}/>;
    case 'l10_revisions': return <L10Detail data={data}/>;
    default: return <pre>{JSON.stringify(data, null, 2)}</pre>;
  }
}

function DetailSection({ title, children }){
  return (
    <div style={{marginBottom:22}}>
      <div className="section-label" style={{marginBottom:10}}>{title}</div>
      {children}
    </div>
  );
}
function KV({ rows }){
  return (
    <dl className="kvp" style={{gridTemplateColumns:'150px 1fr'}}>
      {rows.map(([k,v]) => (
        <React.Fragment key={k}>
          <dt>{k}</dt><dd>{v}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

function L1Detail({ data }){
  return <>
    <DetailSection title="▸ summary">
      <KV rows={[
        ['mean perplexity', data.mean_perplexity.toFixed(2)],
        ['std', data.std_perplexity.toFixed(2)],
        ['evidence', data.evidence],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ per-sentence perplexity (first 30)">
      <div style={{border:'1px solid var(--border)', padding:8, background:'var(--surface-2)'}}>
        <MiniLine values={data.per_sentence_scores} width={460} height={140} threshold={30} />
      </div>
      <div className="mono" style={{fontSize:10, color:'var(--text-dim)', marginTop:6}}>
        dashed line = AI/human threshold (perplexity 30)
      </div>
    </DetailSection>
    {data.low_perplexity_sentences.length > 0 && (
      <DetailSection title="▸ lowest-perplexity sentences (most AI-like)">
        <div style={{display:'flex', flexDirection:'column', gap:8}}>
          {data.low_perplexity_sentences.map((s, i) => (
            <div key={i} className="mono" style={{padding:'10px 12px', border:'1px solid var(--border)', fontSize:12}}>
              <div style={{display:'flex', justifyContent:'space-between', marginBottom:6, fontSize:10, color:'var(--text-dim)'}}>
                <span>sent #{s.index}</span>
                <span className="tnum" style={{color:'var(--red)'}}>perp = {s.perplexity.toFixed(1)}</span>
              </div>
              <div style={{color:'var(--text-muted)', fontSize:12}}>{s.sentence}</div>
            </div>
          ))}
        </div>
      </DetailSection>
    )}
  </>;
}

function L2Detail({ data }){
  return <>
    <DetailSection title="▸ summary">
      <KV rows={[
        ['mean sentence len', data.mean_length.toFixed(1)],
        ['std', data.sentence_length_std.toFixed(2)],
        ['Gini coefficient', data.gini_coefficient.toFixed(2)],
        ['evidence', data.evidence],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ length distribution (words/sentence)">
      <div style={{border:'1px solid var(--border)', padding:'12px', background:'var(--surface-2)'}}>
        <div style={{display:'flex', alignItems:'flex-end', gap:3, height:120}}>
          {data.length_distribution.map((v,i)=>{
            const max = Math.max(...data.length_distribution);
            return <div key={i} style={{flex:1, height:`${(v/max)*100}%`, background:'var(--blue)', opacity:0.6 + (v/max)*0.4}} title={`bin ${i*5}-${(i+1)*5}: ${v} sentences`}></div>;
          })}
        </div>
        <div className="mono" style={{display:'flex', justifyContent:'space-between', marginTop:6, fontSize:10, color:'var(--text-dim)'}}>
          <span>0</span><span>25</span><span>50+</span>
        </div>
      </div>
    </DetailSection>
  </>;
}

function L3Detail({ data }){
  const aiSig = ['ai_phrase_density','hedging_density','em_dash_density','passive_voice_ratio','nominalization_ratio'];
  return <>
    {data.top_ai_signals.length > 0 && (
      <DetailSection title="▸ top AI signals">
        <div style={{display:'flex', flexDirection:'column', gap:6}}>
          {data.top_ai_signals.map((s, i) => (
            <div key={i} className="mono" style={{
              padding:'8px 12px', borderLeft:'2px solid var(--red)',
              background:'rgba(239,68,68,.06)', fontSize:12, color:'var(--text)',
            }}>{s}</div>
          ))}
        </div>
      </DetailSection>
    )}
    <DetailSection title="▸ stylometric features">
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:'2px 24px', fontFamily:'var(--mono)', fontSize:12}}>
        {Object.entries(data.features).map(([k,v]) => {
          const isAi = aiSig.includes(k) && v > 1;
          return (
            <div key={k} style={{display:'flex', justifyContent:'space-between', padding:'6px 0', borderBottom:'1px solid var(--border)'}}>
              <span style={{color:'var(--text-dim)'}}>{k}</span>
              <span className="tnum" style={{color: isAi ? 'var(--red)' : 'var(--text)'}}>{Number(v).toFixed(4)}</span>
            </div>
          );
        })}
      </div>
    </DetailSection>
  </>;
}

function L4Detail({ data }){
  return <>
    <DetailSection title="▸ summary">
      <KV rows={[
        ['mean similarity', data.paragraph_similarity_mean.toFixed(3)],
        ['std', data.paragraph_similarity_std.toFixed(3)],
        ['evidence', data.evidence],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ paragraph cosine matrix (excerpt)">
      <table style={{borderCollapse:'collapse', fontFamily:'var(--mono)', fontSize:11}}>
        <tbody>
          {data.cosine_matrix_excerpt.map((row,i) => (
            <tr key={i}>
              {row.map((v,j) => (
                <td key={j} style={{
                  padding:'8px 10px', border:'1px solid var(--border)',
                  background: i===j ? 'var(--surface-3)' : `rgba(59,130,246,${v*0.45})`,
                  color: 'var(--text)', textAlign:'center', minWidth:60,
                }}>{v.toFixed(2)}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <div className="mono" style={{fontSize:10, color:'var(--text-dim)', marginTop:8}}>
        cell intensity = cosine similarity. uniform off-diagonals → AI-typical even drift.
      </div>
    </DetailSection>
  </>;
}

function L5Detail({ data }){
  return <>
    <DetailSection title="▸ summary">
      <KV rows={[
        ['invisible chars', data.invisible_char_count],
        ['homoglyphs', data.homoglyph_count],
        ['evidence', data.evidence],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ findings">
      {data.findings.length === 0 ? (
        <div className="mono" style={{color:'var(--green)', fontSize:12, padding:'10px 0'}}>
          ✓ clean — no suspicious code points detected
        </div>
      ) : (
        <table style={{width:'100%', borderCollapse:'collapse', fontFamily:'var(--mono)', fontSize:12}}>
          <thead>
            <tr>
              <th style={thStyle('left',40)}>Char</th>
              <th style={thStyle('left',80)}>Hex</th>
              <th style={thStyle('left')}>Name</th>
              <th style={thStyle('right',60)}>Count</th>
              <th style={thStyle('left',120)}>First positions</th>
            </tr>
          </thead>
          <tbody>
            {data.findings.map((f,i) => (
              <tr key={i} style={{borderBottom:'1px solid var(--border)'}}>
                <td style={tdStyle('left')}><span style={{fontSize:14, color:'var(--amber)'}}>{f.char}</span></td>
                <td style={tdStyle('left')}>{f.hex}</td>
                <td style={{...tdStyle('left'), color:'var(--text-muted)', fontSize:11}}>{f.name}</td>
                <td style={tdStyle('right')}>{f.count}</td>
                <td style={{...tdStyle('left'), color:'var(--text-dim)', fontSize:11}}>{f.first_positions.join(', ')}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}
    </DetailSection>
  </>;
}

function L6Detail({ data }){
  return <>
    <DetailSection title="▸ document metadata">
      <KV rows={[
        ['creator', <span style={{color: data.creator?.toLowerCase().includes('chatgpt') || data.creator?.toLowerCase().includes('gpt') ? 'var(--red)' : 'var(--text)'}}>{data.creator || '—'}</span>],
        ['producer', data.producer || '—'],
        ['author', data.author || <span style={{color:'var(--text-dim)'}}>(empty)</span>],
        ['created', data.created],
        ['modified', data.modified],
        ['EOF markers', data.eof_markers],
        ['pages', data.page_count],
      ]}/>
    </DetailSection>
    {data.signals.length > 0 && (
      <DetailSection title="▸ flagged signals">
        <div style={{display:'flex', flexDirection:'column', gap:6}}>
          {data.signals.map((s, i) => (
            <div key={i} className="mono" style={{
              padding:'8px 12px', borderLeft:'2px solid var(--red)',
              background:'rgba(239,68,68,.06)', fontSize:12, color:'var(--text)',
            }}>{s}</div>
          ))}
        </div>
      </DetailSection>
    )}
  </>;
}

function L7Detail({ data }){
  return <>
    <DetailSection title="▸ summary">
      <KV rows={[
        ['font count', data.font_count],
        ['entropy', data.entropy.toFixed(2)],
        ['default-only', data.default_only ? 'yes' : 'no'],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ fonts in use">
      <div style={{display:'flex', flexDirection:'column', gap:0}}>
        {data.fonts.map((f, i) => (
          <div key={i} className="mono" style={{padding:'8px 12px', borderBottom:'1px solid var(--border)', fontSize:12}}>
            <span style={{color:'var(--text-dim)', marginRight:10}}>{String(i+1).padStart(2,'0')}</span>
            {f}
          </div>
        ))}
      </div>
    </DetailSection>
  </>;
}

function L8Detail({ data }){
  const max = Math.max(...data.model_results.map(m => m.ai_probability), 1);
  return <>
    <DetailSection title="▸ ensemble verdict">
      <div className="mono tnum" style={{fontSize:30, color: signalColor(data.ai_probability) === 'red' ? 'var(--red)' : signalColor(data.ai_probability)==='green' ? 'var(--green)' : 'var(--amber)', fontWeight:600}}>
        {fmt3(data.ai_probability)}
      </div>
      <div className="mono" style={{color:'var(--text-muted)', fontSize:11, marginTop:4}}>weighted across 3 classifier models</div>
    </DetailSection>
    <DetailSection title="▸ per-model results">
      <div style={{display:'flex', flexDirection:'column', gap:14}}>
        {data.model_results.map(m => {
          const c = signalColor(m.ai_probability);
          const colorVar = c==='red' ? 'var(--red)' : c==='green' ? 'var(--green)' : 'var(--amber)';
          return (
            <div key={m.model}>
              <div className="mono" style={{display:'flex', justifyContent:'space-between', marginBottom:6, fontSize:12}}>
                <span style={{color:'var(--text)'}}>{m.model}</span>
                <span className="tnum" style={{color: colorVar}}>{fmt3(m.ai_probability)}</span>
              </div>
              <div style={{height:14, background:'var(--surface-2)', border:'1px solid var(--border)', position:'relative'}}>
                <div style={{height:'100%', width:`${m.ai_probability*100}%`, background: colorVar, opacity:0.85}}></div>
                <div style={{position:'absolute', top:0, left:`${m.weight*100}%`, height:'100%', width:1, background:'var(--text-dim)'}}></div>
              </div>
              <div className="mono" style={{display:'flex', justifyContent:'space-between', marginTop:4, fontSize:10, color:'var(--text-dim)'}}>
                <span>weight {pct0(m.weight)} · {m.chunks_analyzed} chunks</span>
                <span>vertical tick marks weight position</span>
              </div>
            </div>
          );
        })}
      </div>
    </DetailSection>
  </>;
}

function L9Detail({ data }){
  const statusColor = { verified: 'var(--green)', unverifiable: 'var(--amber)', contradicted: 'var(--red)' };
  return <>
    <DetailSection title="▸ extracted claims · verifier output">
      <div style={{display:'flex', flexDirection:'column', gap:8}}>
        {data.claims.map((c, i) => (
          <div key={i} style={{
            padding:'10px 12px', border:'1px solid var(--border)', background:'var(--surface-2)',
            display:'grid', gridTemplateColumns:'1fr 110px', gap:10, alignItems:'flex-start',
          }}>
            <div style={{fontSize:12, color:'var(--text)', lineHeight:1.5}}>{c.text}</div>
            <div className="mono upper" style={{
              fontSize:10, padding:'3px 8px', textAlign:'center',
              border:`1px solid ${statusColor[c.status]}`,
              color: statusColor[c.status],
            }}>{c.status}</div>
          </div>
        ))}
      </div>
    </DetailSection>
  </>;
}

function L10Detail({ data }){
  return <>
    <DetailSection title="▸ revision footprint">
      <KV rows={[
        ['DOCX revisions', data.docx_revision_count],
        ['emit passes', data.page_emit_passes],
        ['edit span (min)', data.last_save_minutes_after_create.toLocaleString()],
        ['evidence', data.evidence],
      ]}/>
    </DetailSection>
    <DetailSection title="▸ interpretation">
      <div className="mono" style={{padding:'10px 12px', border:'1px solid var(--border)', background:'var(--surface-2)', fontSize:12, color:'var(--text-muted)', lineHeight:1.55}}>
        Humans iterate. A document with hundreds of revisions across multiple sessions
        is unlikely to have been emitted in a single AI generation pass. Conversely,
        a 0-revision document with a single emit pass is consistent with raw model output.
      </div>
    </DetailSection>
  </>;
}

window.HeroStrip = HeroStrip;
window.ConflictBanner = ConflictBanner;
window.DocumentPanel = DocumentPanel;
window.LayerTable = LayerTable;
window.SentenceHeatmap = SentenceHeatmap;
window.SuspiciousPassages = SuspiciousPassages;
window.LayerDrawer = LayerDrawer;
