// v2 DATA stage — editable project, folder tree, dropzone, file grid, team pane

// File-card thumbnail. Image files render an actual <img>; video files use
// the <video> element's first frame as a poster (so users see the content,
// not a generic icon).
const V2FileThumb = ({file}) => {
  const [errored, setErrored] = React.useState(false);
  const [loaded, setLoaded] = React.useState(false);
  const meta = window.v2FileMeta(file.kind);
  const url = window.API.fileRawUrl(file.id);
  const fallback = (
    <div style={{height:130,background:meta.tint,color:'#fff',display:'flex',alignItems:'center',justifyContent:'center'}}>
      <V2Icon name={meta.icon} size={28} color="rgba(255,255,255,.85)"/>
    </div>
  );
  if (errored) return fallback;
  return (
    <div style={{position:'relative',height:130,overflow:'hidden',background:'var(--bg-2)'}}>
      {!loaded && <div className="v2-skeleton" style={{position:'absolute',inset:0}}/>}
      {file.kind === 'image' ? (
        <img src={url} alt="" loading="lazy"
          onLoad={()=>setLoaded(true)} onError={()=>setErrored(true)}
          style={{width:'100%',height:'100%',objectFit:'cover',
            opacity: loaded?1:0, transition:'opacity .25s ease-out'}}/>
      ) : (
        <video src={url} preload="metadata" muted playsInline
          onLoadedData={()=>setLoaded(true)} onError={()=>setErrored(true)}
          style={{width:'100%',height:'100%',objectFit:'cover',
            opacity: loaded?1:0, transition:'opacity .25s ease-out'}}/>
      )}
      {file.kind === 'video' && loaded && (
        <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,.3) 100%)',pointerEvents:'none'}}>
          <div style={{width:38,height:38,borderRadius:19,background:'rgba(255,255,255,.95)',
            display:'flex',alignItems:'center',justifyContent:'center'}}>
            <V2Icon name="play" size={16} color="var(--ink)"/>
          </div>
        </div>
      )}
    </div>
  );
};

