// Column 2 — Analysis blocks. Per-block editable cards for the selected file.
const analysisStyles = {
  col: { background:'var(--paper)', display:'flex', flexDirection:'column', minHeight:0, borderRight:'1px solid var(--line)' },
  head: { padding:'16px 28px 14px 28px', borderBottom:'1px solid var(--line)', display:'flex', alignItems:'flex-end', gap:20, justifyContent:'space-between' },
  eyebrow: { fontFamily:'var(--mono)', fontSize:10, letterSpacing:'.16em', color:'var(--ink-3)', textTransform:'uppercase' },
  title: { fontFamily:'var(--serif)', fontSize:22, lineHeight:1.1, marginTop:4, color:'var(--ink)' },
  sub: { fontSize:11.5, color:'var(--ink-3)', marginTop:4, maxWidth:540 },
  rightHead: { display:'flex', gap:6 },
  pill: { height:26, padding:'0 10px', border:'1px solid var(--line-2)', borderRadius:999, background:'var(--paper)', fontSize:11, color:'var(--ink-2)', display:'inline-flex', alignItems:'center', gap:6, cursor:'pointer' },
  pillActive: { background:'var(--ink)', color:'var(--paper)', border:'1px solid var(--ink)' },
  scroll: { overflowY:'auto', flex:1, padding:'20px 28px 160px 28px' },

  fileCard: { border:'1px solid var(--line)', borderRadius:10, background:'var(--paper)', marginBottom:24, overflow:'hidden' },
  fileHead: { display:'grid', gridTemplateColumns:'auto 1fr auto', columnGap:14, alignItems:'center', padding:'14px 18px', borderBottom:'1px solid var(--line)', background:'var(--paper-2)' },
  fileIcon: { width:34, height:34, borderRadius:6, background:'var(--paper)', border:'1px solid var(--line-2)', display:'flex', alignItems:'center', justifyContent:'center', color:'var(--ink-2)' },
  fileTitle: { fontFamily:'var(--serif)', fontSize:16, color:'var(--ink)', lineHeight:1.2 },
  fileSub: { fontFamily:'var(--mono)', fontSize:10, color:'var(--ink-3)', marginTop:3, letterSpacing:'.06em', textTransform:'uppercase' },
  actions: { display:'flex', gap:6 },

  fileBody: { padding:'14px 22px 18px 22px' },

  block: { borderTop:'1px dashed var(--line)', padding:'16px 0 4px 0' },
  blockFirst: { borderTop:'none', paddingTop:4 },
  blockHead: { display:'flex', alignItems:'baseline', justifyContent:'space-between', gap:12, marginBottom:8 },
  blockTitle: { fontFamily:'var(--serif)', fontSize:14, color:'var(--ink)', fontWeight:500 },
  blockMeta: { fontFamily:'var(--mono)', fontSize:10, color:'var(--ink-4)', letterSpacing:'.08em', textTransform:'uppercase' },

  summary: {
    fontFamily:'var(--display)', fontSize:15.5, lineHeight:1.5, color:'var(--ink)', fontStyle:'italic', fontWeight:400,
    cursor:'text', padding:'8px 12px', margin:'0 -12px', borderRadius:8, minHeight:46,
    borderWidth:1, borderStyle:'solid', borderColor:'transparent',
    transition:'background .15s, border-color .15s',
    position:'relative',
  },
  summaryEditing: { fontFamily:'var(--display)', fontSize:15.5, lineHeight:1.5, color:'var(--ink)', fontStyle:'italic', fontWeight:400, width:'calc(100% + 24px)', minHeight:80, border:'1px solid var(--accent)', borderRadius:8, padding:'8px 12px', margin:'0 -12px', background:'var(--hl)', resize:'vertical', outline:'none' },
  summaryHint: {
    position:'absolute', top:4, right:8, fontFamily:'var(--mono)', fontSize:9,
    letterSpacing:'.1em', color:'var(--ink-4)', textTransform:'uppercase',
    opacity:0, transition:'opacity .15s',
  },
  editCommitRow: { display:'flex', gap:8, justifyContent:'flex-end', marginTop:6 },
  editBtn: { fontFamily:'var(--mono)', fontSize:10, letterSpacing:'.1em', textTransform:'uppercase', padding:'5px 10px', borderRadius:999, cursor:'pointer' },

  kwRow: { display:'flex', flexWrap:'wrap', gap:8, marginTop:10 },
  kw: (drag, editing) => ({
    display:'inline-flex', alignItems:'stretch', borderRadius:999, overflow:'hidden',
    border: editing ? '1px solid var(--accent)' : '1px solid var(--line-2)',
    background: editing ? 'var(--hl)' : 'var(--paper)',
    cursor: drag ? 'grabbing' : 'pointer', userSelect:'none',
    boxShadow: drag ? '0 8px 20px rgba(0,0,0,.08)' : 'none',
    transform: drag ? 'scale(1.03)' : 'none',
    transition:'transform .14s, box-shadow .14s',
  }),
  kwDrag: { padding:'0 6px 0 8px', display:'flex', alignItems:'center', color:'var(--ink-4)' },
  kwMain: { padding:'6px 10px', display:'flex', alignItems:'center', gap:8 },
  kwEn: { fontSize:12.5, color:'var(--ink)', fontWeight:500 },
  kwKo: { fontFamily:'var(--display)', fontSize:13, color:'var(--ink-2)', fontStyle:'italic' },
  heat: { width:28, height:3, borderRadius:2, background:'var(--line)', overflow:'hidden', position:'relative' },
  heatFill: (v) => ({ position:'absolute', inset:0, width:`${v*100}%`, background:'var(--accent)' }),
  kwAdd: { padding:'0 9px', display:'flex', alignItems:'center', borderLeft:'1px solid var(--line)', color:'var(--ink-3)', cursor:'pointer' },
  kwDel: { padding:'0 9px', display:'flex', alignItems:'center', borderLeft:'1px solid var(--line)', color:'var(--ink-4)', cursor:'pointer' },

  addKwBtn: { marginTop:8, padding:'4px 10px', border:'1px dashed var(--line-2)', borderRadius:999, background:'transparent', fontSize:11, color:'var(--ink-3)', cursor:'pointer', fontFamily:'var(--mono)', letterSpacing:'.06em' },

  tierTag: (t) => ({
    fontFamily:'var(--mono)', fontSize:9, letterSpacing:'.12em', textTransform:'uppercase',
    color: t==='metaphor' ? 'var(--accent)' : t==='universal' ? 'var(--accent-3)' : 'var(--ink-4)',
    marginRight:4,
  }),

  emptyBlock: {
    border:'1px dashed var(--line-2)', borderRadius:10, padding:'40px 28px', textAlign:'center', color:'var(--ink-3)',
    fontFamily:'var(--display)', fontSize:18, fontStyle:'italic',
  },
};

