// Column 3 — Board. Drop zone for selected keywords + brainstorm (omni LLM) + final section.
const boardStyles = {
  col: { background:'linear-gradient(180deg, var(--paper) 0%, var(--paper-2) 100%)', display:'flex', flexDirection:'column', minHeight:0 },
  head: { padding:'16px 24px 14px 24px', borderBottom:'1px solid var(--line)' },
  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 },

  scroll: { overflowY:'auto', flex:1, padding:'18px 22px 32px 22px' },

  section: { marginBottom:22 },
  sectionHead: { display:'flex', alignItems:'baseline', justifyContent:'space-between', marginBottom:10 },
  sectionTitle: { fontFamily:'var(--mono)', fontSize:10, letterSpacing:'.14em', color:'var(--ink-3)', textTransform:'uppercase' },
  sectionCount: { fontFamily:'var(--mono)', fontSize:10, color:'var(--ink-4)' },

  dropzone: (hot) => ({
    minHeight: 60, border: hot ? '1.5px dashed var(--accent)' : '1.5px dashed var(--line-2)',
    borderRadius:10, padding:10, display:'flex', flexWrap:'wrap', gap:8, alignContent:'flex-start',
    background: hot ? 'rgba(244,81,28,.06)' : 'transparent', transition:'all .15s',
  }),

  pinned: (colorVar) => ({
    display:'inline-flex', alignItems:'stretch', borderRadius:8, overflow:'hidden',
    border:'1px solid var(--line-2)',
    background:'var(--paper)',
    boxShadow:'0 1px 0 rgba(0,0,0,.04)',
  }),
  pinnedAccent: (c) => ({
    width:4, background: c==='accent-2' ? 'var(--accent-2)' : c==='accent-3' ? 'var(--accent-3)' : c==='ink' ? 'var(--ink)' : 'var(--accent)',
  }),
  pinnedBody: { padding:'8px 10px 8px 10px', minWidth:120, maxWidth:260 },
  pinnedRow: { display:'flex', alignItems:'baseline', gap:8, flexWrap:'wrap', rowGap:2 },
  pinnedEn: { fontSize:13, color:'var(--ink)', fontWeight:500, whiteSpace:'nowrap' },
  pinnedKo: { fontFamily:'var(--display)', fontSize:13.5, color:'var(--ink-2)', fontStyle:'italic', whiteSpace:'nowrap' },
  pinnedNote: { fontSize:10.5, color:'var(--ink-3)', marginTop:4, lineHeight:1.4 },
  pinnedSrc: { fontFamily:'var(--mono)', fontSize:9, color:'var(--ink-4)', marginTop:3, letterSpacing:'.05em' },
  pinnedClose: { padding:'0 8px', borderLeft:'1px solid var(--line)', display:'flex', alignItems:'center', color:'var(--ink-4)', cursor:'pointer' },

  emptyDrop: { fontSize:11.5, color:'var(--ink-4)', fontStyle:'italic', padding:'8px 4px' },

  brainBox: {
    border:'1px solid var(--line)', borderRadius:10, background:'var(--paper)',
    padding:0, overflow:'hidden', marginBottom:18,
  },
  brainHead: { padding:'10px 14px', borderBottom:'1px solid var(--line)', display:'flex', justifyContent:'space-between', alignItems:'center', background:'var(--paper-2)' },
  brainTitle: { fontFamily:'var(--serif)', fontSize:14, color:'var(--ink)' },
  modelPick: { display:'flex', gap:4, fontFamily:'var(--mono)', fontSize:9.5, letterSpacing:'.08em' },
  modelChip: (a) => ({
    padding:'3px 7px', borderRadius:4,
    border:'1px solid var(--line-2)',
    background: a ? 'var(--ink)' : 'var(--paper)',
    color: a ? 'var(--paper)' : 'var(--ink-3)', cursor:'pointer',
    textTransform:'uppercase',
  }),
  messages: { padding:'12px 14px', maxHeight:260, overflowY:'auto', display:'flex', flexDirection:'column', gap:10 },
  msg: (me) => ({
    maxWidth:'86%', alignSelf: me ? 'flex-end' : 'flex-start',
    padding:'8px 11px', fontSize:12.5, lineHeight:1.5,
    borderRadius:10,
    background: me ? 'var(--ink)' : 'var(--paper-2)',
    color: me ? 'var(--paper)' : 'var(--ink)',
    border: me ? 'none' : '1px solid var(--line)',
  }),
  msgKw: { display:'flex', flexWrap:'wrap', gap:6, marginTop:8 },
  ghostKw: {
    display:'inline-flex', alignItems:'center', gap:6, padding:'3px 8px', borderRadius:999,
    background:'var(--paper)', border:'1px dashed var(--line-2)', fontSize:11.5,
    cursor:'grab',
  },
  msgMeta: { fontFamily:'var(--mono)', fontSize:9, color:'var(--ink-4)', marginTop:4, letterSpacing:'.06em' },

  composer: { display:'flex', gap:8, padding:'10px 12px', borderTop:'1px solid var(--line)', background:'var(--paper-2)', alignItems:'center' },
  composerInput: { flex:1, border:'1px solid var(--line-2)', borderRadius:8, padding:'8px 10px', fontSize:12.5, background:'var(--paper)', outline:'none', fontFamily:'var(--sans)' },
  sendBtn: { height:32, padding:'0 12px', border:'1px solid var(--ink)', borderRadius:8, background:'var(--ink)', color:'var(--paper)', fontSize:12, cursor:'pointer', display:'inline-flex', alignItems:'center', gap:5 },

  finalBox: {
    background:'var(--ink)', color:'var(--paper)', borderRadius:12, padding:'16px 18px 18px 18px',
    marginTop:6, boxShadow:'0 10px 30px rgba(0,0,0,.12)',
    backgroundImage:'radial-gradient(circle at 100% 0%, rgba(244,81,28,.35), transparent 55%)',
  },
  finalHead: { display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:10 },
  finalTitle: { fontFamily:'var(--serif)', fontSize:22, color:'var(--paper)', letterSpacing:'-.01em' },
  finalTag: { fontFamily:'var(--mono)', fontSize:9.5, letterSpacing:'.14em', color:'rgba(255,255,255,.55)', textTransform:'uppercase' },
  finalList: { display:'flex', flexWrap:'wrap', gap:8, marginTop:12, marginBottom:14 },
  finalKw: { padding:'6px 10px', borderRadius:999, border:'1px solid rgba(255,255,255,.25)', fontSize:12.5, display:'inline-flex', alignItems:'center', gap:8 },
  finalKo: { fontFamily:'var(--display)', fontStyle:'italic', color:'rgba(255,255,255,.7)' },
  finalActions: { display:'flex', gap:8, marginTop:4 },
  ghostBtn: { height:30, padding:'0 12px', borderRadius:999, border:'1px solid rgba(255,255,255,.3)', background:'transparent', color:'var(--paper)', fontSize:11.5, cursor:'pointer' },
  primaryBtn: { height:30, padding:'0 14px', borderRadius:999, border:'1px solid var(--accent)', background:'var(--accent)', color:'var(--paper)', fontSize:11.5, cursor:'pointer', fontWeight:500 },
};