const V2DataStage = ({palette, project, user, onUpload, onAnalyzeFile, onAnalyzeAll, onUpdateProject})=>{
  const [uploads, setUploads] = React.useState({});  // {tempId: {name, progress, error}}
  const [pendingAnalyze, setPendingAnalyze] = React.useState({}); // {fileId: true}
  // Folder list is the source of truth on `project.folders` so empty folders
  // still show up in the tree (otherwise users can't drop files into them).
  const folders = {};
  (project?.folders || []).forEach(f => { folders[f.name] = []; });
  (palette.files || []).forEach(f => { (folders[f.folder] ||= []).push(f); });
  const folderNames = Object.keys(folders);
  const [activeFolder, setActiveFolder] = React.useState(folderNames[0] || '');
  const [editing, setEditing] = React.useState(null);
  const [name, setName] = React.useState(palette.project?.name || '');
  const [desc, setDesc] = React.useState(palette.project?.brief || '');
  React.useEffect(() => { setName(palette.project?.name || ''); setDesc(palette.project?.brief || ''); }, [palette.project?.id]);

  const stats = (palette.files || []).reduce((s,f) => {
    s.total++;
    s[f.status] = (s[f.status]||0) + 1;
    return s;
  }, {total:0});
  const visibleFiles = activeFolder ? (folders[activeFolder] || []) : (palette.files || []);

  const fileInputRef = React.useRef(null);
  const onDropFiles = async (files) => {
    if (!files?.length) return;
    const folderId = (project?.folders || []).find(f => f.name === activeFolder)?.id || (project?.folders || [])[0]?.id;
    if (!folderId) return;
    for (const file of files) {
      const tid = 'up-' + Math.random().toString(36).slice(2,9);
      setUploads(u => ({...u, [tid]: {name: file.name, progress: 0}}));
      try {
        await onUpload(folderId, file, (p) => {
          setUploads(u => ({...u, [tid]: {...u[tid], progress: p}}));
        });
        setUploads(u => { const {[tid]:_, ...rest} = u; return rest; });
      } catch(e) {
        setUploads(u => ({...u, [tid]: {...u[tid], error: e.message}}));
        setTimeout(() => setUploads(u => { const {[tid]:_, ...rest} = u; return rest; }), 4000);
      }
    }
  };

  const triggerAnalyze = async (fileId) => {
    setPendingAnalyze(p => ({...p, [fileId]: true}));
    try { await onAnalyzeFile?.(fileId); }
    finally { setPendingAnalyze(p => { const {[fileId]:_, ...rest} = p; return rest; }); }
  };

  return (
    <div className="v2-app" style={{display:'grid',gridTemplateColumns:'320px 1fr 380px',height:'100%',background:'var(--bg)'}}>
      {/* LEFT — folder tree + project header */}
      <div style={{borderRight:'1px solid var(--rule)',background:'var(--paper-2)',padding:'18px 14px',overflow:'auto'}}>
        <div style={{padding:'4px 10px 18px',borderBottom:'1px solid var(--rule)',marginBottom:18}}>
          <div className="mono" style={{fontSize:9,color:'var(--muted)',letterSpacing:'.1em',marginBottom:6}}>PROJECT</div>
          {editing==='name' ? (
            <input autoFocus value={name} onChange={e=>setName(e.target.value)}
              onBlur={()=>{ setEditing(null); if (name !== palette.project?.name) onUpdateProject?.({name}); }}
              onKeyDown={e=>{if(e.key==='Enter')e.target.blur();}}
              style={{width:'100%',padding:'2px 4px',margin:'0 -4px',fontSize:13,fontWeight:700,
                border:'1px solid var(--accent)',borderRadius:4,outline:'none',fontFamily:'inherit'}}/>
          ) : (
            <div onClick={()=>setEditing('name')} style={{fontSize:13,fontWeight:700,cursor:'text',padding:'2px 0',marginBottom:6,display:'flex',alignItems:'center',gap:6}}>
              <span style={{flex:1,minWidth:0,overflow:'hidden',textOverflow:'ellipsis'}}>{name || 'Untitled'}</span>
              <V2Icon name="pencil" size={11} color="var(--muted)"/>
            </div>
          )}
          {editing==='desc' ? (
            <textarea autoFocus value={desc} onChange={e=>setDesc(e.target.value)}
              onBlur={()=>{ setEditing(null); if (desc !== palette.project?.brief) onUpdateProject?.({brief:desc}); }}
              rows={3}
              style={{width:'100%',padding:'4px',margin:'0 -4px',fontSize:11,lineHeight:1.45,
                border:'1px solid var(--accent)',borderRadius:4,outline:'none',fontFamily:'inherit',resize:'none'}}/>
          ) : (
            <div onClick={()=>setEditing('desc')} style={{fontSize:11,color:'var(--sub)',lineHeight:1.45,cursor:'text',padding:'2px 0',minHeight:18,display:'flex',gap:6}}>
              <span style={{flex:1,minWidth:0}}>{desc || <span style={{color:'var(--muted)',fontStyle:'italic'}}>프로젝트 설명을 입력하세요…</span>}</span>
              <V2Icon name="pencil" size={10} color="var(--muted)"/>
            </div>
          )}
        </div>

        <div className="tag" style={{color:'var(--muted)',marginBottom:14}}>FOLDERS</div>
        <div onClick={()=>setActiveFolder('')} style={{display:'flex',alignItems:'center',gap:8,padding:'8px 10px',borderRadius:6,fontSize:12.5,marginBottom:2,
          background: activeFolder==='' ? 'var(--accent-3)' : 'transparent', cursor:'pointer'}}>
          <V2Icon name="folder" size={14} color={activeFolder==='' ? 'var(--accent)' : 'var(--sub)'}/>
          모든 파일
          <span className="mono" style={{marginLeft:'auto',fontSize:10,color:'var(--muted)'}}>{stats.total}</span>
        </div>
        {folderNames.map(n => (
          <div key={n} onClick={()=>setActiveFolder(n)} style={{display:'flex',alignItems:'center',gap:8,padding:'8px 10px',borderRadius:6,fontSize:12.5,marginBottom:2,
            background: activeFolder===n ? 'var(--accent-3)' : 'transparent', cursor:'pointer'}}>
            <V2Icon name="folder" size={14} color={activeFolder===n ? 'var(--accent)' : 'var(--sub)'}/>
            {n}
            <span className="mono" style={{marginLeft:'auto',fontSize:10,color:'var(--muted)'}}>{folders[n].length}</span>
          </div>
        ))}
      </div>

      {/* CENTER — dropzone + grid */}
      <div className="scroll" style={{padding:28, overflow:'auto'}}
        onDragOver={e=>{e.preventDefault();}}
        onDrop={e=>{e.preventDefault(); onDropFiles(Array.from(e.dataTransfer?.files||[]));}}>
        <div style={{padding:'48px 32px',border:'1.5px dashed rgba(0,103,71,.4)',borderRadius:16,
          background:'linear-gradient(135deg, rgba(0,103,71,.06), rgba(255,182,0,.05) 60%, transparent)',
          textAlign:'center',marginBottom:32,position:'relative'}}>
          <div style={{display:'inline-flex',gap:6,marginBottom:14}}>
            {[
              {bg:'#006747',n:'doc'},{bg:'#004D35',n:'image'},{bg:'#1B2A24',n:'video'},
              {bg:'#FFB600',n:'audio',fg:'#1B2A24'},{bg:'#4D5A53',n:'url'}
            ].map((b,i) => (
              <div key={i} style={{width:36,height:36,borderRadius:8,background:b.bg,
                color:b.fg||'#fff',display:'flex',alignItems:'center',justifyContent:'center'}}>
                <V2Icon name={b.n} size={16} color={b.fg||'#fff'}/>
              </div>
            ))}
          </div>
          <h2 style={{margin:'0 0 6px',fontSize:18,fontWeight:600}}>자료를 끌어다 놓으세요</h2>
          <p style={{margin:0,fontSize:13,color:'var(--sub)',lineHeight:1.6}}>
            PDF · DOCX · PPTX · 이미지 · 영상 · 오디오 · URL — 폴더째 업로드 가능합니다.<br/>
            업로드되는 즉시 AI가 페이지/씬/영역 단위로 분석을 시작합니다.
          </p>
          <div style={{display:'flex',gap:8,justifyContent:'center',marginTop:16}}>
            <button className="btn primary" onClick={()=>fileInputRef.current?.click()}>
              <V2Icon name="plus" size={13}/> 파일 선택
            </button>
            <input ref={fileInputRef} type="file" multiple style={{display:'none'}}
              onChange={e=>onDropFiles(Array.from(e.target.files || []))}/>
          </div>
        </div>

        {Object.keys(uploads).length > 0 && (
          <div style={{marginBottom:18,padding:'12px 14px',background:'var(--paper)',border:'1px solid var(--rule)',
            borderRadius:10,display:'flex',flexDirection:'column',gap:8,boxShadow:'var(--shadow-1)'}}>
            <div className="tag" style={{color:'var(--muted)'}}>UPLOADING · {Object.keys(uploads).length}</div>
            {Object.entries(uploads).map(([tid, u]) => (
              <div key={tid} style={{display:'flex',alignItems:'center',gap:10,fontSize:12}}>
                {u.error ? <V2Icon name="x" size={12} color="var(--accent)"/> : <V2Spinner size="sm" color="var(--accent)"/>}
                <div style={{flex:1,minWidth:0}}>
                  <div style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',fontWeight:500}}>{u.name}</div>
                  <div style={{height:4,background:'var(--bg-2)',borderRadius:2,marginTop:5,overflow:'hidden'}}>
                    <div style={{height:'100%',width:`${Math.round((u.progress||0)*100)}%`,
                      background: u.error ? 'var(--accent)' : 'var(--ink)',
                      transition:'width .2s ease-out'}}/>
                  </div>
                  {u.error && <div style={{fontSize:10.5,color:'var(--accent)',marginTop:3}}>업로드 실패: {u.error}</div>}
                </div>
                <span className="mono" style={{fontSize:10,color:'var(--muted)',minWidth:32,textAlign:'right'}}>
                  {u.error ? 'FAIL' : `${Math.round((u.progress||0)*100)}%`}
                </span>
              </div>
            ))}
          </div>
        )}
        <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:14}}>
          <h3 style={{margin:0,fontSize:14,fontWeight:600}}>업로드된 자료</h3>
          <span className="mono" style={{fontSize:10,color:'var(--muted)'}}>
            {visibleFiles.length} ITEMS
            {stats.analyzed ? ` · ${stats.analyzed} ANALYZED` : ''}
            {stats.analyzing ? ` · ${stats.analyzing} ANALYZING` : ''}
            {stats.not_analyzed ? ` · ${stats.not_analyzed} NOT ANALYZED` : ''}
          </span>
          <div style={{flex:1}}/>
          <button className="btn sm primary" onClick={onAnalyzeAll}><V2Icon name="sparkles" size={11}/> Analyze all</button>
        </div>

        <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(220px,1fr))',gap:12}}>
          {visibleFiles.map(f => {
            const meta = v2FileMeta(f.kind);
            const isNew = f.status==='not_analyzed';
            const canAnalyze = f.status==='not_analyzed' || f.status==='pending' || f.status==='failed' || f.status==='idle';
            const hasMedia = f.kind === 'image' || f.kind === 'video';
            return (
              <div key={f.id} style={{background:'var(--paper)',
                border: isNew ? '1.5px solid var(--accent)' : '1px solid var(--rule)',
                borderRadius:10,
                padding: hasMedia ? 0 : 14,
                overflow:'hidden',
                boxShadow: isNew ? '0 0 0 3px rgba(200,74,31,.08), var(--shadow-1)' : 'var(--shadow-1)',
                position:'relative',
                display:'flex',flexDirection:'column'}}>
                {isNew && (
                  <span className="mono" style={{position:'absolute',top:hasMedia?8:-9,left:hasMedia?8:12,
                    fontSize:8.5,letterSpacing:'.1em',
                    padding:'2px 7px',background:'var(--accent)',color:'#fff',borderRadius:3,fontWeight:600,
                    zIndex:2}}>NEW · 방금 업로드</span>
                )}
                {hasMedia && <V2FileThumb file={f}/>}
                <div style={{padding: hasMedia ? '12px 14px 0' : 0,
                  display:'flex',gap:10,alignItems:'flex-start',marginBottom:10}}>
                  {!hasMedia && (
                    <div style={{width:32,height:32,borderRadius:7,background:meta.tint,color:'#fff',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
                      <V2Icon name={meta.icon} size={15}/>
                    </div>
                  )}
                  <div style={{flex:1,minWidth:0}}>
                    <div style={{fontSize:12.5,fontWeight:500,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{f.name}</div>
                    <div className="mono" style={{fontSize:10,color:'var(--muted)',marginTop:3}}>{meta.label} · {f.size||''}</div>
                  </div>
                </div>
                <div style={{padding: hasMedia ? '0 14px 14px' : 0,marginTop:hasMedia?'auto':0}}>
                  <div style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
                    <V2StatusPill status={f.status} progress={f.progress}/>
                    <span className="mono" style={{fontSize:10,color:'var(--muted)'}}>{f.updated}</span>
                  </div>
                  {canAnalyze && (
                    <button className={isNew?'btn sm primary':'btn sm'}
                      onClick={()=>triggerAnalyze(f.id)}
                      disabled={!!pendingAnalyze[f.id]}
                      style={{marginTop:10,width:'100%',justifyContent:'center',fontSize:11}}>
                      {pendingAnalyze[f.id] ? <>
                        <V2Spinner size="sm" color={isNew?'#fff':'currentColor'}/>
                        <span style={{marginLeft:6}}>분석 중…</span>
                      </> : <>
                        <V2Icon name="sparkles" size={11} color={isNew?'#fff':undefined}/>
                        {isNew ? '지금 분석 시작' : 'Analyze'}
                      </>}
                    </button>
                  )}
                  {f.status === 'analyzing' && !pendingAnalyze[f.id] && (
                    <div style={{marginTop:8,display:'flex',alignItems:'center',gap:6,fontSize:10.5,color:'var(--muted)'}}>
                      <V2Spinner size="sm" color="var(--accent)"/>
                      <span>백그라운드 분석 중…</span>
                    </div>
                  )}
                </div>
              </div>
            );
          })}
          {visibleFiles.length === 0 && (
            <div style={{gridColumn:'1 / -1',padding:'40px 20px',textAlign:'center',color:'var(--muted)',fontSize:12.5}}>
              이 폴더에는 아직 파일이 없습니다. 위 영역으로 끌어다 놓아주세요.
            </div>
          )}
        </div>
      </div>

      {/* RIGHT — Team pane */}
      <V2TeamPane projectId={project?.id} currentUserId={user?.id}/>
    </div>
  );
};

const v2Initial = (s) => (s || '·').trim().slice(0,1).toUpperCase();
const v2Color = (seed) => {
  const palette = ['#006747','#004D35','#7B6A2A','#857E70','#1B2A24','#FFB600'];
  let h = 0; for (const c of (seed||'')) h = (h*31 + c.charCodeAt(0)) >>> 0;
  return palette[h % palette.length];
};

const V2TeamPane = ({projectId, currentUserId})=>{
  const [members, setMembers] = React.useState([]);
  const [pending, setPending] = React.useState([]);
  const [isOwner, setIsOwner] = React.useState(false);
  const [canManage, setCanManage] = React.useState(false);
  const [loadingMembers, setLoadingMembers] = React.useState(true);
  const [invite, setInvite] = React.useState('');
  const [inviteRole, setInviteRole] = React.useState('editor');
  const [busy, setBusy] = React.useState(false);
  const [shareLink, setShareLink] = React.useState(null);
  const [linkCopied, setLinkCopied] = React.useState(false);
  const [feedback, setFeedback] = React.useState(null);

  const reload = React.useCallback(async () => {
    if (!projectId) return;
    setLoadingMembers(true);
    try {
      const r = await window.API.listMembers(projectId);
      setMembers(r.members || []);
      setPending(r.pending_invites || []);
      setIsOwner(!!r.is_owner);
      setCanManage(!!r.can_manage);
    } catch(e) {
      console.error('listMembers', e);
    } finally { setLoadingMembers(false); }
  }, [projectId]);

  React.useEffect(() => { reload(); }, [reload]);

  const sendInvite = async () => {
    const v = invite.trim();
    if (!projectId) return;
    setBusy(true); setFeedback(null); setShareLink(null); setLinkCopied(false);
    try {
      const res = await window.API.inviteToProject(projectId, {email: v || null, role: inviteRole});
      const url = window.location.origin + res.share_path;
      setShareLink(url);
      setFeedback({ok:true, text: v ? `${v} 에게 보낼 초대 링크가 생성됐어요` : '초대 링크가 생성됐어요'});
      setInvite('');
      reload();
    } catch(e) {
      setFeedback({ok:false, text: '초대 실패 — 권한을 확인해주세요'});
    } finally { setBusy(false); }
  };

  const copyLink = async () => {
    if (!shareLink) return;
    try { await navigator.clipboard.writeText(shareLink); setLinkCopied(true); setTimeout(()=>setLinkCopied(false), 2000); }
    catch(e) { /* ignore */ }
  };

  const removeMember = async (uid) => {
    if (!projectId || !uid) return;
    if (!window.confirm('이 멤버를 프로젝트에서 제거할까요?')) return;
    try { await window.API.removeMember(projectId, uid); reload(); }
    catch(e) { setFeedback({ok:false, text:'제거 실패'}); }
  };

  const changeRole = async (uid, role) => {
    if (!projectId || !uid) return;
    try { await window.API.updateMemberRole(projectId, uid, role); reload(); }
    catch(e) { setFeedback({ok:false, text:'권한 변경 실패'}); }
  };

  return (
    <div style={{borderLeft:'1px solid var(--rule)',background:'var(--paper-2)',padding:24,overflow:'auto'}}>
      <div className="tag" style={{color:'var(--muted)',marginBottom:6}}>TEAM · {members.length} MEMBER{members.length===1?'':'S'}</div>
      <div style={{fontSize:11.5,color:'var(--sub)',lineHeight:1.5,marginBottom:18}}>
        프로젝트를 함께 보거나 편집할 팀원을 초대하세요. 권한은 언제든 바꿀 수 있습니다.
      </div>

      {canManage && (
        <div style={{background:'var(--paper)',border:'1px solid var(--rule)',borderRadius:10,padding:'14px 14px 12px',marginBottom:18,boxShadow:'var(--shadow-1)'}}>
          <label className="mono" style={{fontSize:9.5,color:'var(--muted)',letterSpacing:'.08em',display:'block',marginBottom:6}}>INVITE BY EMAIL</label>
          <div style={{display:'flex',gap:6}}>
            <input value={invite} onChange={e=>setInvite(e.target.value)}
              onKeyDown={e=>{if(e.key==='Enter')sendInvite();}}
              placeholder="name@palette.co · (선택)"
              style={{flex:1,padding:'8px 10px',border:'1px solid var(--rule-2)',borderRadius:6,
                background:'var(--paper-2)',fontSize:12,fontFamily:'inherit',outline:'none',minWidth:0}}/>
            <select value={inviteRole} onChange={e=>setInviteRole(e.target.value)}
              style={{padding:'8px 8px',border:'1px solid var(--rule-2)',borderRadius:6,
                background:'var(--paper-2)',fontSize:11.5,fontFamily:'inherit',cursor:'pointer'}}>
              <option value="editor">Editor</option>
              <option value="viewer">Viewer</option>
            </select>
          </div>
          <button onClick={sendInvite} className="btn primary" disabled={busy}
            style={{width:'100%',justifyContent:'center',marginTop:8,fontSize:12}}>
            {busy ? <><V2Spinner size="sm" color="#fff"/> <span>생성 중…</span></>
                  : <><V2Icon name="plus" size={11} color="#fff"/> 초대 링크 만들기</>}
          </button>
          {feedback && (
            <div style={{marginTop:8,fontSize:10.5,color: feedback.ok ? '#3D8C3D' : 'var(--accent)'}}>{feedback.text}</div>
          )}
          {shareLink && (
            <div style={{marginTop:10,padding:'10px',background:'var(--paper-2)',border:'1px dashed var(--rule-2)',borderRadius:8}}>
              <div className="mono" style={{fontSize:9,color:'var(--muted)',letterSpacing:'.08em',marginBottom:4}}>SHARE LINK</div>
              <div style={{display:'flex',gap:6,alignItems:'center'}}>
                <input readOnly value={shareLink} onClick={e=>e.target.select()}
                  style={{flex:1,padding:'6px 8px',border:'1px solid var(--rule)',borderRadius:5,
                    background:'#fff',fontSize:10.5,fontFamily:'var(--font-mono)',outline:'none',minWidth:0}}/>
                <button onClick={copyLink} className="btn sm">
                  <V2Icon name={linkCopied?'check':'doc'} size={10}/> {linkCopied?'복사됨':'복사'}
                </button>
              </div>
            </div>
          )}
        </div>
      )}

      <div className="tag" style={{color:'var(--muted)',marginBottom:8}}>MEMBERS</div>
      {loadingMembers && members.length === 0 && (
        <div style={{display:'flex',flexDirection:'column',gap:8,marginBottom:8}}>
          {[0,1,2].map(i => (
            <div key={i} style={{display:'flex',alignItems:'center',gap:10,padding:'10px 8px'}}>
              <V2Skeleton w={30} h={30} style={{borderRadius:15}}/>
              <div style={{flex:1,display:'flex',flexDirection:'column',gap:5}}>
                <V2Skeleton w="70%" h={11}/>
                <V2Skeleton w="45%" h={9}/>
              </div>
            </div>
          ))}
        </div>
      )}
      <div style={{display:'flex',flexDirection:'column',gap:2}}>
        {members.map(m => {
          const isMe = m.user_id === currentUserId;
          const display = m.name || (m.email||'').split('@')[0] || '—';
          const isOwnerRow = m.role === 'owner';
          return (
            <div key={m.user_id} style={{display:'flex',alignItems:'center',gap:10,padding:'10px 8px',borderRadius:6}}>
              <div style={{position:'relative',flexShrink:0}}>
                <div style={{width:30,height:30,borderRadius:15,background:v2Color(m.email||m.user_id),color:'#fff',display:'flex',alignItems:'center',justifyContent:'center',fontSize:12,fontWeight:600}}>{v2Initial(m.name || m.email)}</div>
              </div>
              <div style={{flex:1,minWidth:0}}>
                <div style={{display:'flex',alignItems:'center',gap:6}}>
                  <span style={{fontSize:12.5,fontWeight:500}}>{display}</span>
                  {isMe && <span className="mono" style={{fontSize:9,color:'var(--muted)'}}>· YOU</span>}
                </div>
                <div className="mono" style={{fontSize:9.5,color:'var(--muted)',marginTop:1,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{m.email||'—'}</div>
              </div>
              {isOwnerRow ? (
                <span className="mono" style={{fontSize:9.5,color:'var(--accent)',padding:'3px 8px',background:'var(--accent-3)',borderRadius:10}}>OWNER</span>
              ) : canManage ? (
                <>
                  <select value={m.role} onChange={e=>changeRole(m.user_id, e.target.value)}
                    style={{padding:'3px 6px',border:'1px solid var(--rule)',borderRadius:5,
                      background:'var(--paper)',fontSize:10.5,fontFamily:'inherit',cursor:'pointer'}}>
                    <option value="editor">Editor</option>
                    <option value="viewer">Viewer</option>
                  </select>
                  <button onClick={()=>removeMember(m.user_id)} className="btn sm ghost"
                    title="멤버 제거" style={{padding:'2px 6px'}}>
                    <V2Icon name="x" size={11} color="var(--muted)"/>
                  </button>
                </>
              ) : (
                <span className="mono" style={{fontSize:10,color:'var(--sub)',padding:'4px 8px',border:'1px solid var(--rule)',borderRadius:6}}>{m.role||'member'}</span>
              )}
            </div>
          );
        })}
        {members.length === 0 && (
          <div style={{padding:'20px 6px',fontSize:11,color:'var(--muted)'}}>아직 멤버 정보 없음 — 새로고침해주세요.</div>
        )}
      </div>

      {pending.length > 0 && canManage && (
        <>
          <div className="tag" style={{color:'var(--muted)',marginTop:18,marginBottom:8}}>PENDING INVITES · {pending.length}</div>
          <div style={{display:'flex',flexDirection:'column',gap:2}}>
            {pending.map(inv => (
              <div key={inv.token} style={{display:'flex',alignItems:'center',gap:10,padding:'8px',borderRadius:6}}>
                <div style={{width:24,height:24,borderRadius:12,background:'var(--paper)',border:'1px dashed var(--rule-2)',display:'flex',alignItems:'center',justifyContent:'center'}}>
                  <V2Icon name="mail" size={11} color="var(--muted)"/>
                </div>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:12,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{inv.email || '(open invite)'}</div>
                  <div className="mono" style={{fontSize:9.5,color:'var(--muted)'}}>· {inv.role} · {(inv.token||'').slice(0,12)}…</div>
                </div>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
};

window.V2DataStage = V2DataStage;