const mutateStyles = {
  overlay: { position:'fixed', inset:0, background:'rgba(0,0,0,.35)', zIndex:60, display:'flex', alignItems:'center', justifyContent:'center' },
  panel: { width:520, maxHeight:'78vh', background:'var(--paper)', borderRadius:14, boxShadow:'0 30px 60px rgba(0,0,0,.22)', display:'flex', flexDirection:'column', overflow:'hidden' },
  head: { padding:'16px 20px 12px 20px', borderBottom:'1px solid var(--line)' },
  headRow: { display:'flex', alignItems:'center', gap:10, justifyContent:'space-between' },
  word: { fontFamily:'var(--serif)', fontSize:20, color:'var(--ink)' },
  wordKo: { fontFamily:'var(--display)', fontSize:15, color:'var(--ink-2)', fontStyle:'italic', marginLeft:6 },
  source: { fontFamily:'var(--mono)', fontSize:10, color:'var(--ink-4)', letterSpacing:'.08em', textTransform:'uppercase', marginTop:4 },
  actions: { display:'flex', gap:6, padding:'12px 20px', borderBottom:'1px solid var(--line)', background:'var(--paper-2)' },
  actionBtn: (active) => ({
    height:28, padding:'0 12px', border: active ? '1px solid var(--ink)' : '1px solid var(--line-2)',
    background: active ? 'var(--ink)' : 'var(--paper)', color: active ? 'var(--paper)' : 'var(--ink)',
    borderRadius:999, fontSize:11.5, fontFamily:'var(--sans)', cursor:'pointer', display:'inline-flex', alignItems:'center', gap:4,
  }),
  body: { flex:1, overflowY:'auto', padding:'14px 20px', minHeight:120 },
  msg: (me) => ({
    margin:'8px 0', padding:'10px 12px', borderRadius:10,
    background: me ? 'var(--ink)' : 'var(--paper-2)',
    color: me ? 'var(--paper)' : 'var(--ink)',
    fontSize:12.5, lineHeight:1.55, maxWidth:'92%',
    alignSelf: me ? 'flex-end' : 'flex-start',
    fontFamily: me ? 'var(--sans)' : 'var(--serif)',
  }),
  msgWrap: { display:'flex', flexDirection:'column' },
  sugRow: { display:'flex', flexWrap:'wrap', gap:8, marginTop:10 },
  sug: { display:'inline-flex', alignItems:'stretch', borderRadius:999, border:'1px solid var(--line-2)', background:'var(--paper)', cursor:'pointer' },
  sugMain: { padding:'6px 12px', display:'flex', alignItems:'center', gap:8 },
  sugEn: { fontSize:12.5, color:'var(--ink)', fontWeight:500 },
  sugKo: { fontFamily:'var(--display)', fontSize:13, color:'var(--ink-2)', fontStyle:'italic' },
  sugAdd: { padding:'0 10px', display:'flex', alignItems:'center', borderLeft:'1px solid var(--line)', color:'var(--accent)' },
  composer: { padding:'10px 14px', borderTop:'1px solid var(--line)', display:'flex', gap:8, alignItems:'center', background:'var(--paper)' },
  input: { flex:1, border:'1px solid var(--line-2)', borderRadius:999, padding:'7px 14px', fontSize:12.5, fontFamily:'var(--sans)', background:'var(--paper-2)', outline:'none', color:'var(--ink)' },
  send: { height:30, padding:'0 14px', border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--paper)', borderRadius:999, fontSize:12, cursor:'pointer' },
  closeBtn: { border:'none', background:'transparent', color:'var(--ink-3)', cursor:'pointer', fontFamily:'var(--mono)', fontSize:10, letterSpacing:'.14em' },
};