function PinnedChip({ k, onRemove, fileNameById }) {
  const fromLabel = k.from && fileNameById && fileNameById[k.from] ? fileNameById[k.from] : k.from;
  return (
    <div style={boardStyles.pinned(k.color)}>
      <div style={boardStyles.pinnedAccent(k.color)}/>
      <div style={boardStyles.pinnedBody}>
        <div style={boardStyles.pinnedRow}>
          <span style={boardStyles.pinnedEn}>{k.w}</span>
          <span style={boardStyles.pinnedKo}>{k.ko}</span>
        </div>
        {(k.note || k.why) && <div style={boardStyles.pinnedNote}>{k.note || k.why}</div>}
        {fromLabel && <div style={boardStyles.pinnedSrc}>from · {fromLabel}</div>}
      </div>
      <div style={boardStyles.pinnedClose} onClick={() => onRemove(k)}><Ico.x/></div>
    </div>
  );
}

function WatchingChip({ k, onPromote, onRemove }) {
  return (
    <div style={{display:'inline-flex', alignItems:'center', gap:6, padding:'4px 4px 4px 10px', borderRadius:999, border:'1px solid var(--line-2)', background:'var(--paper)', fontSize:12, whiteSpace:'nowrap'}}>
      <span style={{whiteSpace:'nowrap'}}>{k.w}</span>
      <span style={{fontFamily:'var(--display)', fontStyle:'italic', color:'var(--ink-3)', whiteSpace:'nowrap'}}>{k.ko}</span>
      <button onClick={() => onPromote(k)} title="Promote" style={{width:20, height:20, border:'1px solid var(--line-2)', borderRadius:'50%', background:'var(--paper-2)', display:'inline-flex', alignItems:'center', justifyContent:'center', cursor:'pointer', color:'var(--accent)'}}>
        <Ico.arrow s={9}/>
      </button>
      <button onClick={() => onRemove(k)} style={{width:20, height:20, border:'1px solid var(--line-2)', borderRadius:'50%', background:'var(--paper)', display:'inline-flex', alignItems:'center', justifyContent:'center', cursor:'pointer', color:'var(--ink-4)'}}>
        <Ico.x s={8}/>
      </button>
    </div>
  );
}

const BRAINSTORM_INTRO = {
  me:false, model:'Palette · omni',
  text:"왼쪽에서 키워드를 모아두면 여기서 함께 이야기해요. 상투어는 부숴 드리고, 숨은 긴장감도 찾아낼게요.",
  kws:[],
};

