// v2 REFERENCES stage — Board (search-first), Results (masonry), Final tab

const V2_REF_SOURCES_BUILTIN = [
  {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:'ad'},
  {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'},
];
// Persisted across stage mounts in the same session.
const V2_RECENT_SEARCHES_KEY = 'v2_ref_recent';

const V2RefStage = ({palette, projectId, pinnedIds, onDownload}) => {
  const map = {};
  (palette.blocks || []).forEach(b => (b.keywords || []).forEach(k => map[k.id] = {...k, blockId: b.id, fileId: b.file}));
  const pinned = pinnedIds.map(id => map[id]).filter(Boolean);
  const folderOptions = ((palette.files || []).reduce((acc,f) => {
    if (!acc.includes(f.folder)) acc.push(f.folder); return acc;
  }, []));

  const [tab, setTab] = React.useState('board');
  const [query, setQuery] = React.useState('');
  const [sources, setSources] = React.useState(V2_REF_SOURCES_BUILTIN);
  const [activeSources, setActiveSources] = React.useState(new Set(V2_REF_SOURCES_BUILTIN.map(s => s.id)));
  const [selectedKw, setSelectedKw] = React.useState(new Set(pinnedIds.slice(0, 6)));
  const [savedFolder, setSavedFolder] = React.useState(folderOptions[0] || '02 · References');
  const [results, setResults] = React.useState(null);
  const [searching, setSearching] = React.useState(false);
  const [searchPhase, setSearchPhase] = React.useState('');
  const [savedItems, setSavedItems] = React.useState(new Set());
  const [downloading, setDownloading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [recentSearches, setRecentSearches] = React.useState(() => {
    try { return JSON.parse(sessionStorage.getItem(V2_RECENT_SEARCHES_KEY) || '[]'); } catch(e) { return []; }
  });
  const [showAddSource, setShowAddSource] = React.useState(false);
  const [newSrcLabel, setNewSrcLabel] = React.useState('');
  const [newSrcKind, setNewSrcKind] = React.useState('web');

  const addCustomSource = () => {
    const label = newSrcLabel.trim();
    if (!label) return;
    const id = 'custom_' + label.toLowerCase().replace(/[^a-z0-9]+/g,'_');
    if (sources.some(s => s.id === id)) {
      setShowAddSource(false); setNewSrcLabel(''); return;
    }
    setSources([...sources, {id, label: label.toUpperCase(), kind: newSrcKind}]);
    setActiveSources(new Set([...activeSources, id]));
    setNewSrcLabel('');
    setShowAddSource(false);
  };

  const recordSearch = (q, kws, srcs, hits) => {
    const entry = {
      q: q || (kws.slice(0,3).join(' + ') || '(pinned)'),
      sources: srcs.map(s => sources.find(x => x.id === s)?.label || s).slice(0, 3).join(' · '),
      hits, when: new Date().toISOString(),
      query: q, kws, srcs,
    };
    const next = [entry, ...recentSearches.filter(r => r.q !== entry.q)].slice(0, 6);
    setRecentSearches(next);
    try { sessionStorage.setItem(V2_RECENT_SEARCHES_KEY, JSON.stringify(next)); } catch(e) {}
  };

  const replayRecent = (r) => {
    setQuery(r.query || '');
    if (Array.isArray(r.srcs)) setActiveSources(new Set(r.srcs));
    setTimeout(runSearch, 50);
  };

  const formatRecentWhen = (iso) => {
    const t = Date.now() - new Date(iso).getTime();
    if (t < 60000) return '방금';
    if (t < 3600000) return `${Math.floor(t/60000)}분 전`;
    if (t < 86400000) return `${Math.floor(t/3600000)}시간 전`;
    return `${Math.floor(t/86400000)}일 전`;
  };

  const toggleSource = (id) => setActiveSources(prev => { const n = new Set(prev); n.has(id)?n.delete(id):n.add(id); return n; });
  const toggleKw = (id) => setSelectedKw(prev => { const n = new Set(prev); n.has(id)?n.delete(id):n.add(id); return n; });

  const kwTexts = Array.from(selectedKw).map(id => map[id]).filter(Boolean).map(k => k.ko || k.en);
  const hasBrief = !!(palette.project?.brief || '').trim();
  // Backend auto-falls-back through pinned → distilled → analyzed keywords,
  // so the button enables as soon as there's *any* keyword surface available.
  const hasAnyAnalyzed = (palette.blocks || []).some(b => (b.keywords || []).length > 0);
  const hasDistilled = (palette.distilled || []).length > 0;
  const canSearch = !!(query.trim() || kwTexts.length || hasBrief || hasAnyAnalyzed || hasDistilled);

  const runSearch = async () => {
    setError(null); setSearching(true); setSearchPhase('쿼리 다듬는 중…');
    // Cycle through phase strings so the user knows it's still working.
    const phases = ['쿼리 다듬는 중…', '8개 소스 동시 검색 중…', '결과 정리 중…'];
    let phaseIdx = 0;
    const phaseTimer = setInterval(() => {
      phaseIdx = Math.min(phaseIdx + 1, phases.length - 1);
      setSearchPhase(phases[phaseIdx]);
    }, 4000);
    try {
      const res = await window.API.searchReferences(projectId, {
        natural_language_query: query.trim() || null,
        keywords: kwTexts,
        sites: Array.from(activeSources),
        target_count: 60, per_site_max: 8,
      });
      setResults(res.candidates || []);
      setTab('results');
      recordSearch(query.trim(), kwTexts, Array.from(activeSources), (res.candidates||[]).length);
    } catch(e) {
      const msg = String(e.message || '');
      const detail = (msg.match(/\{"detail":"([^"]+)"/) || [])[1];
      setError('레퍼런스 찾기 실패 — ' + (detail || msg));
    }
    finally {
      clearInterval(phaseTimer);
      setSearching(false); setSearchPhase('');
    }
  };

  const toggleSave = (url) => setSavedItems(prev => { const n = new Set(prev); n.has(url)?n.delete(url):n.add(url); return n; });

  const downloadAll = async () => {
    if (!results || !onDownload) return;
    setDownloading(true);
    try {
      for (const cand of results) {
        if (!savedItems.has(cand.url)) continue;
        try { await onDownload({candidate: cand, folder_id: null}); }
        catch(e) { console.error('download failed', e); }
      }
      setSavedItems(new Set());
    } finally { setDownloading(false); }
  };

  return (
    <div className="v2-app" style={{display:'flex',flexDirection:'column',height:'100%',background:'var(--bg)'}}>
      <div style={{padding:'18px 32px 0',borderBottom:'1px solid var(--rule)',background:'var(--paper)'}}>
        <div style={{display:'flex',alignItems:'baseline',gap:14,marginBottom:14}}>
          <h1 style={{margin:0,fontSize:22,fontWeight:700,letterSpacing:'-.02em'}}>레퍼런스 영상·이미지 · References</h1>
          <span className="mono" style={{fontSize:10.5,color:'var(--muted)'}}>검색 · 다운로드 · 자료실로 바로 저장</span>
        </div>
        <div style={{display:'flex',gap:6}}>
          {[{id:'board',label:'보드 · Board',icon:'pin'},{id:'results',label:'레퍼런스 · References',icon:'search'},{id:'final',label:'최종 · Final',icon:'doc'}].map(t => (
            <button key={t.id} onClick={()=>setTab(t.id)} style={{
              display:'inline-flex',alignItems:'center',gap:7,padding:'8px 14px',
              borderRadius:'10px 10px 0 0',border:'1px solid var(--rule)',
              borderBottom: tab===t.id ? '1px solid var(--paper)' : '1px solid var(--rule)',
              marginBottom:-1, background: tab===t.id ? 'var(--paper)' : 'var(--paper-2)',
              fontSize:12.5,fontWeight: tab===t.id?600:500,
              color: tab===t.id ? 'var(--ink)' : 'var(--sub)', cursor:'pointer', fontFamily:'inherit',
            }}>
              <V2Icon name={t.icon} size={11} color={tab===t.id?'var(--accent)':'var(--muted)'}/>
              {t.label}
            </button>
          ))}
        </div>
      </div>

      <div className="scroll" style={{flex:1,overflow:'auto',position:'relative'}}>
        {searching && (
          <V2LoadingOverlay msg={searchPhase || '검색 중…'}/>
        )}
        {tab === 'board' && (
          <div style={{padding:'30px 36px 60px',maxWidth:1100,margin:'0 auto'}}>
            <div style={{display:'flex',gap:0,background:'var(--paper)',borderRadius:12,
              border:'1px solid var(--rule)',boxShadow:'var(--shadow-2)',overflow:'hidden',marginBottom:18}}>
              <input value={query} onChange={e=>setQuery(e.target.value)}
                onKeyDown={e=>{ if (e.key==='Enter') runSearch(); }}
                placeholder="자유 검색어 (비우면 모아둔 키워드로 검색)"
                style={{flex:1,padding:'16px 20px',border:'none',outline:'none',
                  fontSize:14,fontFamily:'inherit',background:'transparent'}}/>
              <button onClick={runSearch} disabled={searching || !canSearch} style={{
                padding:'0 28px',
                background: (searching || !canSearch) ? 'rgba(0,103,71,.32)' : 'var(--primary)',
                color:'#fff',border:'none',
                fontSize:13.5,fontWeight:600,
                cursor: searching ? 'wait' : (canSearch ? 'pointer' : 'not-allowed'),
                display:'flex',alignItems:'center',gap:8}}>
                {searching ? <V2Spinner size="sm" color="#fff"/> : <V2Icon name="search" size={12} color="#fff"/>}
                {searching ? (searchPhase || '검색 중…') : '레퍼런스 찾기'}
              </button>
            </div>
            {!canSearch && !error && (
              <div style={{fontSize:11.5,color:'var(--muted)',marginBottom:10,padding:'8px 12px',
                background:'var(--paper-2)',border:'1px dashed var(--rule-2)',borderRadius:6}}>
                💡 시작하려면 — 위에 자유 검색어를 입력하거나, <b>02 · ANALYSIS</b>에서 키워드를 몇 개 핀하거나, Data 단계에서 프로젝트 브리프를 작성하세요.
              </div>
            )}
            {error && <div style={{color:'var(--accent)',fontSize:12,marginBottom:14}}>⚠ {error}</div>}

            <div style={{display:'flex',flexWrap:'wrap',gap:7,marginBottom:24,alignItems:'center'}}>
              {sources.map(s => {
                const on = activeSources.has(s.id);
                return (
                  <button key={s.id} onClick={()=>toggleSource(s.id)} style={{
                    display:'inline-flex',alignItems:'center',gap:6,padding:'7px 13px',
                    background: on ? 'var(--ink)' : 'var(--paper)',
                    color: on ? '#fff' : 'var(--ink)',
                    border:'1px solid', borderColor: on ? 'var(--ink)' : 'var(--rule)',
                    borderRadius:7, fontFamily:'var(--font-mono)', fontSize:10.5, fontWeight:600,
                    letterSpacing:'.04em', cursor:'pointer'
                  }}>{s.label}</button>
                );
              })}
              {!showAddSource ? (
                <button onClick={()=>setShowAddSource(true)} style={{
                  display:'inline-flex',alignItems:'center',gap:5,padding:'7px 12px',
                  background:'transparent',color:'var(--muted)',
                  border:'1px dashed var(--rule)',borderRadius:7,
                  fontFamily:'var(--font-mono)',fontSize:10.5,fontWeight:600,
                  letterSpacing:'.04em',cursor:'pointer'
                }}>+ 소스 추가</button>
              ) : (
                <div style={{display:'inline-flex',alignItems:'center',gap:6,
                  padding:'5px 6px 5px 10px',background:'var(--paper)',
                  border:'1px solid var(--primary)',borderRadius:7,boxShadow:'var(--shadow-2)'}}>
                  <input autoFocus value={newSrcLabel} onChange={e=>setNewSrcLabel(e.target.value)}
                    onKeyDown={e=>{ if(e.key==='Enter') addCustomSource(); if(e.key==='Escape') setShowAddSource(false); }}
                    placeholder="소스 이름 (예: Vimeo, Behance)"
                    style={{border:'none',outline:'none',background:'transparent',
                      fontFamily:'var(--font-mono)',fontSize:11,width:180,color:'var(--ink)'}}/>
                  <select value={newSrcKind} onChange={e=>setNewSrcKind(e.target.value)} style={{
                    padding:'4px 6px',border:'1px solid var(--rule)',borderRadius:5,
                    background:'var(--paper-2)',fontSize:10,
                    fontFamily:'var(--font-mono)',color:'var(--ink)',cursor:'pointer'}}>
                    <option value="web">web</option>
                    <option value="image">image</option>
                    <option value="video">video</option>
                    <option value="ad">ad</option>
                  </select>
                  <button onClick={addCustomSource} style={{
                    padding:'5px 10px',background:'var(--primary)',color:'#fff',border:'none',
                    borderRadius:5,fontSize:11,fontWeight:600,cursor:'pointer',
                    fontFamily:'var(--font-mono)'}}>추가</button>
                  <button onClick={()=>{setShowAddSource(false);setNewSrcLabel('');}} style={{
                    padding:'5px 8px',background:'transparent',color:'var(--muted)',
                    border:'none',borderRadius:5,fontSize:13,cursor:'pointer'}}>×</button>
                </div>
              )}
            </div>

            <div style={{display:'flex',alignItems:'center',gap:10,padding:'10px 4px',fontSize:12,color:'var(--sub)',marginBottom:30}}>
              <span style={{color:'var(--muted)'}}>자료실 폴더 ·</span>
              <select value={savedFolder} onChange={e=>setSavedFolder(e.target.value)} style={{
                padding:'5px 10px',background:'var(--paper)',border:'1px solid var(--rule)',
                borderRadius:6,fontSize:12,fontFamily:'inherit',cursor:'pointer'}}>
                {folderOptions.map(f => <option key={f}>{f}</option>)}
              </select>
              <span className="mono" style={{fontSize:10,color:'var(--muted)'}}>· 다운로드 버튼이 이 폴더로 저장</span>
            </div>

            <div style={{marginTop:36}}>
              <div style={{display:'flex',alignItems:'baseline',gap:10,marginBottom:12}}>
                <h3 style={{margin:0,fontSize:14,fontWeight:600}}>검색에 사용할 키워드</h3>
                <span className="mono" style={{fontSize:10,color:'var(--muted)'}}>
                  · {selectedKw.size} of {pinned.length} selected · 클릭으로 토글
                </span>
                <div style={{flex:1}}/>
                <button className="btn sm" onClick={()=>setSelectedKw(new Set(pinned.map(p=>p.id)))}>전체 선택</button>
                <button className="btn sm" onClick={()=>setSelectedKw(new Set())}>해제</button>
              </div>
              <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
                {pinned.map(kw => {
                  const on = selectedKw.has(kw.id);
                  return (
                    <button key={kw.id} onClick={()=>toggleKw(kw.id)} className={`chip t-${kw.type}`}
                      style={{cursor:'pointer', opacity: on ? 1 : .35, outline: on ? '2px solid var(--accent)' : 'none', outlineOffset:'-1px'}}>
                      <span className="chip-type">{kw.type.slice(0,3)}</span>
                      <span className="chip-en">{kw.en}</span>
                      <span className="chip-ko">·{kw.ko}</span>
                    </button>
                  );
                })}
                {pinned.length === 0 && <span style={{fontSize:12,color:'var(--muted)'}}>핀된 키워드가 없습니다 — Analysis 단계에서 핀해주세요.</span>}
              </div>
            </div>

            {recentSearches.length > 0 && (
              <div style={{marginTop:36}}>
                <div className="tag" style={{color:'var(--muted)',marginBottom:10}}>RECENT SEARCHES</div>
                {recentSearches.map((r, i) => (
                  <div key={i} onClick={()=>replayRecent(r)} style={{
                    display:'flex',alignItems:'center',gap:14,padding:'12px 16px',
                    background:'var(--paper)',border:'1px solid var(--rule)',borderRadius:8,
                    marginBottom:6,cursor:'pointer'}}>
                    <V2Icon name="refresh" size={12} color="var(--muted)"/>
                    <div style={{flex:1}}>
                      <div style={{fontSize:13,fontWeight:500}}>{r.q}</div>
                      <div className="mono" style={{fontSize:10,color:'var(--muted)',marginTop:3}}>
                        {r.sources} · {r.hits} hits
                      </div>
                    </div>
                    <span className="mono" style={{fontSize:10,color:'var(--muted)'}}>{formatRecentWhen(r.when)}</span>
                    <button className="btn sm" onClick={(e)=>{e.stopPropagation(); replayRecent(r);}}>다시 열기</button>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}

        {tab === 'results' && results && (
          <>
            <V2RefResults results={results} sources={sources.filter(s=>activeSources.has(s.id))}
              savedItems={savedItems} toggleSave={toggleSave} folder={savedFolder}
              onBack={()=>setTab('board')} onDownloadAll={downloadAll}
              downloading={downloading}/>
            {savedItems.size > 0 && (
              <div style={{position:'sticky',bottom:0,left:0,right:0,padding:'12px 24px',
                background:'rgba(255,255,255,.95)',backdropFilter:'blur(12px)',
                borderTop:'1px solid var(--rule)',display:'flex',alignItems:'center',gap:14,
                boxShadow:'0 -4px 12px rgba(0,0,0,.06)',zIndex:8}}>
                <V2Icon name="folder" size={14} color="var(--primary)"/>
                <div style={{flex:1}}>
                  <div style={{fontSize:13,fontWeight:600}}>{savedItems.size}개 저장 대기 중</div>
                  <div className="mono" style={{fontSize:10,color:'var(--muted)',marginTop:2}}>저장 폴더 · {savedFolder}</div>
                </div>
                <button className="btn sm" onClick={()=>setSavedItems(new Set())}>해제</button>
                <button className="btn sm primary" onClick={downloadAll} disabled={downloading}>
                  {downloading
                    ? <><V2Spinner size="sm" color="#fff"/> 저장 중…</>
                    : <><V2Icon name="folder" size={11} color="#fff"/> {savedFolder}로 저장 ({savedItems.size})</>}
                </button>
              </div>
            )}
          </>
        )}

        {tab === 'final' && (
          <div style={{padding:'40px 36px',maxWidth:900,margin:'0 auto'}}>
            <div style={{padding:'48px',background:'var(--paper)',border:'1px solid var(--rule)',borderRadius:14,textAlign:'center'}}>
              <div style={{fontSize:32,marginBottom:18}}>📑</div>
              <div style={{fontSize:15,color:'var(--sub)'}}>최종 덱은 <b style={{color:'var(--ink)'}}>05 · FINAL</b> 단계에서 빌드됩니다.</div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

const V2RefResults = ({results, sources, savedItems, toggleSave, folder, onBack, onDownloadAll, downloading}) => {
  const bySource = {};
  results.forEach(r => { (bySource[r.site] ||= []).push(r); });
  const flat = [];
  const queues = sources.map(s => (bySource[s.id] || []).map(it => ({...it, _source: s})));
  let pulled = true;
  while (pulled) {
    pulled = false;
    for (const q of queues) { if (q.length) { flat.push(q.shift()); pulled = true; } }
  }
  const others = results.filter(r => !sources.find(s => s.id === r.site));
  flat.push(...others.map(it => ({...it, _source: {label:(it.site||'').toUpperCase(), kind: it.kind||'web'}})));

  // Fallback height when a thumbnail fails to load or there's none — keeps
  // the placeholder reasonably-sized for the medium type instead of zero-height.
  const placeholderHeight = (kind) => ({video: 230, ad: 250, image: 260, web: 170}[kind] || 200);

  return (
    <div style={{padding:'24px 32px 60px'}}>
      <div style={{display:'flex',gap:14,alignItems:'center',marginBottom:18,padding:'14px 18px',
        background:'var(--paper)',border:'1px solid var(--rule)',borderRadius:10,boxShadow:'var(--shadow-1)'}}>
        <button onClick={onBack} className="btn sm">← 보드</button>
        <div style={{flex:1,minWidth:0}}>
          <div className="mono" style={{fontSize:9.5,color:'var(--muted)',letterSpacing:'.08em',marginBottom:3}}>QUERY</div>
          <div style={{fontSize:14,fontWeight:600}}>{results.length} hits across {sources.length} sources</div>
        </div>
        <button className="btn sm primary" disabled={savedItems.size===0 || downloading} onClick={onDownloadAll}>
          {downloading
            ? <><V2Spinner size="sm" color="#fff"/> <span>저장 중…</span></>
            : <><V2Icon name="folder" size={11} color="#fff"/> {folder}로 저장 ({savedItems.size})</>}
        </button>
      </div>

      <div style={{columnCount:4,columnGap:14}}>
        {flat.map((it, idx) => (
          <V2RefCard key={it.url + idx} it={it}
            saved={savedItems.has(it.url)}
            toggleSave={toggleSave}
            placeholderHeight={placeholderHeight}/>
        ))}
      </div>
    </div>
  );
};

// Single result card — uses the real image's natural aspect ratio for masonry.
// Falls back to a kind-typed placeholder while loading or on error.
const V2RefCard = ({it, saved, toggleSave, placeholderHeight}) => {
  const isPlayable = (it.kind === 'video') || (it._source.kind === 'video') || (it._source.kind === 'ad');
  const [loaded, setLoaded] = React.useState(false);
  const [errored, setErrored] = React.useState(false);
  const ph = placeholderHeight(it._source?.kind || it.kind);

  return (
    <div onClick={()=>window.open(it.url,'_blank','noopener')} style={{
      breakInside:'avoid',marginBottom:14,position:'relative',
      background:'var(--paper)',borderRadius:10,overflow:'hidden',
      boxShadow: saved ? '0 0 0 2px var(--accent), var(--shadow-1)' : 'var(--shadow-1)',
      cursor:'pointer',display:'block'}}>
      <div style={{position:'relative',background:'linear-gradient(160deg,#e8e3d8,#bfb6a3)',
        minHeight: (loaded && !errored) ? 0 : ph,
        display: 'block'}}>
        {it.thumbnail && !errored && (
          <img src={it.thumbnail} alt="" loading="lazy"
            onLoad={()=>setLoaded(true)}
            onError={()=>setErrored(true)}
            style={{display:'block',width:'100%',height:'auto',
              opacity: loaded ? 1 : 0, transition:'opacity .25s ease-out'}}/>
        )}
        {!loaded && !errored && it.thumbnail && (
          <div className="v2-skeleton" style={{position:'absolute',inset:0}}/>
        )}
        {(errored || !it.thumbnail) && (
          <div style={{height:ph, display:'flex',alignItems:'center',justifyContent:'center',
            color:'rgba(255,255,255,.7)',fontFamily:'var(--font-mono)',fontSize:11,letterSpacing:'.08em'}}>
            {(it._source?.label || it.site || 'WEB').toUpperCase()}
          </div>
        )}
        <div style={{position:'absolute',top:8,left:8,padding:'3px 7px',background:'rgba(255,255,255,.95)',borderRadius:4,
          fontSize:9,fontFamily:'var(--font-mono)',fontWeight:700,letterSpacing:'.06em'}}>
          {it._source.label}
        </div>
        <button onClick={e=>{e.stopPropagation();toggleSave(it.url);}} style={{
          position:'absolute',top:8,right:8,padding:'6px 10px',borderRadius:18,
          background: saved ? 'var(--accent)' : '#fff',
          color: saved ? '#fff' : 'var(--ink)',
          border:'none',cursor:'pointer',fontSize:11,fontWeight:700,
          display:'inline-flex',alignItems:'center',gap:5,
          boxShadow:'0 2px 8px rgba(0,0,0,.18)', fontFamily:'inherit'
        }}>
          <V2Icon name={saved?'check':'plus'} size={11} color={saved?'#fff':'var(--ink)'}/>
          {saved ? '저장됨' : '저장'}
        </button>
        {isPlayable && (
          <div style={{position:'absolute',inset:0,display:'flex',alignItems:'center',justifyContent:'center',
            background:'linear-gradient(180deg, rgba(0,0,0,0) 60%, rgba(0,0,0,.35) 100%)',pointerEvents:'none'}}>
            <div style={{width:46,height:46,borderRadius:23,background:'rgba(255,255,255,.95)',display:'flex',alignItems:'center',justifyContent:'center'}}>
              <V2Icon name="play" size={20} color="var(--ink)"/>
            </div>
          </div>
        )}
      </div>
      <div style={{padding:'10px 12px 12px'}}>
        <div style={{fontSize:12.5,fontWeight:500,lineHeight:1.4,
          display:'-webkit-box',WebkitLineClamp:2,WebkitBoxOrient:'vertical',overflow:'hidden'}}>
          {it.title || '(no title)'}
        </div>
        {(it.author) && (
          <div className="mono" style={{fontSize:9.5,color:'var(--muted)',marginTop:5,letterSpacing:'.02em'}}>
            {it.author}
          </div>
        )}
      </div>
    </div>
  );
};

window.V2RefStage = V2RefStage;