function KeywordMutatePopup({ file_id, block_id, kw, onClose, onAddKw, onReplaceKw }) {
  const [action, setAction] = React.useState('sharper');
  const [input, setInput] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [messages, setMessages] = React.useState([]); // {me, text, kws?}
  const bodyRef = React.useRef(null);

  React.useEffect(() => {
    // Auto-fire initial mutate when action picked fresh (not on re-render).
    run(action, null);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  React.useEffect(() => {
    if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
  }, [messages, busy]);

  const run = async (actionKey, freeText) => {
    setBusy(true);
    const userText = freeText || ({
      sharper: '이 키워드, 더 날카롭게.',
      opposite: '이 키워드를 뒤집어봐.',
      similar: '비슷한 각도로 2~3개 더.',
      explain: '이 키워드가 왜 어울리거나 안 어울리는지 설명해줘.',
    }[actionKey] || '제안 부탁해.');
    const nextMessages = [...messages, {me:true, text:userText}];
    setMessages(nextMessages);
    try {
      const res = await API.mutateKeyword({
        file_id, block_id,
        term: kw.ko || kw.w,
        ko: kw.ko || null,
        action: actionKey,
        conversation: nextMessages.map(m => ({me:m.me, text:m.text})),
      });
      setMessages(m => [...m, {me:false, text:res.reply || '', kws: res.kws || []}]);
    } catch(e) {
      setMessages(m => [...m, {me:false, text:'⚠ ' + e.message}]);
    } finally { setBusy(false); }
  };

  const onSend = () => {
    const t = input.trim();
    if (!t) return;
    setInput('');
    run(action, t);
  };

  const onAction = (a) => {
    setAction(a);
    run(a, null);
  };

  return (
    <div style={mutateStyles.overlay} onClick={onClose}>
      <div style={mutateStyles.panel} onClick={e => e.stopPropagation()}>
        <div style={mutateStyles.head}>
          <div style={mutateStyles.headRow}>
            <div>
              <span style={mutateStyles.word}>{kw.w}</span>
              {kw.ko && <span style={mutateStyles.wordKo}>· {kw.ko}</span>}
              {kw.tier && <span style={{...analysisStyles.tierTag(kw.tier), marginLeft:10}}>{kw.tier}</span>}
              <div style={mutateStyles.source}>Mini-chat · mutate this keyword</div>
            </div>
            <button style={mutateStyles.closeBtn} onClick={onClose}>CLOSE ✕</button>
          </div>
        </div>
        <div style={mutateStyles.actions}>
          {[
            ['sharper','Sharper ↑'],
            ['opposite','Opposite ⇄'],
            ['similar','Similar ≈'],
            ['explain','Explain ℹ'],
          ].map(([k,label]) => (
            <button key={k} style={mutateStyles.actionBtn(action===k)}
                    onClick={() => onAction(k)} disabled={busy}>{label}</button>
          ))}
        </div>
        <div ref={bodyRef} style={{...mutateStyles.body, display:'flex', flexDirection:'column'}}>
          {messages.map((m,i) => (
            <div key={i} style={mutateStyles.msgWrap}>
              <div style={mutateStyles.msg(m.me)}>{m.text || (busy && i===messages.length-1 ? '…' : '')}</div>
              {!m.me && m.kws && m.kws.length>0 && (
                <div style={mutateStyles.sugRow}>
                  {m.kws.map((s,j) => (
                    <div key={j} style={mutateStyles.sug} title={s.why}>
                      <div style={mutateStyles.sugMain} onClick={() => onReplaceKw && onReplaceKw({...s, block_id, tier: s.tier || kw.tier})}>
                        {s.tier && <span style={analysisStyles.tierTag(s.tier)}>{s.tier}</span>}
                        <span style={mutateStyles.sugEn}>{s.w}</span>
                        {s.ko && <span style={mutateStyles.sugKo}>{s.ko}</span>}
                      </div>
                      <div style={mutateStyles.sugAdd} title="Pin to board" onClick={() => onAddKw({...s, from: file_id, tier: s.tier || kw.tier, block_id})}>
                        <Ico.plus s={11}/>
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))}
          {busy && <div style={{...mutateStyles.msg(false), opacity:.6}}>Thinking…</div>}
        </div>
        <div style={mutateStyles.composer}>
          <input style={mutateStyles.input}
                 placeholder="Ask for a specific angle — e.g. 더 촉각적으로, 레트로 감성으로"
                 value={input}
                 disabled={busy}
                 onChange={e => setInput(e.target.value)}
                 onKeyDown={e => { if (e.key==='Enter') onSend(); }}/>
          <button style={mutateStyles.send} onClick={onSend} disabled={busy || !input.trim()}>Send</button>
        </div>
      </div>
    </div>
  );
}

function KwChip({ kw, fileId, onDrag, onClick, onAdd, onDelete, editing }) {
  const [dragging, setDragging] = React.useState(false);
  return (
    <div draggable
         onDragStart={(e) => {
           e.dataTransfer.setData('application/kw', JSON.stringify({...kw, from: fileId}));
           e.dataTransfer.effectAllowed = 'copy';
           setDragging(true);
         }}
         onDragEnd={() => setDragging(false)}
         style={analysisStyles.kw(dragging, editing)}
         title={kw.why}>
      <div style={analysisStyles.kwDrag} onClick={onClick}><Ico.drag/></div>
      <div style={analysisStyles.kwMain} onClick={onClick}>
        {kw.tier && <span style={analysisStyles.tierTag(kw.tier)}>{kw.tier}</span>}
        <span style={analysisStyles.kwEn}>{kw.w}</span>
        {kw.ko && <span style={analysisStyles.kwKo}>{kw.ko}</span>}
        {typeof kw.heat === 'number' && (
          <span style={analysisStyles.heat}><span style={analysisStyles.heatFill(kw.heat)}/></span>
        )}
      </div>
      <div style={analysisStyles.kwAdd} title="Pin to board" onClick={onAdd}>
        <Ico.plus s={11}/>
      </div>
      {onDelete && (
        <div style={analysisStyles.kwDel} title="Remove from block" onClick={onDelete}>×</div>
      )}
    </div>
  );
}

function EditableSummary({ value, onSave }) {
  const [editing, setEditing] = React.useState(false);
  const [hover, setHover] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [draft, setDraft] = React.useState(value || '');
  const taRef = React.useRef(null);
  React.useEffect(() => { if (!editing) setDraft(value || ''); }, [value, editing]);

  const commit = async () => {
    const trimmed = draft.trim();
    if (trimmed === (value || '').trim()) { setEditing(false); return; }
    setSaving(true);
    try { await onSave(trimmed); } finally {
      setSaving(false); setEditing(false);
    }
  };
  const cancel = () => { setDraft(value || ''); setEditing(false); };

  if (editing) {
    return (
      <div>
        <textarea ref={taRef} autoFocus style={analysisStyles.summaryEditing}
                  value={draft}
                  onChange={e => setDraft(e.target.value)}
                  onKeyDown={e => {
                    if (e.key === 'Escape') cancel();
                    if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) commit();
                  }}/>
        <div style={analysisStyles.editCommitRow}>
          <button style={{...analysisStyles.editBtn, border:'1px solid var(--line-2)', background:'var(--paper)', color:'var(--ink-2)'}}
                  onClick={cancel} disabled={saving}>ESC · 취소</button>
          <button style={{...analysisStyles.editBtn, border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--paper)'}}
                  onClick={commit} disabled={saving}>{saving ? '저장 중…' : '⌘↵ · 저장'}</button>
        </div>
      </div>
    );
  }
  const hoverStyle = hover
    ? { background:'var(--hl)', borderColor:'var(--line-2)' }
    : {};
  return (
    <div
      style={{...analysisStyles.summary, ...hoverStyle}}
      onClick={() => setEditing(true)}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      title="클릭해서 요약 편집 · Click to edit summary">
      {value || <span style={{color:'var(--ink-4)'}}>요약 없음 — 클릭해서 직접 입력하세요</span>}
      <span style={{...analysisStyles.summaryHint, opacity: hover ? 1 : 0}}>✏ 편집</span>
    </div>
  );
}

function BlockCard({ file, block, isFirst, onEditSummary, onDeleteKw, onAddKw, onOpenMutate }) {
  const pending = !!block._pending;
  return (
    <div style={{...analysisStyles.block, ...(isFirst ? analysisStyles.blockFirst : {}), opacity: pending ? .55 : 1}}>
      <div style={analysisStyles.blockHead}>
        <div style={analysisStyles.blockTitle}>{block.title || 'Untitled block'}</div>
        <div style={analysisStyles.blockMeta}>
          {block.page ? `p. ${block.page} · ` : ''}
          {pending
            ? <span style={{color:'var(--accent-3)'}}>● 키워드 뽑는 중 · extracting…</span>
            : `${(block.keywords||[]).length} keywords`}
        </div>
      </div>
      {pending && !block.summary ? (
        <div style={{...analysisStyles.summary, color:'var(--ink-4)', fontStyle:'italic'}}>
          이 블록 아직 처리 중 — 곧 채워집니다.
        </div>
      ) : (
        <EditableSummary value={block.summary || ''} onSave={text => onEditSummary(block.id, text)}/>
      )}
      <div style={analysisStyles.kwRow}>
        {(block.keywords || []).map((k, i) => (
          <KwChip key={k.w + '|' + k.ko + '|' + i}
                  kw={k} fileId={file.id}
                  onClick={() => onOpenMutate(block.id, k)}
                  onAdd={(e) => { e.stopPropagation(); onAddKw({...k, from: file.id, block_id: block.id}); }}
                  onDelete={(e) => { e.stopPropagation(); onDeleteKw(block.id, i); }}/>
        ))}
        {!pending && (block.keywords || []).length === 0 && (
          <div style={{fontSize:11.5, color:'var(--ink-4)', fontFamily:'var(--mono)', letterSpacing:'.08em', textTransform:'uppercase'}}>
            키워드 없음 · click a word in the summary or ask the agent for more.
          </div>
        )}
        {pending && (block.keywords || []).length === 0 && (
          <div style={{display:'flex', gap:6}}>
            {[0,1,2,3].map(i => (
              <span key={i} style={{
                display:'inline-block', width:72, height:24, borderRadius:999,
                background:'var(--paper-2)', border:'1px dashed var(--line-2)',
                animation:`pulse 1.4s ease-in-out ${i*0.12}s infinite`,
              }}/>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function FileBlock({ file, analysis, onAddKw, onAnalyzeFile, analyzing, onPatchBlock, onOpenMutate }) {
  const Kind = kindIcon(file.kind);
  const blocks = analysis && analysis.blocks;
  const hasAnalysis = !!(analysis && (analysis.summary || (analysis.keywords || []).length || (blocks && blocks.length)));
  const progress = analyzing && typeof analyzing === 'object' ? analyzing : null;
  const isAnalyzing = !!analyzing;
  const liveStatus = progress
    ? (progress.phase === 'parse'
        ? '파일 읽는 중 · parsing…'
        : progress.total
          ? `키워드 생성 · ${progress.completed}/${progress.total} 블록 완료`
          : '분석 중…')
    : null;

  const onEditSummary = (block_id, text) => onPatchBlock(file.id, block_id, {summary: text});
  const onDeleteKw = (block_id, idx) => {
    const blk = (analysis.blocks || []).find(b => b.id === block_id);
    if (!blk) return;
    const next = blk.keywords.filter((_, i) => i !== idx);
    onPatchBlock(file.id, block_id, {keywords: next});
  };

  return (
    <div style={analysisStyles.fileCard}>
      <div style={analysisStyles.fileHead}>
        <div style={analysisStyles.fileIcon}><Kind s={16}/></div>
        <div>
          <div style={analysisStyles.fileTitle}>{file.name}</div>
          <div style={analysisStyles.fileSub}>
            {kindLabel(file.kind)} · {file.size} · {file.date}
            {file.duration && <> · {file.duration}</>}
            {file.pages && <> · {file.pages} pages</>}
            {isAnalyzing
              ? <span style={{color:'var(--accent-3)', marginLeft:10}}>● {liveStatus}</span>
              : hasAnalysis
                ? <span style={{color:'var(--accent-2)', marginLeft:10}}>● 분석 완료 · {blocks ? blocks.length : (analysis.block_count||0)} blocks</span>
                : <span style={{color:'var(--ink-4)', marginLeft:10}}>○ 분석 대기</span>}
          </div>
        </div>
        <div style={analysisStyles.actions}>
          <button style={analysisStyles.pill} onClick={() => onAnalyzeFile(file.id)} disabled={isAnalyzing}>
            <Ico.sparkle s={10}/> {isAnalyzing ? '분석 중…' : hasAnalysis ? '다시 분석' : '분석 시작'}
          </button>
        </div>
      </div>

      <div style={analysisStyles.fileBody}>
        {!hasAnalysis && (
          <div style={{color:'var(--ink-3)', fontStyle:'italic', padding:'18px 0', fontFamily:'var(--serif)', fontSize:15}}>
            {isAnalyzing
              ? (liveStatus || 'Agent A 가 파일을 읽고 있습니다…')
              : '분석 시작을 눌러 요약과 키워드를 뽑아보세요. · Click Analyze to extract summary & concept-ladder keywords.'}
          </div>
        )}
        {hasAnalysis && blocks && blocks.length > 0 && blocks.map((b, i) => (
          <BlockCard key={b.id} file={file} block={b} isFirst={i===0}
                     onEditSummary={onEditSummary}
                     onDeleteKw={onDeleteKw}
                     onAddKw={onAddKw}
                     onOpenMutate={onOpenMutate}/>
        ))}
        {hasAnalysis && (!blocks || blocks.length === 0) && (
          // Legacy fallback — old analyses without blocks[]. Show flat view.
          <div style={{...analysisStyles.block, ...analysisStyles.blockFirst}}>
            <EditableSummary value={analysis.summary || ''} onSave={() => {}}/>
            <div style={analysisStyles.kwRow}>
              {(analysis.keywords || []).map((k, i) => (
                <KwChip key={i} kw={k} fileId={file.id}
                        onClick={() => onOpenMutate(null, k)}
                        onAdd={(e) => { e.stopPropagation(); onAddKw({...k, from: file.id}); }}/>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function AnalysisCol({ project, selectedFileId, analyses, onAddKw, onAnalyzeFile, analyzing, onPatchBlock }) {
  const files = (project.folders || []).flatMap(f => f.files || []);
  const ordered = [
    ...files.filter(f => f.id === selectedFileId),
    ...files.filter(f => f.id !== selectedFileId),
  ];
  const [view, setView] = React.useState('file');
  const [mutate, setMutate] = React.useState(null); // {file_id, block_id, kw}

  const onOpenMutate = (file_id) => (block_id, kw) => setMutate({file_id, block_id, kw});

  const onReplaceKw = async (s) => {
    // Replace the currently-opened kw in its source block with the suggested one.
    if (!mutate) return;
    const ana = analyses[mutate.file_id];
    const blk = ana && (ana.blocks || []).find(b => b.id === mutate.block_id);
    if (!blk) { onAddKw(s); return; }
    const next = blk.keywords.map(k =>
      (k.w === mutate.kw.w && k.ko === mutate.kw.ko)
        ? {w: s.w, ko: s.ko, why: s.why, tier: s.tier || k.tier, heat: k.heat}
        : k
    );
    await onPatchBlock(mutate.file_id, mutate.block_id, {keywords: next});
    setMutate(null);
  };

  return (
    <div style={analysisStyles.col}>
      <div style={analysisStyles.head}>
        <div>
          <div style={analysisStyles.eyebrow}>02 · Analysis</div>
          <div style={analysisStyles.title}>{project.name}</div>
          <div style={analysisStyles.sub}>
            Click a keyword to reshape it conversationally — click the summary to edit.
            Drag any chip onto the board, or ＋ to pin.
          </div>
        </div>
        <div style={analysisStyles.rightHead}>
          {['Per file','By theme','By quote'].map((t,i) => (
            <button key={t} onClick={() => setView(['file','theme','quote'][i])}
                    style={{...analysisStyles.pill, ...((['file','theme','quote'][i]===view) ? analysisStyles.pillActive : {})}}>
              {t}
            </button>
          ))}
        </div>
      </div>
      <div style={analysisStyles.scroll}>
        {ordered.length === 0 && (
          <div style={analysisStyles.emptyBlock}>
            No files yet. Upload into a folder from the dataroom →
          </div>
        )}
        {ordered.map(f => (
          <FileBlock key={f.id} file={f} analysis={analyses[f.id]}
                     onAddKw={onAddKw} onAnalyzeFile={onAnalyzeFile}
                     analyzing={!!analyzing[f.id]}
                     onPatchBlock={onPatchBlock}
                     onOpenMutate={onOpenMutate(f.id)}/>
        ))}
      </div>
      {mutate && (
        <KeywordMutatePopup
          file_id={mutate.file_id}
          block_id={mutate.block_id}
          kw={mutate.kw}
          onClose={() => setMutate(null)}
          onAddKw={onAddKw}
          onReplaceKw={onReplaceKw}/>
      )}
    </div>
  );
}

window.AnalysisCol = AnalysisCol;