const refStyles = {
  wrap: { border:'1px solid var(--line)', borderRadius:10, background:'var(--paper)', overflow:'hidden' },
  bar: { display:'flex', gap:8, padding:'10px 12px', borderBottom:'1px solid var(--line)', alignItems:'center', background:'var(--paper-2)', flexWrap:'wrap' },
  input: { flex:1, minWidth:140, border:'1px solid var(--line-2)', borderRadius:999, padding:'6px 12px', fontSize:12, fontFamily:'var(--sans)', background:'var(--paper)', outline:'none' },
  siteChip: (a) => ({
    padding:'4px 9px', borderRadius:999, border:'1px solid var(--line-2)',
    background: a ? 'var(--ink)' : 'var(--paper)', color: a ? 'var(--paper)' : 'var(--ink-3)',
    fontSize:10.5, fontFamily:'var(--mono)', letterSpacing:'.08em', textTransform:'uppercase', cursor:'pointer',
  }),
  searchBtn: { height:28, padding:'0 14px', borderRadius:999, border:'1px solid var(--accent)', background:'var(--accent)', color:'var(--paper)', fontSize:12, cursor:'pointer', fontFamily:'var(--sans)' },
  grid: { display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(150px, 1fr))', gridAutoRows:'1fr', gap:10, padding:'12px', alignItems:'stretch' },
  card: { border:'1px solid var(--line)', borderRadius:8, overflow:'hidden', background:'var(--paper)', display:'flex', flexDirection:'column', cursor:'pointer', transition:'transform .12s, box-shadow .12s', height:'100%' },
  cardBody: { display:'flex', flexDirection:'column', flex:1, minHeight:0 },
  thumb: { width:'100%', aspectRatio:'16/9', objectFit:'cover', background:'var(--paper-3)', display:'block' },
  meta: { padding:'6px 8px 4px 8px', fontSize:11, color:'var(--ink)', lineHeight:1.35, flex:1, display:'flex', flexDirection:'column', minHeight:0 },
  titleL: {
    display:'-webkit-box', WebkitLineClamp:2, WebkitBoxOrient:'vertical',
    overflow:'hidden', wordBreak:'break-word',
    minHeight:'calc(1.35em * 2)',          // always reserve 2 lines so cards align
  },
  siteTag: { fontFamily:'var(--mono)', fontSize:9, letterSpacing:'.1em', color:'var(--ink-4)', textTransform:'uppercase', marginTop:3, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' },
  actions: { display:'flex', justifyContent:'space-between', padding:'0 8px 8px 8px', alignItems:'center', marginTop:'auto' },
  iconBtn: { width:22, height:22, border:'1px solid var(--line-2)', borderRadius:'50%', display:'inline-flex', alignItems:'center', justifyContent:'center', cursor:'pointer', background:'var(--paper)', color:'var(--ink-3)' },
  empty: { padding:'28px 16px', textAlign:'center', color:'var(--ink-4)', fontFamily:'var(--serif)', fontStyle:'italic', fontSize:14 },
  savedStrip: { display:'flex', gap:8, overflowX:'auto', padding:'10px 12px', borderTop:'1px solid var(--line)' },
  savedCard: { width:150, minWidth:150, maxWidth:150, border:'1px solid var(--line-2)', borderRadius:6, overflow:'hidden', position:'relative', cursor:'pointer', flexShrink:0, display:'flex', flexDirection:'column' },
  savedThumb: { width:150, aspectRatio:'16/9', objectFit:'cover', display:'block', background:'var(--paper-3)' },
  savedTitle: { padding:'4px 6px 6px 6px', fontSize:10.5, lineHeight:1.3, color:'var(--ink)', display:'-webkit-box', WebkitLineClamp:2, WebkitBoxOrient:'vertical', overflow:'hidden', wordBreak:'break-word', minHeight:'calc(1.3em * 2 + 10px)' },
  savedRemove: { position:'absolute', top:4, right:4, width:18, height:18, borderRadius:'50%', border:'none', background:'rgba(0,0,0,.55)', color:'white', fontSize:10, cursor:'pointer', display:'inline-flex', alignItems:'center', justifyContent:'center' },
};

const SITE_GROUPS = [
  { id: 'youtube_kr',       label: 'YouTube KR', kind: 'video' },
  { id: 'youtube_jp',       label: 'YouTube JP', kind: 'video' },
  { id: 'tvcf',             label: 'TVCF', kind: 'video' },
  { id: 'ads_of_the_world', label: 'Ads of the World · 글로벌 광고', kind: 'video' },
  { id: 'naver_images',     label: 'Naver · 네이버', kind: 'image' },
  { id: 'google_images_kr', label: 'Google KR', kind: 'image' },
  { id: 'google_images_jp', label: 'Google JP', kind: 'image' },
  { id: 'pinterest',        label: 'Pinterest', kind: 'image' },
];

const SITE_LABEL = SITE_GROUPS.reduce((a, s) => (a[s.id] = s.label, a), {});

function ReferencesPanel({ project, boardPinned, savedRefs, onSearched, onSaveRef, onRemoveRef, onDownloadRef }) {
  const folders = (project && project.folders) || [];
  // Prefer a folder that has "reference" or "레퍼런스" in its name.
  const defaultFolderId = React.useMemo(() => {
    const hit = folders.find(f => /reference|레퍼런스/i.test(f.name || ''));
    return (hit || folders[0] || {}).id;
  }, [folders]);
  const [targetFolderId, setTargetFolderId] = React.useState(defaultFolderId);
  const [downloading, setDownloading] = React.useState({});
  // Keep targetFolderId valid for the current project: if it isn't one of
  // this project's folders (because we switched projects), snap to default.
  React.useEffect(() => {
    const valid = folders.some(f => f.id === targetFolderId);
    if (!valid && defaultFolderId) setTargetFolderId(defaultFolderId);
  }, [defaultFolderId, folders, targetFolderId]);

  const triggerDownload = async (cand, rid) => {
    if (!onDownloadRef) { console.warn('onDownloadRef not wired'); return; }
    const key = rid || cand.url;
    setDownloading(d => ({...d, [key]: true}));
    try {
      await onDownloadRef({ ref_id: rid, candidate: cand, folder_id: targetFolderId });
    } catch(e) {
      console.error('download failed', e);
    } finally {
      setDownloading(d => { const x={...d}; delete x[key]; return x; });
    }
  };

  const [query, setQuery] = React.useState('');
  const [sites, setSites] = React.useState(() =>
    SITE_GROUPS.reduce((a, s) => (a[s.id] = true, a), {})
  );
  const [busy, setBusy] = React.useState(false);
  const [results, setResults] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [tab, setTab] = React.useState('all'); // 'all' | 'videos' | 'images'

  // Clear search state whenever we switch to a different project so the
  // previous project's results don't linger on screen.
  React.useEffect(() => {
    setResults(null);
    setError(null);
    setBusy(false);
    setTab('all');
    setQuery('');
  }, [project && project.id]);

  const kwList = React.useMemo(() => boardPinned.map(k => k.ko || k.w).filter(Boolean), [boardPinned]);

  const toggleSite = (id) => setSites(s => ({...s, [id]: !s[id]}));

  const runSearch = async () => {
    setError(null); setBusy(true);
    try {
      const chosen = Object.keys(sites).filter(s => sites[s]);
      const res = await API.searchReferences(project.id, {
        natural_language_query: query.trim() || null,
        keywords: kwList,
        sites: chosen.length ? chosen : SITE_GROUPS.map(s => s.id),
        target_count: 24,
        per_site_max: 10,
      });
      setResults(res.candidates || []);
      onSearched && onSearched(res.candidates || []);
    } catch(e) {
      setError('레퍼런스 찾기 실패 — ' + e.message);
    } finally { setBusy(false); }
  };

  const filtered = React.useMemo(() => {
    if (!results) return null;
    if (tab === 'videos') return results.filter(c => c.kind !== 'image');
    if (tab === 'images') return results.filter(c => c.kind === 'image');
    return results;
  }, [results, tab]);

  const tabStyle = (a) => ({
    padding:'4px 10px', borderRadius:999, border:'1px solid ' + (a ? 'var(--ink)' : 'var(--line-2)'),
    background: a ? 'var(--ink)' : 'var(--paper)', color: a ? 'var(--paper)' : 'var(--ink-2)',
    fontSize:11, fontFamily:'var(--sans)', cursor:'pointer',
  });

  const openCard = (c) => window.open(c.url, '_blank', 'noopener,noreferrer');

  return (
    <div style={refStyles.wrap}>
      <div style={refStyles.bar}>
        <input style={refStyles.input}
               placeholder={kwList.length ? `자유 검색어 (비우면 모아둔 키워드로 검색)` : '키워드 또는 자유 문장으로 검색'}
               value={query}
               onChange={e => setQuery(e.target.value)}
               onKeyDown={e => { if (e.key==='Enter') runSearch(); }}/>
        <button style={refStyles.searchBtn} onClick={runSearch} disabled={busy || (!query.trim() && !kwList.length)}>
          {busy ? '찾는 중…' : '레퍼런스 찾기'}
        </button>
      </div>

      <div style={{display:'flex', gap:6, flexWrap:'wrap', padding:'8px 12px', borderBottom:'1px solid var(--line)'}}>
        {SITE_GROUPS.map(s => (
          <span key={s.id} style={refStyles.siteChip(sites[s.id])} onClick={() => toggleSite(s.id)}
                title={s.kind === 'image' ? 'image source' : 'video source'}>
            {s.kind === 'image' ? '🖼 ' : '▶ '}{s.label}
          </span>
        ))}
      </div>

      <div style={{display:'flex', gap:8, alignItems:'center', padding:'6px 12px 4px 12px', fontSize:11, color:'var(--ink-3)', fontFamily:'var(--mono)', letterSpacing:'.06em'}}>
        <span>자료실 폴더 ·</span>
        <select value={targetFolderId || ''} onChange={e => setTargetFolderId(e.target.value)}
                style={{fontFamily:'var(--sans)', fontSize:12, border:'1px solid var(--line-2)', borderRadius:6, padding:'3px 6px', background:'var(--paper)', color:'var(--ink)'}}>
          {folders.map(f => <option key={f.id} value={f.id}>{f.name}</option>)}
        </select>
        <span style={{color:'var(--ink-4)'}}>· 다운로드 버튼 누르면 이 폴더로 저장</span>
      </div>

      {results && results.length > 0 && (
        <div style={{display:'flex', gap:6, padding:'10px 12px 4px 12px'}}>
          <button style={tabStyle(tab==='all')} onClick={() => setTab('all')}>
            전체 · {results.length}
          </button>
          <button style={tabStyle(tab==='videos')} onClick={() => setTab('videos')}>
            ▶ 영상 · {results.filter(c => c.kind !== 'image').length}
          </button>
          <button style={tabStyle(tab==='images')} onClick={() => setTab('images')}>
            🖼 이미지 · {results.filter(c => c.kind === 'image').length}
          </button>
        </div>
      )}

      {error && <div style={{padding:'10px 14px', color:'var(--accent)', fontSize:12}}>{error}</div>}

      {results === null && !busy && (
        <div style={{...refStyles.empty, padding:'22px 16px 26px 16px'}}>
          <div style={{fontSize:28, marginBottom:6}}>🎬 🖼</div>
          <div style={{color:'var(--ink-2)', fontSize:14, marginBottom:4}}>
            {kwList.length
              ? `모아둔 ${kwList.length}개 키워드로 YouTube · TVCF · Naver · Google · Pinterest 한 번에 검색`
              : '키워드를 모아둔 뒤 ▶ 레퍼런스 찾기 버튼을 누르면 영상과 이미지가 여기 나와요.'}
          </div>
          <div style={{fontFamily:'var(--mono)', fontSize:10, color:'var(--ink-4)', letterSpacing:'.08em', marginTop:4}}>
            Korean + Japanese bias · click to open · ＋ to save
          </div>
        </div>
      )}

      {results !== null && results.length === 0 && !busy && (
        <div style={refStyles.empty}>결과 없음 — 다른 키워드로 시도해볼까요?</div>
      )}

      {filtered && filtered.length > 0 && (
        <div style={refStyles.grid}>
          {filtered.map((c, i) => {
            const isImage = c.kind === 'image';
            const thumbStyle = isImage
              ? { ...refStyles.thumb, aspectRatio:'1 / 1' }
              : refStyles.thumb;
            return (
              <div key={c.url + i} style={refStyles.card}
                   onMouseEnter={e => { e.currentTarget.style.transform='translateY(-1px)'; e.currentTarget.style.boxShadow='0 8px 18px rgba(0,0,0,.08)'; }}
                   onMouseLeave={e => { e.currentTarget.style.transform='none'; e.currentTarget.style.boxShadow='none'; }}>
                <div onClick={() => openCard(c)} title={c.title}
                     style={{position:'relative', display:'flex', flexDirection:'column', flex:1, minHeight:0}}>
                  {c.thumbnail
                    ? <img src={c.thumbnail} alt="" style={thumbStyle} loading="lazy"
                           onError={e => { e.currentTarget.style.display='none'; }}/>
                    : <div style={{...thumbStyle, display:'flex', alignItems:'center', justifyContent:'center', fontFamily:'var(--serif)', fontStyle:'italic', color:'var(--ink-4)'}}>no thumb</div>}
                  <span style={{
                    position:'absolute', top:6, left:6,
                    padding:'2px 7px', borderRadius:999, fontSize:9.5,
                    fontFamily:'var(--mono)', letterSpacing:'.08em', textTransform:'uppercase',
                    background:'rgba(0,0,0,.62)', color:'#fff',
                  }}>{isImage ? '🖼 ' : '▶ '}{SITE_LABEL[c.site] || c.site}</span>
                  <div style={refStyles.meta}>
                    <div style={refStyles.titleL}>{c.title || '(제목 없음)'}</div>
                    <div style={refStyles.siteTag}>{SITE_LABEL[c.site] || c.site}{c.duration ? ` · ${Math.round(c.duration/60)}분` : ''}</div>
                  </div>
                </div>
                <div style={refStyles.actions}>
                  <span style={{fontFamily:'var(--mono)', fontSize:9.5, color:'var(--ink-4)'}}>
                    {c.matched_keywords && c.matched_keywords.length ? `↳ ${c.matched_keywords.slice(0,2).join(', ')}` : ''}
                  </span>
                  <div style={{display:'flex', gap:4}}>
                    <div title="자료실로 다운로드" style={{...refStyles.iconBtn, opacity: downloading[c.url] ? .4 : 1}}
                         onClick={e => {e.stopPropagation(); if (!downloading[c.url]) triggerDownload(c, null);}}>
                      {downloading[c.url] ? '…' : '↓'}
                    </div>
                    <div title="저장 (pin)" style={refStyles.iconBtn} onClick={e => {e.stopPropagation(); onSaveRef(c);}}>
                      <Ico.plus s={10}/>
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}

      {savedRefs && savedRefs.length > 0 && (
        <div style={refStyles.savedStrip}>
          {savedRefs.map(s => (
            <div key={s.id} style={refStyles.savedCard} onClick={() => window.open(s.url, '_blank', 'noopener,noreferrer')} title={s.title}>
              {s.thumbnail && <img src={s.thumbnail} alt="" style={refStyles.savedThumb} loading="lazy"/>}
              <div style={refStyles.savedTitle}>{s.title}</div>
              <button style={{...refStyles.savedRemove, right:26, background:'rgba(244,81,28,.92)', opacity: downloading[s.id] ? .5 : 1}}
                      onClick={e => { e.stopPropagation(); if (!downloading[s.id]) triggerDownload(s, s.id); }}
                      title="자료실로 다운로드">{downloading[s.id] ? '…' : '↓'}</button>
              <button style={refStyles.savedRemove}
                      onClick={e => { e.stopPropagation(); onRemoveRef(s.id); }}
                      title="삭제">×</button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function BoardCol({ project, board, setBoard, messages, setMessages, distilled, onDistill, onExport, onExportDeck, fileNameById, onAddKw, savedRefs, onSaveRef, onRemoveRef, onDownloadRef, onSearchedRefs, step }) {
  const [hot, setHot] = React.useState(null); // 'pinned' | 'watching' | null
  const [model, setModel] = React.useState('omni');
  const [input, setInput] = React.useState('');
  const msgs = (messages && messages.length) ? messages : [BRAINSTORM_INTRO];

  const onDrop = (zone) => (e) => {
    e.preventDefault();
    setHot(null);
    const raw = e.dataTransfer.getData('application/kw');
    if (!raw) return;
    const k = JSON.parse(raw);
    if (zone === 'pinned') {
      setBoard({...board, pinned:[...board.pinned, {...k, color: ['accent','accent-2','accent-3','ink'][board.pinned.length%4]}]});
    } else {
      setBoard({...board, watching:[...board.watching, k]});
    }
  };
  const allowDrop = (zone) => (e) => { e.preventDefault(); setHot(zone); };
  const leave = () => setHot(null);

  const removePinned = (k) => setBoard({...board, pinned: board.pinned.filter(x => x.w !== k.w)});
  const removeWatch = (k) => setBoard({...board, watching: board.watching.filter(x => x.w !== k.w)});
  const promote = (k) => setBoard({
    ...board,
    pinned: [...board.pinned, {...k, color:'accent'}],
    watching: board.watching.filter(x => x.w !== k.w)
  });

  const [sending, setSending] = React.useState(false);
  const send = async () => {
    if (!input.trim() || sending) return;
    const base = (messages && messages.length) ? messages : [BRAINSTORM_INTRO];
    const withUser = [...base, { me:true, text: input }];
    setMessages(withUser);
    const prompt = input;
    setInput('');
    setSending(true);
    try {
      const res = await API.brainstorm(project.id, model, withUser);
      const reply = { me:false, model: res.model, text: res.reply, kws: res.kws || [] };
      setMessages([...withUser, reply]);
    } catch(e) {
      setMessages([...withUser, { me:false, model:'(error)', text:'Brainstorm failed: ' + e.message, kws:[] }]);
    } finally {
      setSending(false);
    }
  };

  const resetBrainstorm = () => {
    if (!confirm('Clear the brainstorm history for this project?')) return;
    setMessages([]);
  };

  // Project-scoped sub-tabs: Board (default), References (fullscreen), Final.
  const [tab, setTab] = React.useState('board');
  React.useEffect(() => { setTab('board'); }, [project && project.id]);

  // Sync with the top-bar step indicator: step 4 = references, step 5 = final.
  // (steps 0–2 are the 3 columns, step 3 = Board column root, so map 4/5 here.)
  React.useEffect(() => {
    if (step === 4) setTab('references');
    else if (step === 5) setTab('final');
    else if (step === 3) setTab('board');
  }, [step]);
  const TABS = [
    { id:'board',      label:'🎨 보드 · Board' },
    { id:'references', label:'🎬 레퍼런스 · References' },
    { id:'final',      label:'📝 최종 · Final deck' },
  ];
  const tabBtn = (active) => ({
    padding:'6px 14px', borderRadius:999,
    border:'1px solid ' + (active ? 'var(--ink)' : 'var(--line-2)'),
    background: active ? 'var(--ink)' : 'var(--paper)',
    color: active ? 'var(--paper)' : 'var(--ink-2)',
    fontSize:11.5, fontFamily:'var(--sans)', cursor:'pointer',
  });

  // Final-deck subtree, reused by the stacked + dedicated views.
  const finalDeck = (
    <div style={boardStyles.section} id="board-pinned">
      <div style={boardStyles.sectionHead}>
        <span style={boardStyles.sectionTitle}>04 · 최종 키워드 · Final</span>
        <span style={boardStyles.sectionCount}>프로젝트에 저장됨 · v0.4</span>
      </div>
      <div style={boardStyles.finalBox}>
        <div style={boardStyles.finalHead}>
          <div style={boardStyles.finalTitle}>{project.name}</div>
          <div style={boardStyles.finalTag}>{board.pinned.length ? `확정 · ${board.pinned.length}개` : '작성 중'}</div>
        </div>
        {project.brief && (
          <div style={{fontFamily:'var(--display)', fontStyle:'italic', fontSize:14, color:'rgba(255,255,255,.75)', lineHeight:1.45, maxWidth:420}}>
            “{project.brief}”
          </div>
        )}
        <div style={boardStyles.finalList}>
          {board.pinned.length === 0 && (
            <div style={{fontSize:12, color:'rgba(255,255,255,.5)', fontStyle:'italic'}}>
              위에서 키워드를 모아두면 여기 최종본으로 굳어요.
            </div>
          )}
          {board.pinned.map((k,i) => (
            <div key={i} style={{display:'flex', justifyContent:'space-between', padding:'6px 0', borderBottom:'1px solid rgba(255,255,255,.08)'}}>
              <div><b style={{color:'#fff'}}>{k.w}</b> <i style={{color:'rgba(255,255,255,.7)', fontStyle:'italic'}}>· {k.ko}</i></div>
              {k.note && <span style={{fontSize:11, color:'rgba(255,255,255,.55)'}}>— {k.note}</span>}
            </div>
          ))}
        </div>
        <div style={{display:'flex', gap:8, marginTop:16, flexWrap:'wrap'}}>
          <button style={boardStyles.primaryBtn} onClick={onExportDeck || onExport} disabled={!board.pinned.length}>
            🖨 HTML 덱 열기 · Pitch deck
          </button>
          {onExport && onExportDeck && (
            <button style={{...boardStyles.primaryBtn, background:'transparent', color:'var(--paper)', border:'1px solid rgba(255,255,255,.2)'}} onClick={onExport} disabled={!board.pinned.length}>
              📥 Markdown 다운로드
            </button>
          )}
        </div>
      </div>
    </div>
  );

  const referencesPanelNode = (
    <ReferencesPanel project={project}
                     boardPinned={board.pinned}
                     savedRefs={savedRefs || []}
                     onSearched={onSearchedRefs}
                     onSaveRef={onSaveRef}
                     onRemoveRef={onRemoveRef}
                     onDownloadRef={onDownloadRef}/>
  );

  return (
    <div style={boardStyles.col}>
      <div style={boardStyles.head}>
        <div style={boardStyles.eyebrow}>03 · 조합 · Board</div>
        <div style={boardStyles.title}>
          {tab === 'references' ? '레퍼런스 영상·이미지 · References'
           : tab === 'final'    ? '최종 키워드 · Final deck'
           :                       '키워드 정리 · Keyword board'}
        </div>
        <div style={boardStyles.sub}>
          {tab === 'references' ? '검색 · 다운로드 · 자료실로 바로 저장'
           : tab === 'final'    ? '모아둔 키워드로 덱을 출력하고 공유하세요.'
           :                       '키워드를 여기 끌어 모으고, 레퍼런스를 붙이고, 최종본을 내보내세요.'}
        </div>
        <div style={{display:'flex', gap:6, marginTop:12, flexWrap:'wrap'}}>
          {TABS.map(t => (
            <button key={t.id} style={tabBtn(tab === t.id)} onClick={() => setTab(t.id)}>{t.label}</button>
          ))}
        </div>
      </div>

      {tab === 'references' ? (
        <div style={{...boardStyles.scroll, padding:'16px 20px 40px 20px'}}>
          {referencesPanelNode}
        </div>
      ) : tab === 'final' ? (
        <div style={{...boardStyles.scroll, padding:'16px 20px 40px 20px'}}>
          {finalDeck}
        </div>
      ) : (
      <div style={boardStyles.scroll}>
        <div style={boardStyles.section}>
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}>● 모아둔 키워드 · Pinned</span>
            <span style={boardStyles.sectionCount}>{board.pinned.length} / 6 권장</span>
          </div>
          <div style={boardStyles.dropzone(hot==='pinned')}
               onDragOver={allowDrop('pinned')} onDragLeave={leave} onDrop={onDrop('pinned')}>
            {board.pinned.length === 0 && <div style={boardStyles.emptyDrop}>여기로 키워드를 끌어오거나, ＋ 를 눌러 모아두세요.</div>}
            {board.pinned.map((k,i) => <PinnedChip key={i} k={k} onRemove={removePinned} fileNameById={fileNameById}/>)}
          </div>
        </div>

        <div style={boardStyles.section}>
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}>○ 관심 · Watching</span>
            <span style={boardStyles.sectionCount}>{board.watching.length}</span>
          </div>
          <div style={boardStyles.dropzone(hot==='watching')}
               onDragOver={allowDrop('watching')} onDragLeave={leave} onDrop={onDrop('watching')}>
            {board.watching.length === 0 && <div style={boardStyles.emptyDrop}>당장은 아니지만 기억해두고 싶은 단어들.</div>}
            {board.watching.map((k,i) => <WatchingChip key={i} k={k} onPromote={promote} onRemove={removeWatch}/>)}
          </div>
        </div>

        <div style={boardStyles.section}>
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}>✦ 핵심 키워드 · Top words</span>
            <span style={{...boardStyles.sectionCount, cursor:'pointer', color:'var(--accent)'}} onClick={onDistill}>
              {distilled && distilled.length ? '새로고침' : '추출'}
            </span>
          </div>
          <div style={{display:'flex', flexWrap:'wrap', gap:6, padding:'4px 2px'}}>
            {(!distilled || !distilled.length) && (
              <div style={boardStyles.emptyDrop}>파일 분석이 끝나면 자동으로 여기에 쌓여요 · 수동 <em>추출</em> 도 가능.</div>
            )}
            {distilled && distilled.map((k,i) => (
              <div key={i}
                   draggable
                   onDragStart={(e) => {
                     e.dataTransfer.setData('application/kw', JSON.stringify(k));
                     e.dataTransfer.effectAllowed = 'copy';
                   }}
                   onClick={() => onAddKw && onAddKw(k)}
                   style={{...boardStyles.ghostKw, cursor:'grab'}}
                   title={(k.why||'') + (k.occurrences ? `  ·  seen ×${k.occurrences}` : '')}>
                <Ico.drag/>
                <span>{k.w}</span>
                <span style={{fontFamily:'var(--display)', fontStyle:'italic', color:'var(--ink-3)'}}>{k.ko}</span>
                {k.occurrences > 1 && <span style={{fontFamily:'var(--mono)', fontSize:9, color:'var(--ink-4)'}}>×{k.occurrences}</span>}
              </div>
            ))}
          </div>
        </div>

        <div style={boardStyles.section} id="board-references">
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}>▶ 레퍼런스 영상 · Video references</span>
            <span style={boardStyles.sectionCount}>{savedRefs ? `${savedRefs.length} 저장됨` : 'YouTube · TVCF · AoTW'}</span>
          </div>
          <ReferencesPanel project={project}
                           boardPinned={board.pinned}
                           savedRefs={savedRefs || []}
                           onSearched={onSearchedRefs}
                           onSaveRef={onSaveRef}
                           onRemoveRef={onRemoveRef}
                           onDownloadRef={onDownloadRef}/>
        </div>

        <div style={boardStyles.section}>
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}><Ico.sparkle s={10}/> 대화로 아이디어 · Brainstorm</span>
            {messages && messages.length > 0 && (
              <span style={{...boardStyles.sectionCount, cursor:'pointer'}} onClick={resetBrainstorm}>초기화</span>
            )}
          </div>
          <div style={boardStyles.brainBox}>
            <div style={boardStyles.brainHead}>
              <span style={boardStyles.brainTitle}>함께 이야기 나눠요</span>
              <div style={boardStyles.modelPick}>
                {[['omni','Omni'],['claude','Claude'],['gpt','GPT'],['gemini','Gemini']].map(([id,l]) => (
                  <span key={id} style={boardStyles.modelChip(model===id)} onClick={() => setModel(id)}>{l}</span>
                ))}
              </div>
            </div>
            <div style={boardStyles.messages}>
              {msgs.map((m,i) => (
                <div key={i} style={{display:'flex', flexDirection:'column'}}>
                  <div style={boardStyles.msg(m.me)}>
                    {m.text}
                    {m.kws && m.kws.length > 0 && (
                      <div style={boardStyles.msgKw}>
                        {m.kws.map((k,j) => (
                          <div key={j}
                               draggable
                               onDragStart={(e) => {
                                 e.dataTransfer.setData('application/kw', JSON.stringify(k));
                                 e.dataTransfer.effectAllowed = 'copy';
                               }}
                               style={boardStyles.ghostKw}
                               title={k.why}>
                            <Ico.drag/>
                            <span>{k.w}</span>
                            <span style={{fontFamily:'var(--display)', fontStyle:'italic', color:'var(--ink-3)'}}>{k.ko}</span>
                          </div>
                        ))}
                      </div>
                    )}
                  </div>
                  {!m.me && m.model && <div style={boardStyles.msgMeta}>{m.model}</div>}
                </div>
              ))}
            </div>
            <div style={boardStyles.composer}>
              <input style={boardStyles.composerInput}
                     value={input} onChange={e=>setInput(e.target.value)}
                     onKeyDown={e => { if (e.key==='Enter') send(); }}
                     disabled={sending}
                     placeholder={sending ? '생각하는 중…' : "대화로 묻기 — '조용함과 유능함 사이를 잇는 단어는?'"}/>
              <button style={boardStyles.sendBtn} onClick={send} disabled={sending}>
                <Ico.sparkle s={10}/> {sending ? '…' : '보내기'}
              </button>
            </div>
          </div>
        </div>

        <div style={boardStyles.section} id="board-pinned">
          <div style={boardStyles.sectionHead}>
            <span style={boardStyles.sectionTitle}>04 · 최종 키워드 · Final</span>
            <span style={boardStyles.sectionCount}>프로젝트에 저장됨 · v0.4</span>
          </div>
          <div style={boardStyles.finalBox}>
            <div style={boardStyles.finalHead}>
              <div style={boardStyles.finalTitle}>{project.name}</div>
              <div style={boardStyles.finalTag}>{board.pinned.length ? `확정 · ${board.pinned.length}개` : '작성 중'}</div>
            </div>
            {project.brief && (
              <div style={{fontFamily:'var(--display)', fontStyle:'italic', fontSize:14, color:'rgba(255,255,255,.75)', lineHeight:1.45, maxWidth:420}}>
                “{project.brief}”
              </div>
            )}
            <div style={boardStyles.finalList}>
              {board.pinned.length === 0 && (
                <div style={{fontSize:12, color:'rgba(255,255,255,.5)', fontStyle:'italic'}}>
                  위에서 키워드를 모아두면 여기 최종본으로 굳어요.
                </div>
              )}
              {board.pinned.map((k,i) => (
                <span key={i} style={boardStyles.finalKw}>
                  {k.w} <span style={boardStyles.finalKo}>{k.ko}</span>
                </span>
              ))}
            </div>
            <div style={boardStyles.finalActions}>
              <button style={boardStyles.primaryBtn} onClick={onExportDeck || onExport} disabled={!board.pinned.length}>
                저장 · 덱으로 내보내기
              </button>
              <button style={boardStyles.ghostBtn} onClick={onExport}>Markdown으로 받기</button>
            </div>
          </div>
        </div>
      </div>
      )}
    </div>
  );
}

window.BoardCol = BoardCol;
