// Moment — main interactive app
// Wires every screen to the ASP.NET Core API (same origin, no CORS needed)

const { useState, useEffect, useRef, useCallback } = React;

// ── Video template definitions (mirrors C# CollageService.TemplateMap) ───────
const TEMPLATES = {
  2: [
    { id:'2-topbottom', name:'위아래',   desc:'세로로 나란히',       orientation:'portrait',  cw:1080, ch:1920,
      slots:[{x:0,y:0,w:1080,h:960},{x:0,y:960,w:1080,h:960}] },
    { id:'2-leftright', name:'좌우',     desc:'가로로 나란히',       orientation:'landscape', cw:1920, ch:1080,
      slots:[{x:0,y:0,w:960,h:1080},{x:960,y:0,w:960,h:1080}] },
    { id:'2-bigsmall',  name:'주인공',   desc:'하나가 더 크게',      orientation:'square',    cw:1080, ch:1080,
      slots:[{x:0,y:0,w:700,h:1080},{x:700,y:0,w:380,h:1080}] },
  ],
  3: [
    { id:'3-strips',    name:'세 줄',    desc:'위아래로 세 줄',      orientation:'portrait',  cw:1080, ch:1920,
      slots:[{x:0,y:0,w:1080,h:640},{x:0,y:640,w:1080,h:640},{x:0,y:1280,w:1080,h:640}] },
    { id:'3-bigtop',    name:'하이라이트', desc:'위 하나 크게, 아래 둘', orientation:'portrait', cw:1080, ch:1920,
      slots:[{x:0,y:0,w:1080,h:1100},{x:0,y:1100,w:540,h:820},{x:540,y:1100,w:540,h:820}] },
    { id:'3-triptych',  name:'트리티크', desc:'가로 3등분',          orientation:'landscape', cw:1920, ch:1080,
      slots:[{x:0,y:0,w:640,h:1080},{x:640,y:0,w:640,h:1080},{x:1280,y:0,w:640,h:1080}] },
  ],
  4: [
    { id:'4-grid',      name:'그리드',   desc:'2×2 격자',           orientation:'square',    cw:1080, ch:1080,
      slots:[{x:0,y:0,w:540,h:540},{x:540,y:0,w:540,h:540},{x:0,y:540,w:540,h:540},{x:540,y:540,w:540,h:540}] },
    { id:'4-row',       name:'가로 줄',  desc:'네 명이 나란히',     orientation:'landscape', cw:1920, ch:1080,
      slots:[{x:0,y:0,w:480,h:1080},{x:480,y:0,w:480,h:1080},{x:960,y:0,w:480,h:1080},{x:1440,y:0,w:480,h:1080}] },
    { id:'4-feature',   name:'피처',     desc:'왼쪽 하나 크게',     orientation:'landscape', cw:1920, ch:1080,
      slots:[{x:0,y:0,w:1200,h:1080},{x:1200,y:0,w:720,h:360},{x:1200,y:360,w:720,h:360},{x:1200,y:720,w:720,h:360}] },
    { id:'4-stagger',   name:'엇갈림',   desc:'위아래가 엇갈려요',  orientation:'portrait',  cw:1080, ch:1920,
      slots:[{x:0,y:0,w:600,h:960},{x:600,y:0,w:480,h:960},{x:0,y:960,w:480,h:960},{x:480,y:960,w:600,h:960}] },
    { id:'4-column',    name:'세로 줄',  desc:'네 명이 세로로',     orientation:'portrait',  cw:1080, ch:1920,
      slots:[{x:0,y:0,w:1080,h:480},{x:0,y:480,w:1080,h:480},{x:0,y:960,w:1080,h:480},{x:0,y:1440,w:1080,h:480}] },
  ],
  5: [
    { id:'5-grid',      name:'그리드',   desc:'다섯 명 격자',       orientation:'square',    cw:1080, ch:1080,
      slots:[{x:0,y:0,w:540,h:540},{x:540,y:0,w:540,h:540},{x:0,y:540,w:360,h:540},{x:360,y:540,w:360,h:540},{x:720,y:540,w:360,h:540}] },
    { id:'5-portrait',  name:'세로',     desc:'다섯 명 세로형',     orientation:'portrait',  cw:1080, ch:1920,
      slots:[{x:0,y:0,w:540,h:960},{x:540,y:0,w:540,h:960},{x:0,y:960,w:360,h:960},{x:360,y:960,w:360,h:960},{x:720,y:960,w:360,h:960}] },
  ],
  6: [
    { id:'6-grid',      name:'그리드',   desc:'여섯 명 격자',       orientation:'square',    cw:1080, ch:1080,
      slots:[{x:0,y:0,w:360,h:540},{x:360,y:0,w:360,h:540},{x:720,y:0,w:360,h:540},{x:0,y:540,w:360,h:540},{x:360,y:540,w:360,h:540},{x:720,y:540,w:360,h:540}] },
  ],
};

function getTemplates(capacity) {
  const key = Math.min(Math.max(capacity, 2), 6);
  return TEMPLATES[key] || TEMPLATES[6];
}

const ORIENT_LABEL = { portrait:'세로형', landscape:'가로형', square:'정방형' };
const SLOT_COLORS  = ['#d8a7b1','#a7c7c5','#e2c58f','#b9acd6','#c9b79a','#9fb7d1'];

// ── Helpers ──────────────────────────────────────────────────────────────────
const COLORS = ['#f43f5e','#ec4899','#f97316','#0d9488','#8b5cf6','#f59e0b','#e11d3a','#0ea5e9'];
const EMOJIS = ['🐰','🐻','🦊','🐯','🐨','🦁','🐸','🐼'];

function memberStyle(userId) {
  const h = Math.abs([...String(userId)].reduce((a, c) => a * 31 + c.charCodeAt(0), 0));
  return { color: COLORS[h % COLORS.length], emoji: EMOJIS[h % EMOJIS.length] };
}

function fmtCountdown(ms) {
  if (ms <= 0) return '00:00:00';
  const h = Math.floor(ms / 3600000);
  const m = Math.floor((ms % 3600000) / 60000);
  const s = Math.floor((ms % 60000) / 1000);
  return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
}

function fmtLocalDate(date = new Date()) {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
}

function HomeLogo({ onClick, size = 20, color, sub = false }) {
  return (
    <div
      role={onClick ? 'button' : undefined}
      tabIndex={onClick ? 0 : undefined}
      title={onClick ? '그룹 선택으로 이동' : undefined}
      onClick={onClick}
      onKeyDown={e => {
        if (!onClick) return;
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onClick();
        }
      }}
      style={{
        display:'inline-flex',
        cursor:onClick ? 'pointer' : 'default',
        borderRadius:8,
        padding:4,
      }}
    >
      <WebLogo size={size} color={color} sub={sub} />
    </div>
  );
}

function inputSt(M, extra = {}) {
  return {
    width: '100%', height: 54, padding: '0 18px', borderRadius: 16,
    border: `1.5px solid ${M.ink4}`, background: M.ivory,
    fontSize: 15, color: M.ink, outline: 'none', boxSizing: 'border-box',
    fontFamily: M.fontBody, transition: 'border-color 0.18s, box-shadow 0.18s',
    ...extra,
  };
}

function MemberAvatar({ userId, name, size = 32 }) {
  const { color, emoji } = memberStyle(userId);
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', background: color,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: size * 0.45, flexShrink: 0,
    }}>{emoji}</div>
  );
}

// ── Label badge (타임라인 헤더와 동일한 뱃지 스타일) ─────────────────────────
function LabelBadge({ children, variant = 'neutral', mb = 10 }) {
  const M = window.M;
  const styles = {
    neutral: { color: M.ink2,  background: M.ivory,    border: `1px solid ${M.ink4}` },
    coral:   { color: M.coral, background: M.coralSoft, border: `1px solid ${M.coral}44` },
  };
  const s = styles[variant] || styles.neutral;
  return (
    <div style={{
      display: 'inline-block', fontFamily: M.fontMono, fontSize: 11, fontWeight: 700,
      letterSpacing: 0.5, padding: '3px 10px', borderRadius: 6, marginBottom: mb,
      ...s,
    }}>{children}</div>
  );
}

// ── Reaction Summary ─────────────────────────────────────────────────────────
const REACTION_OPTIONS = [['heart','😍'],['laugh','😂'],['moved','🥹'],['spark','🤩']];

function ReactionSummary({ reactions }) {
  const M = window.M;
  const [hovered, setHovered] = React.useState(null);
  const [tapped, setTapped] = React.useState(null);
  const grouped = REACTION_OPTIONS.map(([type, emoji]) => {
    const items = (reactions || []).filter(r => r.reactionType === type);
    return { type, emoji, count: items.length, names: items.map(r => r.name).join(', ') };
  }).filter(r => r.count > 0);
  if (grouped.length === 0) return null;
  return (
    <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', marginTop: 4 }}>
      {grouped.map(({ type, emoji, count, names }) => {
        const show = (hovered === type || tapped === type) && names;
        return (
          <div key={type}
            onMouseEnter={() => setHovered(type)}
            onMouseLeave={() => setHovered(null)}
            onClick={() => setTapped(p => p === type ? null : type)}
            style={{ display: 'inline-flex', alignItems: 'center', gap: 3, position: 'relative',
              padding: '2px 8px', borderRadius: 99, cursor: 'pointer', userSelect: 'none',
              background: tapped === type ? 'rgba(255,255,255,0.12)' : 'rgba(255,255,255,0.06)',
              border: `1px solid ${tapped === type ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.1)'}`,
              fontSize: 11,
            }}>
            <span style={{ fontSize: 12 }}>{emoji}</span>
            <span style={{ color: M.ink2, fontFamily: M.fontMono, fontWeight: 700 }}>{count}</span>
            {show && (
              <div style={{ position: 'absolute', bottom: 'calc(100% + 6px)', left: '50%', transform: 'translateX(-50%)',
                background: M.ivory, border: `1px solid ${M.ink4}`, borderRadius: 8, padding: '5px 10px',
                fontSize: 11, color: M.ink2, whiteSpace: 'nowrap', zIndex: 200,
                boxShadow: M.shadowCard, pointerEvents: 'none',
              }}>
                {names}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ── Template Preview Card ────────────────────────────────────────────────────
function TemplatePreview({ template, size = 110, members = [] }) {
  const scale = size / Math.max(template.cw, template.ch);
  const w = Math.round(template.cw * scale);
  const h = Math.round(template.ch * scale);
  return (
    <div style={{
      width: w, height: h, background: '#0a0a12',
      position: 'relative', borderRadius: 4, overflow: 'hidden', flexShrink: 0,
    }}>
      {template.slots.map((slot, i) => (
        <div key={i} style={{
          position: 'absolute',
          left: Math.round(slot.x * scale), top: Math.round(slot.y * scale),
          width: Math.round(slot.w * scale), height: Math.round(slot.h * scale),
          background: SLOT_COLORS[i % SLOT_COLORS.length],
          opacity: 0.82,
          border: `2px solid ${SLOT_COLORS[i % SLOT_COLORS.length]}`,
          boxShadow: 'inset 0 0 0 1px rgba(24,8,16,0.22)',
          boxSizing: 'border-box',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          {members[i] && (
            <MemberAvatar userId={members[i].userId} name={members[i].name} size={Math.min(Math.round(slot.w * scale * 0.35), 24)} />
          )}
        </div>
      ))}
    </div>
  );
}

// ── Template Vote Screen ─────────────────────────────────────────────────────
function TemplateVoteScreen({ group, user, onGenerated, onBack, onHome }) {
  const M = window.M;
  const templates = getTemplates(group.capacity);
  const [todayQuestion, setTodayQuestion] = useState(null);

  useEffect(() => {
    window.API.getTodayQuestion().then(setTodayQuestion).catch(() => {});
  }, []);

  const [selected, setSelected]       = useState(null);
  const [votes, setVotes]             = useState(null);
  const [members, setMembers]         = useState([]);
  const [generating, setGenerating]   = useState(false);
  const [error, setError]             = useState('');
  const pollRef = useRef(null);

  // Load current votes and member list
  const loadVotes = useCallback(async () => {
    try {
      const [voteData, detail] = await Promise.all([
        window.API.getTemplateVotes(group.id),
        window.API.getGroup(group.id),
      ]);
      setVotes(voteData);
      setMembers(detail.members || []);
      // Pre-select my own existing vote
      const myVote = voteData.votes?.find(v => v.userId === user.id);
      if (myVote && !selected) setSelected(myVote.templateId);
    } catch (e) {}
  }, [group.id, user.id]);

  useEffect(() => {
    loadVotes();
    pollRef.current = setInterval(loadVotes, 4000);
    return () => clearInterval(pollRef.current);
  }, [loadVotes]);

  async function handleVote(templateId) {
    setSelected(templateId);
    try {
      await window.API.voteTemplate(group.id, templateId);
      await loadVotes();
    } catch (e) {}
  }

  async function handleGenerate() {
    if (!selected) { setError('먼저 템플릿을 선택해주세요.'); return; }
    if (!allVoted) { setError('모든 그룹원이 템플릿 투표를 완료해야 영상을 생성할 수 있습니다.'); return; }
    setError('');
    setGenerating(true);
    try {
      const result = await window.API.generateCollage(group.id, winningId);
      if (result?.url) onGenerated(result);
      else setError('영상 생성에 실패했습니다. FFmpeg가 설치되어 있는지 확인해주세요.');
    } catch (e) {
      setError(e.message || '영상 생성 중 오류가 발생했습니다.');
    } finally {
      setGenerating(false);
    }
  }

  // Vote counts per templateId
  const voteCounts = {};
  if (votes?.votes) {
    for (const v of votes.votes) {
      voteCounts[v.templateId] = (voteCounts[v.templateId] || 0) + 1;
    }
  }
  const totalVoted = votes?.votes?.length ?? 0;
  const totalMembers = votes?.totalMembers ?? group.capacity;
  const allVoted = totalMembers > 0 && totalVoted >= totalMembers;
  const winningId = votes?.winningTemplateId || Object.entries(voteCounts).sort((a,b)=>b[1]-a[1])[0]?.[0];
  const winningTemplate = templates.find(t => t.id === winningId);
  const ownerMember = members.find(m => m.role === 'Owner' || m.userId === group.ownerUserId);
  const ownerName = ownerMember?.name || '방장';

  // Which member voted for what (for avatar display)
  function votersFor(templateId) {
    return (votes?.votes || []).filter(v => v.templateId === templateId);
  }

  const orientLabel = { portrait:'세로형', landscape:'가로형', square:'정방형' };

  if (generating) {
    return (
      <div style={{ width:'100vw', height:'100vh', background:M.cream, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:24, position:'relative', overflow:'hidden' }}>
        <style>{`@keyframes tv-spin { to { transform:rotate(360deg); } }`}</style>
        <div style={{ position:'absolute', width:400, height:400, borderRadius:'50%', background:`radial-gradient(circle, rgba(168,85,247,0.15) 0%, transparent 65%)`, top:'-10%', right:'-10%', filter:'blur(60px)', pointerEvents:'none' }} />
        <div style={{ position:'absolute', width:300, height:300, borderRadius:'50%', background:`radial-gradient(circle, rgba(244,63,94,0.12) 0%, transparent 65%)`, bottom:'-10%', left:'-5%', filter:'blur(50px)', pointerEvents:'none' }} />
        <div style={{ position:'relative', width:64, height:64 }}>
          <div style={{ position:'absolute', inset:0, borderRadius:'50%', border:`3px solid ${M.rose}22`, borderTop:`3px solid ${M.rose}`, animation:'tv-spin 1.4s linear infinite' }} />
          <div style={{ position:'absolute', inset:6, borderRadius:'50%', border:`2px solid ${M.coral}22`, borderTop:`2px solid ${M.coral}`, animation:'tv-spin 0.9s linear infinite reverse' }} />
        </div>
        <div style={{ fontFamily:M.fontTemplate, fontSize:22, fontWeight:700, textAlign:'center',
          background:`linear-gradient(135deg, ${M.rose}, ${M.coral})`,
          WebkitBackgroundClip:'text', WebkitTextFillColor:'transparent',
        }}>
          영상을 합치는 중이에요
        </div>
      </div>
    );
  }

  return (
    <div style={{ width:'100vw', height:'100vh', background:M.cream, display:'flex', flexDirection:'column', overflow:'hidden' }}>
      {/* Header */}
      <div style={{ height:56, borderBottom:`1px solid ${M.ink4}`, display:'flex', alignItems:'center', padding:'0 clamp(14px,4vw,32px)', gap:10, flexShrink:0, background:M.cream }}>
        <HomeLogo size={22} onClick={onHome} />
        <span style={{ color:M.ink4, fontSize:14 }}>/</span>
        <span style={{ fontSize:13, fontWeight:600, color:M.ink2, fontFamily:M.fontBody }}>템플릿 투표</span>
        <div style={{ flex:1 }} />
        <div style={{ display:'flex', alignItems:'center', gap:8, padding:'4px 12px', borderRadius:8, background:M.ivory, border:`1px solid ${M.ink4}` }}>
          <span style={{ fontFamily:M.fontMono, fontSize:12, color:M.ink3 }}>
            {totalVoted}/{totalMembers}명 투표
          </span>
        </div>
      </div>

      <div style={{ flex:1, display:'grid', gridTemplateColumns:'1fr 300px', overflow:'hidden' }}>

        {/* ── Left: Template grid ── */}
        <div style={{ padding:'clamp(16px,3vw,24px) clamp(14px,4vw,32px)', overflow:'auto' }}>
          <h2 style={{ fontFamily:M.fontSerif, fontSize:'clamp(24px,3vw,34px)', margin:'0 0 18px', fontWeight:700, letterSpacing:-0.5, wordBreak:'keep-all',
            background: `linear-gradient(135deg, #ffffff 40%, ${M.rose})`,
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
          }}>
            어떤 배치?
          </h2>

          <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(200px, 1fr))', gap:12 }}>
            {templates.map(tpl => {
              const isSelected = selected === tpl.id;
              const isWinner   = winningId === tpl.id;
              const vCount     = voteCounts[tpl.id] || 0;
              const tplVoters  = votersFor(tpl.id);
              return (
                <div
                  key={tpl.id}
                  onClick={() => handleVote(tpl.id)}
                  style={{
                    background: M.paper,
                    borderRadius: 18,
                    padding: '18px 14px 14px',
                    cursor: 'pointer',
                    border: `${isSelected ? '1.5px' : '1px'} solid ${isSelected ? M.coral : M.ink4}`,
                    boxShadow: isSelected ? `0 0 0 3px ${M.coral}25, 0 4px 20px ${M.rose}22` : 'none',
                    transition: 'border-color 0.15s, box-shadow 0.15s',
                    display: 'flex', flexDirection: 'column', gap: 10, position: 'relative',
                  }}>

                  {/* Winner badge */}
                  {isWinner && vCount > 0 && (
                    <div style={{
                      position:'absolute', top:-9, right:10,
                      background:M.coral, color:'#fff', fontSize:11, fontWeight:700,
                      padding:'2px 8px', borderRadius:6, fontFamily:M.fontMono, letterSpacing:0.5,
                    }}>1위</div>
                  )}

                  {/* Preview */}
                  <div style={{ display:'flex', justifyContent:'center' }}>
                    <TemplatePreview template={tpl} size={100} members={members} />
                  </div>

                  {/* Info */}
                  <div>
                    <div style={{ display:'flex', alignItems:'center', gap:6, marginBottom:2 }}>
                      <span style={{ fontSize:14, color:M.ink, fontWeight:700, fontFamily:M.fontBody, letterSpacing:-0.2 }}>
                        {tpl.name}
                      </span>
                      <WebChip bg={M.ivory} color={M.ink3} style={{ border:`1px solid ${M.ink4}` }}>
                        {ORIENT_LABEL[tpl.orientation]}
                      </WebChip>
                    </div>
                  </div>

                  {/* Voters */}
                  <div style={{ display:'flex', alignItems:'center', gap:4 }}>
                    {tplVoters.map(v => (
                      <div key={v.userId} title={v.name}>
                        <MemberAvatar userId={v.userId} name={v.name} size={20} />
                      </div>
                    ))}
                    {vCount > 0 && (
                      <span style={{ fontSize:12, color:M.ink3, fontFamily:M.fontMono, marginLeft:2 }}>
                        {vCount}표
                      </span>
                    )}
                  </div>

                  {/* Selection indicator */}
                  {isSelected && (
                    <div style={{
                      position:'absolute', bottom:8, right:10,
                      width:18, height:18, borderRadius:'50%',
                      background:M.coral,
                      display:'flex', alignItems:'center', justifyContent:'center',
                      color:'#fff', fontSize:11, fontWeight:700,
                    }}>✓</div>
                  )}
                </div>
              );
            })}
          </div>
        </div>

        {/* ── Right: Vote status + generate ── */}
        <div style={{
          borderLeft:`1px solid ${M.ink4}`, padding:'24px 20px',
          overflow:'hidden', background:M.paper, display:'flex', flexDirection:'column', gap:14,
          minHeight:0,
        }}>
          {/* Question */}
          <div style={{ padding:'12px 14px', borderRadius:12, background:M.coralSoft, border:`1px solid ${M.coral}33` }}>
            <LabelBadge variant="coral" mb={5}>오늘의 질문</LabelBadge>
            <div style={{ fontSize:13, color:M.ink, lineHeight:1.6, wordBreak:'keep-all', fontFamily:M.fontBody }}>
              {todayQuestion ? todayQuestion.text : '...'}
            </div>
          </div>

          {/* Vote status list */}
          <div style={{ display:'flex', flexDirection:'column', minHeight:0, flex:1 }}>
            <LabelBadge variant="neutral" mb={10}>투표 현황</LabelBadge>
            <div style={{ fontSize:12, color:M.ink3, background:M.ivory, border:`1px solid ${M.ink4}`, borderRadius:8, padding:'8px 10px', marginBottom:10, lineHeight:1.5, fontFamily:M.fontBody }}>
              방장 {ownerName}님 — 동률 시 방장 선택으로 생성됩니다.
            </div>
            <div style={{ display:'flex', flexDirection:'column', gap:6, overflowY:'auto', minHeight:0, paddingRight:2 }}>
              {members.map(m => {
                const mv = votes?.votes?.find(v => v.userId === m.userId);
                const tpl = mv ? templates.find(t => t.id === mv.templateId) : null;
                const isMe = m.userId === user.id;
                return (
                  <div key={m.userId} style={{
                    display:'flex', alignItems:'center', gap:8, padding:'7px 10px',
                    borderRadius:9, background:M.ivory,
                    border: isMe ? `1px solid ${M.coral}55` : `1px solid ${M.ink4}`,
                  }}>
                    <MemberAvatar userId={m.userId} name={m.name} size={26} />
                    <div style={{ flex:1, minWidth:0 }}>
                      <div style={{ fontSize:13, fontWeight:600, color:M.ink, letterSpacing:-0.2 }}>
                        {m.name}{isMe && <span style={{ fontSize:12, color:M.ink3, fontWeight:400 }}> (나)</span>}
                        {m.role === 'Owner' && <span style={{ fontSize:12, color:M.coral }}> · 방장</span>}
                      </div>
                      {tpl ? (
                        <div style={{ fontSize:12, color:M.coral, marginTop:1, fontFamily:M.fontBody }}>
                          {tpl.name} 선택
                        </div>
                      ) : (
                        <div style={{ fontSize:12, color:M.ink4, marginTop:1 }}>
                          아직 미선택
                        </div>
                      )}
                    </div>
                    {tpl && (
                      <TemplatePreview template={tpl} size={32} />
                    )}
                  </div>
                );
              })}
            </div>
          </div>

          {error && (
            <div style={{ fontSize:13, color:M.coral, background:M.coralSoft, padding:'10px 12px', borderRadius:9, lineHeight:1.5, borderLeft:`3px solid ${M.coral}` }}>
              {error}
            </div>
          )}

          {/* CTA */}
          <div style={{ display:'flex', flexDirection:'column', gap:8, flexShrink:0 }}>
            <WebButton
              variant="accent"
              size="lg"
              full
              onClick={handleGenerate}
              disabled={generating || !selected || !allVoted}
            >
              {!allVoted
                ? `투표 대기 중 (${totalVoted}/${totalMembers})`
                : selected
                ? `"${winningTemplate?.name || templates.find(t=>t.id===selected)?.name}" 로 생성`
                : '템플릿을 선택해주세요'
              }
            </WebButton>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Loading ──────────────────────────────────────────────────────────────────
function LoadingScreen() {
  const M = window.M;
  return (
    <>
      <style>{`
        @keyframes ls-float {
          0%, 100% { transform: translateY(0px); }
          50% { transform: translateY(-10px); }
        }
        @keyframes ls-rotate {
          to { transform: rotate(360deg); }
        }
        @keyframes ls-pulse-ring {
          0%, 100% { transform: scale(1); opacity: 0.55; }
          50% { transform: scale(1.14); opacity: 0.12; }
        }
        @keyframes ls-dot {
          0%, 65%, 100% { transform: translateY(0) scale(0.75); opacity: 0.3; }
          32% { transform: translateY(-7px) scale(1.05); opacity: 1; }
        }
        @keyframes ls-fade-up {
          from { opacity: 0; transform: translateY(22px); }
          to { opacity: 1; transform: translateY(0); }
        }
        @keyframes ls-bar {
          0% { width: 0%; }
          20% { width: 22%; }
          45% { width: 48%; }
          70% { width: 70%; }
          100% { width: 88%; }
        }
        @keyframes ls-shimmer {
          from { transform: translateX(-100%); }
          to { transform: translateX(350%); }
        }
        @keyframes ls-orb-a {
          0%, 100% { transform: translate(0px, 0px) scale(1); }
          33% { transform: translate(32px, -26px) scale(1.06); }
          66% { transform: translate(-18px, 18px) scale(0.96); }
        }
        @keyframes ls-orb-b {
          0%, 100% { transform: translate(0px, 0px) scale(1); }
          40% { transform: translate(-26px, 20px) scale(1.05); }
          72% { transform: translate(20px, -22px) scale(0.94); }
        }
        @keyframes ls-orb-c {
          0%, 100% { transform: translate(0px, 0px) scale(1); }
          50% { transform: translate(24px, 32px) scale(1.09); }
        }
      `}</style>

      <div style={{
        width: '100vw', height: '100vh', background: M.cream,
        display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        position: 'relative', overflow: 'hidden',
      }}>

        {/* 배경 보케 블롭 */}
        <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
          <div style={{
            position: 'absolute', width: 480, height: 480, borderRadius: '50%',
            background: 'radial-gradient(circle at center, rgba(168,85,247,0.25) 0%, transparent 68%)',
            top: '-14%', left: '-14%', filter: 'blur(58px)',
            animation: 'ls-orb-a 10s ease-in-out infinite',
          }} />
          <div style={{
            position: 'absolute', width: 380, height: 380, borderRadius: '50%',
            background: 'radial-gradient(circle at center, rgba(74,108,247,0.2) 0%, transparent 68%)',
            bottom: '-10%', right: '-8%', filter: 'blur(50px)',
            animation: 'ls-orb-b 12s ease-in-out infinite',
          }} />
          <div style={{
            position: 'absolute', width: 300, height: 300, borderRadius: '50%',
            background: 'radial-gradient(circle at center, rgba(244,63,94,0.18) 0%, transparent 68%)',
            top: '18%', right: '8%', filter: 'blur(44px)',
            animation: 'ls-orb-c 8s ease-in-out infinite',
          }} />
          <div style={{
            position: 'absolute', width: 240, height: 240, borderRadius: '50%',
            background: 'radial-gradient(circle at center, rgba(6,182,212,0.15) 0%, transparent 68%)',
            bottom: '18%', left: '6%', filter: 'blur(40px)',
            animation: 'ls-orb-a 9s ease-in-out 2.5s infinite',
          }} />
        </div>

        {/* 필름 그레인 텍스처 */}
        <div style={{
          position: 'absolute', inset: 0, pointerEvents: 'none',
          backgroundImage: `radial-gradient(circle, rgba(255,255,255,0.025) 1px, transparent 1px)`,
          backgroundSize: '3px 3px',
        }} />

        {/* 메인 콘텐츠 */}
        <div style={{
          position: 'relative', zIndex: 2,
          display: 'flex', flexDirection: 'column', alignItems: 'center',
          animation: 'ls-fade-up 0.8s cubic-bezier(0.16,1,0.3,1) both',
        }}>

          {/* 스피너 + 아이콘 */}
          <div style={{
            position: 'relative', width: 104, height: 104,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            marginBottom: 30,
          }}>
            {/* 펄스 링 */}
            <div style={{
              position: 'absolute', inset: 0, borderRadius: '50%',
              border: `1px solid ${M.rose}88`,
              animation: 'ls-pulse-ring 2.4s ease-in-out infinite',
            }} />
            {/* 회전 아크 */}
            <svg width={90} height={90} viewBox="0 0 90 90"
              style={{ position: 'absolute', animation: 'ls-rotate 1.8s linear infinite' }}>
              <circle cx={45} cy={45} r={40}
                fill="none" stroke={`${M.rose}22`} strokeWidth={1.5} />
              <circle cx={45} cy={45} r={40}
                fill="none" stroke={M.rose} strokeWidth={1.5}
                strokeLinecap="round" strokeDasharray="60 192" />
            </svg>
            {/* 중앙 아이콘 */}
            <div style={{
              width: 66, height: 66, borderRadius: '50%',
              background: M.paper,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              boxShadow: `0 2px 8px rgba(0,0,0,0.6), 0 8px 28px rgba(168,85,247,0.2)`,
              animation: 'ls-float 3.5s ease-in-out infinite',
            }}>
              <span style={{
                fontFamily: M.fontSerif, fontSize: 28,
                color: M.ink, letterSpacing: -0.5, lineHeight: 1,
              }}>M</span>
            </div>
          </div>

          {/* 브랜드 로고 */}
          <div style={{ marginBottom: 36 }}>
            <WebLogo size={60} sub />
          </div>

          {/* 프로그레스 바 */}
          <div style={{
            width: 200, height: 2, background: `${M.ink4}33`,
            borderRadius: 99, overflow: 'hidden', marginBottom: 22,
          }}>
            <div style={{
              height: '100%', borderRadius: 99,
              background: `linear-gradient(90deg, ${M.point} 0%, ${M.rose} 50%, ${M.coral} 100%)`,
              animation: 'ls-bar 3.2s cubic-bezier(0.4,0,0.2,1) both',
              position: 'relative', overflow: 'hidden',
            }}>
              <div style={{
                position: 'absolute', inset: 0,
                background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent)',
                animation: 'ls-shimmer 1.5s ease-in-out infinite',
              }} />
            </div>
          </div>

          {/* 바운싱 도트 */}
          <div style={{ display: 'flex', gap: 7, alignItems: 'center' }}>
            {[0, 1, 2].map(i => (
              <div key={i} style={{
                width: 5, height: 5, borderRadius: '50%',
                background: [M.point, M.rose, M.coral][i],
                animation: `ls-dot 1.2s ease-in-out ${i * 0.19}s infinite`,
              }} />
            ))}
          </div>
        </div>

      </div>
    </>
  );
}

// ── Auth (Login / Register) ──────────────────────────────────────────────────
function AuthScreen({ onLogin }) {
  const M = window.M;
  const [mode, setMode] = useState('login');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [name, setName] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  async function handleSubmit(e) {
    e.preventDefault();
    setError('');
    setLoading(true);
    try {
      const res = mode === 'login'
        ? await window.API.login(username, password)
        : await window.API.register(username, password, name);
      window.API.setToken(res.accessToken);
      onLogin(res.user);
    } catch (err) {
      setError(err.message || '오류가 발생했습니다.');
      setLoading(false);
    }
  }

  function switchMode() {
    setMode(m => m === 'login' ? 'register' : 'login');
    setUsername('');
    setPassword('');
    setName('');
    setError('');
    setLoading(false);
  }

  return (
    <>
      <style>{`
        @keyframes as-float {
          0%, 100% { transform: translateY(0px); }
          50%       { transform: translateY(-10px); }
        }
        @keyframes as-fade-up {
          from { opacity: 0; transform: translateY(30px); }
          to   { opacity: 1; transform: translateY(0); }
        }
        @keyframes as-shine {
          from { transform: translateX(-200%) skewX(-20deg); }
          to   { transform: translateX(300%) skewX(-20deg); }
        }
        @keyframes as-tab-slide {
          from { opacity: 0; transform: scale(0.96); }
          to   { opacity: 1; transform: scale(1); }
        }
        @keyframes as-ticker {
          from { transform: translateX(0); }
          to   { transform: translateX(-50%); }
        }
        .as-field { position: relative; width: 100%; }
        .as-field-icon {
          position: absolute; left: 18px; top: 50%;
          transform: translateY(-50%);
          font-size: 16px; pointer-events: none;
          opacity: 0.35; transition: opacity 0.18s;
        }
        .as-input {
          width: 100%; height: 56px;
          padding: 0 18px 0 50px;
          border-radius: 16px;
          border: 1.5px solid rgba(255,255,255,0.08);
          background: rgba(255,255,255,0.05);
          font-size: 15px; color: #ffffff; outline: none;
          box-sizing: border-box;
          transition: border-color 0.18s, box-shadow 0.18s;
        }
        .as-input:focus {
          border-color: #a855f7;
          box-shadow: 0 0 0 3px rgba(168,85,247,0.2);
          background: rgba(168,85,247,0.06);
        }
        .as-input:focus + .as-field-icon { opacity: 0.7; }
        .as-input::placeholder { color: rgba(255,255,255,0.25); }
        .as-tab-wrap {
          display: flex; background: rgba(255,255,255,0.04); border-radius: 14px;
          padding: 4px; gap: 4px; margin-bottom: 18px;
          border: 1px solid rgba(255,255,255,0.08);
        }
        .as-tab {
          flex: 1; height: 40px; border: none; border-radius: 11px;
          font-size: 14px; font-weight: 700; cursor: pointer;
          transition: background 0.2s, color 0.2s;
        }
        .as-tab-on  { background: linear-gradient(135deg, #a855f7, #f43f5e); color: #fff; box-shadow: 0 2px 12px rgba(168,85,247,0.4); animation: as-tab-slide 0.18s ease both; }
        .as-tab-off { background: transparent; color: rgba(255,255,255,0.3); }
        .as-tab-off:hover { color: rgba(255,255,255,0.6); }
        .as-cta {
          width: 100%; height: 58px; border: none; border-radius: 29px;
          font-size: 16px; font-weight: 700; cursor: pointer; letter-spacing: -0.2px;
          color: #fff;
          background: linear-gradient(135deg, #a855f7 0%, #f43f5e 100%);
          box-shadow: 0 4px 24px rgba(168,85,247,0.5);
          position: relative; overflow: hidden;
          transition: transform 0.15s, box-shadow 0.18s;
          margin-top: 6px;
        }
        .as-cta::after {
          content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
          background: linear-gradient(90deg, transparent, rgba(255,255,255,0.14), transparent);
          animation: as-shine 2.6s ease-in-out 0.8s infinite;
        }
        .as-cta:not(:disabled):hover { transform: translateY(-2px); box-shadow: 0 8px 32px rgba(168,85,247,0.65); }
        .as-cta:not(:disabled):active { transform: translateY(0); }
        .as-cta:disabled { opacity: 0.4; cursor: not-allowed; }
      `}</style>

      <div style={{ width: '100vw', height: '100dvh', background: M.bg, display: 'flex', flexDirection: 'column', position: 'relative', overflow: 'hidden' }}>

        {/* Ambient glow */}
        <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 0 }}>
          <div style={{ position: 'absolute', width: 700, height: 700, borderRadius: '50%', background: 'radial-gradient(circle, rgba(168,85,247,0.22) 0%, transparent 65%)', top: '-20%', left: '-15%', filter: 'blur(110px)' }} />
          <div style={{ position: 'absolute', width: 600, height: 600, borderRadius: '50%', background: 'radial-gradient(circle, rgba(244,63,94,0.15) 0%, transparent 65%)', bottom: '-20%', right: '-10%', filter: 'blur(100px)' }} />
          <div style={{ position: 'absolute', width: 400, height: 400, borderRadius: '50%', background: 'radial-gradient(circle, rgba(74,108,247,0.1) 0%, transparent 65%)', top: '40%', right: '5%', filter: 'blur(80px)' }} />
          <div style={{ position: 'absolute', inset: 0, backgroundImage: 'radial-gradient(rgba(255,255,255,0.025) 1px, transparent 1px)', backgroundSize: '28px 28px' }} />
        </div>

        {/* Top bar */}
        <div className="as-auth-topbar" style={{ position: 'relative', zIndex: 2, padding: '20px clamp(20px,5vw,48px)', display: 'flex', alignItems: 'center', flexShrink: 0, borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
          <WebLogo size={20} color="#ffffff" sub />
        </div>

        {/* 2-column main */}
        <div className="as-auth-grid" style={{ position: 'relative', zIndex: 2 }}>

          {/* LEFT — hero copy */}
          <div className="as-auth-hero" style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: 'clamp(28px,5vh,56px) clamp(24px,5vw,60px)', borderRight: '1px solid rgba(255,255,255,0.05)' }}>

            {/* Live pill */}
            <div style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '6px 14px', borderRadius: 99, background: 'rgba(244,63,94,0.1)', border: '1px solid rgba(244,63,94,0.28)', marginBottom: 28, alignSelf: 'flex-start', animation: 'as-float 3s ease-in-out infinite' }}>
              <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#f43f5e', boxShadow: '0 0 8px #f43f5e' }} />
              <span style={{ fontFamily: M.fontMono, fontSize: 10, fontWeight: 700, color: '#f43f5e', letterSpacing: 1.5, textTransform: 'uppercase' }}>오늘의 질문 · LIVE</span>
            </div>

            {/* Headline */}
            <h1 style={{ fontFamily: M.fontSerif, fontSize: 'clamp(40px, 6vw, 76px)', color: '#ffffff', margin: '0 0 24px', fontWeight: 700, letterSpacing: -1.5, lineHeight: 1.05, wordBreak: 'keep-all' }}>
              오늘,<br />
              친구들의<br />
              <span style={{ background: 'linear-gradient(135deg, #a855f7 0%, #f43f5e 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', display: 'inline-block' }}>한 장면.</span>
            </h1>

            <p style={{ fontFamily: M.fontBody, fontSize: 14, color: 'rgba(255,255,255,0.4)', margin: '0 0 28px', lineHeight: 1.7, wordBreak: 'keep-all', maxWidth: 300 }}>
              매일 하나의 질문 · 영상으로 답하기 · 친구들과 콜라주
            </p>

            {/* Badge stickers */}
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {[['📹', '매일 영상'], ['👥', '친구들과'], ['✨', '콜라주']].map(([icon, label], i) => (
                <div key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '6px 14px', borderRadius: 99, background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', fontFamily: M.fontBody, fontSize: 12, fontWeight: 600, color: 'rgba(255,255,255,0.55)', animation: `as-float ${3.6 + i * 0.7}s ${i * 0.45}s ease-in-out infinite` }}>
                  <span>{icon}</span><span>{label}</span>
                </div>
              ))}
            </div>
          </div>

          {/* RIGHT — form */}
          <div className="as-auth-form-panel" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 'clamp(24px,4vh,48px) clamp(20px,4vw,52px)', overflowY: 'auto' }}>
            {/* 모바일 전용 히어로 */}
            <div className="as-auth-mobile-hero" style={{ marginBottom: 22, width: '100%' }}>
              {/* Live pill */}
              <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 12px', borderRadius: 99, background: 'rgba(244,63,94,0.1)', border: '1px solid rgba(244,63,94,0.28)', marginBottom: 14, animation: 'as-float 3s ease-in-out infinite' }}>
                <div style={{ width: 5, height: 5, borderRadius: '50%', background: '#f43f5e', boxShadow: '0 0 7px #f43f5e' }} />
                <span style={{ fontFamily: M.fontMono, fontSize: 9, fontWeight: 700, color: '#f43f5e', letterSpacing: 1.5, textTransform: 'uppercase' }}>오늘의 질문 · LIVE</span>
              </div>
              {/* 한 줄 헤드라인 */}
              <h2 style={{ fontFamily: M.fontSerif, fontSize: 'clamp(20px,5.5vw,26px)', color: '#ffffff', margin: '0 0 12px', fontWeight: 700, letterSpacing: -0.5, lineHeight: 1.2, whiteSpace: 'nowrap' }}>
                오늘, 친구들의 <span style={{ background: 'linear-gradient(135deg, #a855f7 0%, #f43f5e 100%)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', display: 'inline' }}>한 장면.</span>
              </h2>
              <p style={{ fontFamily: M.fontBody, fontSize: 12, color: 'rgba(255,255,255,0.38)', margin: '0 0 14px', lineHeight: 1.6 }}>
                매일 하나의 질문 · 영상으로 답하기 · 친구들과 콜라주
              </p>
              {/* 뱃지 */}
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {[['📹', '매일 영상'], ['👥', '친구들과'], ['✨', '콜라주']].map(([icon, label], i) => (
                  <div key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, padding: '5px 10px', borderRadius: 99, background: 'rgba(255,255,255,0.05)', border: '1px solid rgba(255,255,255,0.1)', fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.55)', animation: `as-float ${3.6 + i * 0.7}s ${i * 0.45}s ease-in-out infinite` }}>
                    <span>{icon}</span><span>{label}</span>
                  </div>
                ))}
              </div>
            </div>

            <div style={{ width: '100%', maxWidth: 400, animation: 'as-fade-up 0.75s cubic-bezier(0.16,1,0.3,1) 0.1s both' }}>

              <div style={{ marginBottom: 22 }}>
                <div style={{ fontFamily: M.fontSerif, fontSize: 'clamp(22px,2.5vw,30px)', fontWeight: 700, color: '#fff', letterSpacing: -0.5, marginBottom: 6 }}>
                  함께 시작해요
                </div>
                <div style={{ fontFamily: M.fontBody, fontSize: 13, color: 'rgba(255,255,255,0.35)' }}>
                  {mode === 'login' ? '로그인하고 오늘의 질문에 답해보세요' : '이름, 아이디, 비밀번호를 입력해주세요'}
                </div>
              </div>

              <div className="as-tab-wrap">
                <button className={`as-tab ${mode === 'login' ? 'as-tab-on' : 'as-tab-off'}`} type="button" onClick={() => mode !== 'login' && switchMode()}>로그인</button>
                <button className={`as-tab ${mode === 'register' ? 'as-tab-on' : 'as-tab-off'}`} type="button" onClick={() => mode !== 'register' && switchMode()}>회원가입</button>
              </div>

              <form key={`fm-${mode}`} onSubmit={handleSubmit} noValidate style={{ display: 'flex', flexDirection: 'column', gap: 10, animation: 'as-tab-slide 0.28s cubic-bezier(0.16,1,0.3,1) both' }}>
                {mode === 'register' && (
                  <div className="as-field">
                    <input className="as-input" placeholder="이름" value={name} onChange={e => setName(e.target.value)} required />
                    <span className="as-field-icon">👤</span>
                  </div>
                )}
                <div className="as-field">
                  <input className="as-input" placeholder="아이디" value={username} onChange={e => setUsername(e.target.value)} autoCapitalize="none" required />
                  <span className="as-field-icon">✦</span>
                </div>
                <div className="as-field">
                  <input className="as-input" type="password" placeholder="비밀번호" value={password} onChange={e => setPassword(e.target.value)} required />
                  <span className="as-field-icon">🔑</span>
                </div>

                {error && (
                  <div style={{ fontSize: 13, color: M.coralDark, background: M.coralSoft, padding: '10px 14px', borderRadius: 12, lineHeight: 1.5, borderLeft: `3px solid ${M.coral}` }}>
                    {error}
                  </div>
                )}
                <button className="as-cta" type="submit" disabled={loading}>
                  {loading ? '처리 중...' : (mode === 'login' ? '시작하기 →' : '함께하기 →')}
                </button>
              </form>

              <div style={{ marginTop: 16, textAlign: 'center', fontFamily: M.fontBody, fontSize: 13, color: 'rgba(255,255,255,0.3)' }}>
                {mode === 'login' ? '처음이신가요? ' : '이미 계정이 있으신가요? '}
                <button type="button" onClick={switchMode} style={{ background: 'none', border: 'none', padding: 0, fontFamily: 'inherit', fontSize: 'inherit', fontWeight: 700, color: M.coral, cursor: 'pointer' }}>
                  {mode === 'login' ? '회원가입' : '로그인'}
                </button>
              </div>
            </div>
          </div>
        </div>

        {/* Bottom ticker */}
        <div className="as-auth-ticker" style={{ position: 'relative', zIndex: 2, borderTop: '1px solid rgba(255,255,255,0.05)', padding: '9px 0', overflow: 'hidden', flexShrink: 0 }}>
          <div style={{ display: 'inline-flex', gap: 48, animation: 'as-ticker 20s linear infinite', whiteSpace: 'nowrap' }}>
            {Array.from({ length: 8 }).flatMap(() => ['📹 매일 영상', '✦ 오늘의 질문', '👥 친구들과', '🎬 우리만의 콜라주', '✨ MOMENT']).map((t, i) => (
              <span key={i} style={{ fontFamily: M.fontMono, fontSize: 10, color: 'rgba(255,255,255,0.15)', letterSpacing: 2.5, textTransform: 'uppercase' }}>{t}</span>
            ))}
          </div>
        </div>

      </div>
    </>
  );
}

// ── Groups (list / create / join) ────────────────────────────────────────────
function GroupsScreen({ user, onEnterGroup, onLogout, onHome }) {
  const M = window.M;
  const [groups, setGroups] = useState([]);
  const [tab, setTab] = useState('my');
  const [loading, setLoading] = useState(true);

  // Create
  const [cName, setCName] = useState('');
  const [cCap, setCCap] = useState(4);
  const [cPublic, setCPublic] = useState(false);
  const [cLoading, setCLoading] = useState(false);
  const [cError, setCError] = useState('');
  const [createdGroup, setCreatedGroup] = useState(null);

  // Join
  const [jCode, setJCode] = useState('');
  const [jLoading, setJLoading] = useState(false);
  const [jError, setJError] = useState('');

  // Search
  const [sQuery, setSQuery] = useState('');
  const [sResults, setSResults] = useState([]);
  const [sLoading, setSLoading] = useState(false);
  const [sLoaded, setSLoaded] = useState(false);
  const [joiningPublicId, setJoiningPublicId] = useState('');
  const [publicJoinError, setPublicJoinError] = useState('');

  useEffect(() => { loadGroups(); }, []);

  async function loadGroups() {
    try { setGroups(await window.API.getGroups()); }
    finally { setLoading(false); }
  }

  async function handleCreate(e) {
    e.preventDefault();
    setCError(''); setCLoading(true);
    try {
      const g = await window.API.createGroup(cName, cCap, cPublic);
      setCreatedGroup(g);
      await loadGroups();
    } catch (err) { setCError(err.message); }
    finally { setCLoading(false); }
  }

  async function handleJoin(e) {
    e.preventDefault();
    setJError(''); setJLoading(true);
    try {
      const g = await window.API.joinGroup(jCode.trim(), '');
      onEnterGroup(g);
    } catch (err) { setJError(err.message); setJLoading(false); }
  }

  async function handleJoinPublic(groupId) {
    setPublicJoinError('');
    setJoiningPublicId(groupId);
    try {
      const g = await window.API.joinGroup('', groupId);
      await loadGroups();
      onEnterGroup(g);
    } catch (err) {
      setPublicJoinError(err.message);
    } finally {
      setJoiningPublicId('');
    }
  }

  async function loadPublicGroups(q = sQuery) {
    setSLoading(true);
    try {
      setSResults(await window.API.getPublicGroups(q));
      setSLoaded(true);
    }
    catch {}
    finally { setSLoading(false); }
  }

  async function handleSearch(q) {
    setSQuery(q);
    await loadPublicGroups(q);
  }

  const tabs = [['my', '내 그룹'], ['search', '그룹 검색'], ['create', '그룹 만들기'], ['join', '초대 코드로 참여']];

  return (
    <div style={{ width: '100vw', minHeight: '100vh', background: M.cream, position: 'relative', overflow: 'hidden' }}>
      {/* HYPE background orbs */}
      <div style={{ position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 0 }}>
        <div style={{ position: 'absolute', width: 600, height: 600, borderRadius: '50%', background: 'radial-gradient(circle, rgba(168,85,247,0.08) 0%, transparent 65%)', top: '-15%', right: '-10%', filter: 'blur(80px)' }} />
        <div style={{ position: 'absolute', width: 400, height: 400, borderRadius: '50%', background: 'radial-gradient(circle, rgba(74,108,247,0.07) 0%, transparent 65%)', bottom: '-10%', left: '-5%', filter: 'blur(70px)' }} />
      </div>
      <div style={{ position: 'relative', zIndex: 1, height: 60, borderBottom: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', padding: '0 40px', gap: 16, background: M.cream }}>
        <HomeLogo size={30} onClick={onHome} />
        <div style={{ flex: 1 }} />
        <span style={{ fontSize: 13, color: M.ink3, fontFamily: M.fontBody }}>
          <span style={{ color: M.ink, fontWeight: 700 }}>{user.name}</span>님
        </span>
        <WebButton variant="ghost" size="sm" onClick={onLogout}>로그아웃</WebButton>
      </div>

      <div style={{ maxWidth: 900, margin: '0 auto', padding: 'clamp(28px,5vw,44px) clamp(16px,5vw,40px)', position: 'relative', zIndex: 1 }}>
        <h1 style={{ fontFamily: M.fontSerif, fontSize: 'clamp(32px, 4vw, 56px)', margin: '0 0 24px', fontWeight: 700, letterSpacing: -1,
          background: `linear-gradient(135deg, #ffffff 40%, ${M.rose})`,
          WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
        }}>
          어떤 그룹?
        </h1>

        {/* Tabs */}
        <div style={{ display: 'flex', gap: 6, marginBottom: 28, overflowX: 'auto', paddingBottom: 4, WebkitOverflowScrolling: 'touch' }}>
          {tabs.map(([id, label]) => (
            <div key={id} className="groups-tab-item" onClick={() => setTab(id)} style={{
              padding: '10px 22px', borderRadius: 99, fontSize: 13, fontWeight: 700, cursor: 'pointer', flexShrink: 0,
              background: tab === id ? `linear-gradient(135deg, ${M.coral}, ${M.rose})` : M.paper,
              color: tab === id ? '#fff' : M.ink3,
              border: tab === id ? 'none' : `1px solid ${M.ink4}`,
              boxShadow: tab === id ? `0 2px 14px ${M.coral}44` : 'none',
              transition: 'background 0.15s, color 0.15s, box-shadow 0.15s',
              letterSpacing: -0.1, whiteSpace: 'nowrap',
            }}>{label}</div>
          ))}
        </div>

        {/* Search */}
        {tab === 'search' && (
          <div style={{ maxWidth: 540 }}>
            <div style={{ display:'flex', gap:8, alignItems:'center' }}>
              <input
                value={sQuery}
                onChange={e => setSQuery(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter') loadPublicGroups(sQuery); }}
                placeholder="공개 그룹 이름으로 검색..."
                style={inputSt(M, { flex:1 })}
                autoFocus
              />
              <WebButton variant="accent" size="md" onClick={() => loadPublicGroups(sQuery)} disabled={sLoading}>
                검색
              </WebButton>
              <WebButton variant="secondary" size="md" onClick={() => { setSQuery(''); loadPublicGroups(''); }} disabled={sLoading}>
                전체
              </WebButton>
            </div>
            {sLoading && (
              <div style={{ color: M.ink3, marginTop: 14, fontSize: 13, fontFamily: M.fontBody }}>검색 중...</div>
            )}
            {!sLoading && sLoaded && sResults.length === 0 && (
              <div style={{ color: M.ink3, marginTop: 14, fontSize: 13, fontFamily: M.fontBody }}>
                {sQuery.trim() ? '검색 결과가 없어요.' : '참여 가능한 공개 그룹이 없어요.'}
              </div>
            )}
            {publicJoinError && (
              <div style={{ color: M.coral, marginTop: 14, fontSize: 13, fontFamily: M.fontBody }}>
                {publicJoinError}
              </div>
            )}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 16 }}>
              {sResults.map(g => (
                <div key={g.id} onClick={() => handleJoinPublic(g.id)} style={{
                  background: M.paper, borderRadius: 12, padding: '14px 18px', cursor: 'pointer',
                  border: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', gap: 14,
                  transition: 'border-color 0.15s, transform 0.1s',
                }}
                onMouseEnter={e => { e.currentTarget.style.borderColor = M.coral + '66'; e.currentTarget.style.transform = 'translateY(-1px)'; }}
                onMouseLeave={e => { e.currentTarget.style.borderColor = M.ink4; e.currentTarget.style.transform = 'translateY(0)'; }}
                >
                  <div style={{ width:38, height:38, borderRadius:10, background:M.coralSoft, border:`1px solid ${M.coral}33`, display:'flex', alignItems:'center', justifyContent:'center', fontSize:16 }}>✦</div>
                  <div style={{ flex:1 }}>
                    <div style={{ fontSize:14, fontWeight:600, color:M.ink, letterSpacing:-0.2 }}>{g.name}</div>
                    <div style={{ fontSize:12, color:M.ink3, fontFamily:M.fontMono, marginTop:2 }}>멤버 {g.memberCount}/{g.capacity}명</div>
                  </div>
                  <div style={{ fontSize:13, color:M.coral, fontWeight:700 }}>
                    {joiningPublicId === g.id ? '참여 중...' : '참여 →'}
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* My groups */}
        {tab === 'my' && (
          loading ? (
            <div style={{ fontSize: 13, color: M.ink3, fontFamily: M.fontBody }}>불러오는 중...</div>
          ) : groups.length === 0 ? (
            <div style={{ background: M.paper, borderRadius: 16, padding: 40, textAlign: 'center', border: `1px solid ${M.ink4}`, maxWidth: 400 }}>
              <div style={{ fontSize: 28, marginBottom: 14 }}>🌸</div>
              <div style={{ fontFamily: M.fontSerif, fontSize: 20, color: M.ink, marginBottom: 6, fontWeight: 700 }}>아직 그룹이 없어요</div>
              <div style={{ fontSize: 13, color: M.ink3, marginBottom: 20, fontFamily: M.fontBody }}>그룹을 만들거나 초대 코드로 참여해보세요</div>
              <div style={{ display: 'flex', gap: 10, justifyContent: 'center' }}>
                <WebButton variant="accent" size="md" onClick={() => setTab('create')}>그룹 만들기</WebButton>
                <WebButton variant="secondary" size="md" onClick={() => setTab('join')}>코드로 참여</WebButton>
              </div>
            </div>
          ) : (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(260px, 1fr))', gap: 14 }}>
              {groups.map(g => {
                const isActive = g.status === 'Active';
                const isDone = isActive && g.todayCollageReady;
                const accentColor = isDone ? M.sage : M.coral;
                return (
                  <div key={g.id} onClick={() => onEnterGroup(g)} style={{
                    background: M.paper, borderRadius: 20, padding: 'clamp(16px,3vw,24px)', cursor: 'pointer',
                    border: `1px solid ${isActive ? accentColor + '55' : M.ink4}`,
                    transition: 'border-color 0.15s, transform 0.12s, box-shadow 0.12s',
                    position: 'relative', overflow: 'hidden',
                  }}
                  onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-3px)'; e.currentTarget.style.boxShadow = `0 8px 28px rgba(0,0,0,0.5), 0 0 0 1px ${accentColor}44`; }}
                  onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'none'; }}
                  >
                    {/* gradient top bar */}
                    {isActive && (
                      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 3,
                        background: isDone
                          ? `linear-gradient(90deg, ${M.sage}, ${M.point})`
                          : `linear-gradient(90deg, ${M.coral}, ${M.rose})`,
                        borderRadius: '16px 16px 0 0' }} />
                    )}
                    <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
                      <div style={{
                        width: 40, height: 40, borderRadius: 12,
                        background: isDone
                          ? M.sageSoft
                          : isActive ? `linear-gradient(135deg, ${M.coralSoft}, ${M.roseSoft})` : M.ivory,
                        display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18,
                        border: `1px solid ${isActive ? accentColor + '44' : M.ink4}`,
                      }}>
                        {isDone ? '🎬' : isActive ? (g.isPublic ? '🌐' : '✦') : '⏳'}
                      </div>
                      <div>
                        <div style={{ fontSize: 15, fontWeight: 700, color: M.ink, letterSpacing: -0.2 }}>{g.name}</div>
                        <WebChip
                          bg={!isActive ? M.sunSoft : isDone ? M.sageSoft : M.coralSoft}
                          color={!isActive ? M.sun : isDone ? M.sage : M.coral}
                          style={{ marginTop: 4 }}
                        >
                          {!isActive ? '모집 중' : isDone ? '오늘 영상 완성 ✓' : '진행 중'}
                        </WebChip>
                      </div>
                    </div>
                    <div style={{ fontFamily: M.fontMono, fontSize: 13, color: M.ink3 }}>
                      {g.memberCount} / {g.capacity}명
                    </div>
                    {!isActive && (
                      <div style={{ marginTop: 5, fontSize: 12, color: M.ink3, fontFamily: M.fontBody }}>
                        {g.capacity - g.memberCount}명 더 모이면 시작돼요
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          )
        )}

        {/* Create group */}
        {tab === 'create' && (
          <div style={{ maxWidth: 480, background: M.paper, borderRadius: 22, padding: 'clamp(20px,4vw,32px)', border: `1px solid ${M.ink4}` }}>
            {createdGroup ? (
              <div>
                {createdGroup.isPublic ? (
                  <>
                    <LabelBadge variant="coral" mb={10}>공개 그룹</LabelBadge>
                    <div style={{ fontFamily: M.fontSerif, fontSize: 26, fontWeight: 700, color: M.ink, lineHeight: 1.25, letterSpacing: -0.6 }}>
                      검색으로 바로 참여할 수 있어요
                    </div>
                  </>
                ) : (
                  <>
                    <LabelBadge variant="coral" mb={10}>초대 코드</LabelBadge>
                    <div style={{
                      fontFamily: M.fontMono, fontSize: 52, fontWeight: 700, letterSpacing: 6, lineHeight: 1,
                      background: `linear-gradient(135deg, ${M.coral}, ${M.rose})`,
                      WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
                    }}>
                      {createdGroup.inviteCode.slice(0, 3)} · {createdGroup.inviteCode.slice(3)}
                    </div>
                  </>
                )}
                <div style={{ fontFamily: M.fontDodum, fontSize: 15, color: M.ink2, marginTop: 10, lineHeight: 1.6 }}>"{createdGroup.name}"</div>
                <div style={{ marginTop: 20, display: 'flex', gap: 10 }}>
                  <WebButton variant="accent" size="lg" onClick={() => onEnterGroup(createdGroup)}>그룹으로 이동</WebButton>
                  <WebButton variant="secondary" size="lg" onClick={() => { setCreatedGroup(null); setCName(''); }}>새 그룹</WebButton>
                </div>
              </div>
            ) : (
              <form onSubmit={handleCreate} noValidate style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
                <h3 style={{ fontFamily: M.fontSerif, fontSize: 24, color: M.ink, margin: 0, fontWeight: 700, letterSpacing: -0.6 }}>새 그룹 만들기</h3>

                <div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: M.ink2, marginBottom: 7, textTransform: 'uppercase', letterSpacing: 0.5, fontFamily: M.fontMono }}>그룹 이름</div>
                  <input
                    placeholder="우리만의 이름을 지어보세요"
                    value={cName} onChange={e => setCName(e.target.value)}
                    required style={inputSt(M)}
                  />
                </div>

                <div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: M.ink2, marginBottom: 7, textTransform: 'uppercase', letterSpacing: 0.5, fontFamily: M.fontMono }}>멤버 수</div>
                  <div style={{ display: 'flex', gap: 6 }}>
                    {[2, 3, 4].map(n => (
                      <div key={n} onClick={() => setCCap(n)} style={{
                        width: 52, height: 52, borderRadius: 26, cursor: 'pointer',
                        background: cCap === n ? `linear-gradient(135deg, ${M.coral}, ${M.rose})` : M.ivory,
                        color: cCap === n ? '#fff' : M.ink2,
                        border: cCap === n ? 'none' : `1px solid ${M.ink4}`,
                        boxShadow: cCap === n ? `0 2px 12px ${M.coral}44` : 'none',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontFamily: M.fontSerif, fontSize: 16, fontWeight: 700,
                        transition: 'background 0.15s',
                      }}>{n}</div>
                    ))}
                  </div>
                </div>

                <label style={{ display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer' }}>
                  <input type="checkbox" checked={cPublic} onChange={e => setCPublic(e.target.checked)} style={{ width: 14, height: 14, accentColor: M.coral }} />
                  <span style={{ fontSize: 14, color: M.ink2, fontFamily: M.fontBody }}>공개 그룹으로 만들기</span>
                </label>

                {cError && (
                  <div style={{ fontSize: 13, color: M.coral, background: M.coralSoft, padding: '10px 14px', borderRadius: 9, borderLeft: `3px solid ${M.coral}` }}>{cError}</div>
                )}
                <WebButton variant="accent" size="lg" full disabled={cLoading}>
                  {cLoading ? '생성 중...' : '그룹 만들기'}
                </WebButton>
              </form>
            )}
          </div>
        )}

        {/* Join group */}
        {tab === 'join' && (
          <div style={{ maxWidth: 480, background: M.paper, borderRadius: 22, padding: 'clamp(20px,4vw,32px)', border: `1px solid ${M.ink4}` }}>
            <h3 style={{ fontFamily: M.fontSerif, fontSize: 24, color: M.ink, margin: '0 0 20px', fontWeight: 700, letterSpacing: -0.6 }}>초대 코드로 참여</h3>
            <form onSubmit={handleJoin} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              <input
                placeholder="6자리 초대 코드"
                value={jCode}
                onChange={e => setJCode(e.target.value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase().slice(0, 6))}
                onPaste={e => {
                  e.preventDefault();
                  const text = (e.clipboardData || window.clipboardData).getData('text');
                  setJCode(text.replace(/[^a-zA-Z0-9]/g, '').toUpperCase().slice(0, 6));
                }}
                required maxLength={6}
                autoCapitalize="characters" autoCorrect="off" autoComplete="off" spellCheck="false"
                inputMode="text"
                style={inputSt(M, { fontFamily: M.fontMono, fontSize: 24, letterSpacing: 8, textAlign: 'center', height: 66, borderRadius: 20 })}
              />
              {jError && (
                <div style={{ fontSize: 13, color: M.coral, background: M.coralSoft, padding: '10px 14px', borderRadius: 9, borderLeft: `3px solid ${M.coral}` }}>{jError}</div>
              )}
              <WebButton variant="accent" size="lg" full disabled={jLoading}>
                {jLoading ? '참여 중...' : '참여하기'}
              </WebButton>
            </form>
          </div>
        )}
      </div>
    </div>
  );
}

// ── Waiting Room ─────────────────────────────────────────────────────────────
function WaitingScreen({ group, user, onGroupActivated, onLogout, onChangeGroup, onHome }) {
  const M = window.M;
  const [members, setMembers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [copied, setCopied] = useState(false);

  useEffect(() => {
    loadDetail();
    const interval = setInterval(async () => {
      try {
        const detail = await window.API.getGroup(group.id);
        setMembers(detail.members);
        if (detail.group.status === 'Active') onGroupActivated();
      } catch (e) {}
    }, 5000);
    return () => clearInterval(interval);
  }, [group.id]);

  async function loadDetail() {
    try {
      const detail = await window.API.getGroup(group.id);
      setMembers(detail.members);
      if (detail.group.status === 'Active') onGroupActivated();
    } finally { setLoading(false); }
  }

  function copyCode() {
    navigator.clipboard.writeText(group.inviteCode).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  }

  return (
    <div style={{ width: '100vw', height: '100vh', background: M.cream, display: 'flex', flexDirection: 'column', position: 'relative', overflow: 'hidden' }}>
      {/* HYPE orbs */}
      <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 0 }}>
        <div style={{ position: 'absolute', width: 500, height: 500, borderRadius: '50%', background: 'radial-gradient(circle, rgba(168,85,247,0.1) 0%, transparent 65%)', top: '-15%', right: '-15%', filter: 'blur(80px)' }} />
        <div style={{ position: 'absolute', width: 350, height: 350, borderRadius: '50%', background: 'radial-gradient(circle, rgba(244,63,94,0.08) 0%, transparent 65%)', bottom: '-10%', left: '-10%', filter: 'blur(70px)' }} />
      </div>
      <div style={{ position: 'relative', zIndex: 1, height: 56, borderBottom: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', padding: '0 clamp(14px,4vw,40px)', gap: 12, background: M.cream }}>
        <HomeLogo size={26} onClick={onHome} />
        <div style={{ flex: 1 }} />
        <WebButton variant="ghost" size="sm" onClick={onChangeGroup}>그룹 목록</WebButton>
        <WebButton variant="ghost" size="sm" onClick={onLogout}>로그아웃</WebButton>
      </div>

      <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 'clamp(20px,5vw,40px)', position: 'relative', zIndex: 1 }}>
        <div style={{ maxWidth: 520, width: '100%' }}>
          <LabelBadge variant="coral" mb={10}>멤버 모집 중</LabelBadge>
          <h1 style={{
            fontFamily: M.fontSerif, fontSize: 'clamp(38px, 5vw, 64px)', margin: '0 0 24px', fontWeight: 700, letterSpacing: -1,
            background: `linear-gradient(135deg, #ffffff 30%, ${M.rose})`,
            WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
          }}>
            {group.name}
          </h1>

          <div style={{ background: M.paper, borderRadius: 22, padding: 'clamp(18px,4vw,28px)', marginBottom: 16, border: `1px solid ${M.ink4}`, position: 'relative', overflow: 'hidden' }}>
            <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 3,
              background: `linear-gradient(90deg, ${M.coral}, ${M.rose}, ${M.sun})` }} />
            {group.isPublic ? (
              <>
                <LabelBadge variant="neutral" mb={10}>Public Group</LabelBadge>
                <div style={{ fontSize: 15, color: M.ink, lineHeight: 1.7, fontFamily: M.fontBody }}>
                  공개 그룹 검색에서 코드 없이 참여할 수 있어요.
                </div>
              </>
            ) : (
              <>
                <LabelBadge variant="neutral" mb={12}>Invite Code</LabelBadge>
                <div style={{
                  fontFamily: M.fontMono, fontSize: 52, fontWeight: 700, letterSpacing: 6, lineHeight: 1,
                  background: `linear-gradient(135deg, ${M.coral}, ${M.rose})`,
                  WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
                }}>
                  {group.inviteCode?.slice(0, 3)} · {group.inviteCode?.slice(3)}
                </div>
                <WebButton variant="secondary" size="sm" onClick={copyCode} style={{ marginTop: 16 }}>
                  {copied ? '복사됨 ✓' : '코드 복사'}
                </WebButton>
              </>
            )}
          </div>

          <div style={{ background: M.paper, borderRadius: 22, padding: 'clamp(18px,4vw,28px)', border: `1px solid ${M.ink4}` }}>
            <LabelBadge variant="neutral" mb={16}>참여 현황 {loading ? '' : `${members.length}/${group.capacity}`}</LabelBadge>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10 }}>
              {Array.from({ length: group.capacity }).map((_, i) => {
                const m = members[i];
                return (
                  <div key={i} style={{
                    display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                    gap: 10, padding: '20px 14px', borderRadius: 16, textAlign: 'center',
                    background: m ? M.ivory : 'transparent',
                    border: m ? `1px solid ${M.ink4}` : `1px dashed ${M.ink4}66`,
                    minHeight: 110,
                  }}>
                    {m ? (
                      <MemberAvatar userId={m.userId} name={m.name} size={48} />
                    ) : (
                      <div style={{ width: 48, height: 48, borderRadius: 24, background: M.ivory, border: `1px dashed ${M.ink4}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 20, color: M.ink4 }}>?</div>
                    )}
                    <div>
                      <div style={{ fontSize: 14, fontWeight: 700, color: m ? M.ink : M.ink3 }}>
                        {m ? m.name + (m.userId === user.id ? ' (나)' : '') : '대기 중...'}
                      </div>
                      {m?.role === 'Owner' && (
                        <div style={{ fontSize: 11, color: M.sage, fontFamily: M.fontMono, marginTop: 2 }}>HOST</div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Dashboard ────────────────────────────────────────────────────────────────
function DashboardScreen({ group, user, onUpload, onViewCollage, onVoteTemplate, onViewTimeline, onChangeGroup, onLogout, onHome }) {
  const M = window.M;
  const [dashboard, setDashboard] = useState(null);
  const [loading, setLoading] = useState(true);
  const [countdown, setCountdown] = useState('');
  const [hoveredReaction, setHoveredReaction] = useState(null); // "memberId-type"

  useEffect(() => {
    loadDashboard();
    const interval = setInterval(loadDashboard, 1500);
    return () => clearInterval(interval);
  }, [group.id]);

  useEffect(() => {
    const timer = setInterval(() => {
      const now = new Date();
      const midnight = new Date(now);
      midnight.setHours(23, 59, 0, 0);
      setCountdown(fmtCountdown(midnight - now));
    }, 1000);
    return () => clearInterval(timer);
  }, []);

  async function loadDashboard() {
    try {
      setDashboard(await window.API.getDashboard(group.id));
    } catch (e) {
      console.error('Dashboard error:', e);
    } finally { setLoading(false); }
  }

  if (loading) return <LoadingScreen />;
  if (!dashboard) return <LoadingScreen />;

  const { todayQuestion, members, canUpload, isComplete, collage } = dashboard;
  const uploaded = members.filter(m => m.upload).length;
  const myCard = members.find(m => m.userId === user.id);
  const hasMyUpload = myCard?.upload != null;

  const nav = [['오늘', null], ['타임라인', onViewTimeline], ['그룹', onChangeGroup]];
  const reactionOptions = [
    ['heart', '😍'],
    ['laugh', '😂'],
    ['moved', '🥹'],
    ['spark', '🤩'],
  ];

  async function handleReaction(member, reactionType) {
    if (!member.upload) return;
    try {
      await window.API.reactToUpload(group.id, member.userId, reactionType);
      await loadDashboard();
    } catch (e) {
      console.error('Reaction error:', e);
    }
  }

  return (
    <div style={{ width: '100vw', height: '100vh', background: M.cream, display: 'flex', flexDirection: 'column', position: 'relative', overflow: 'hidden' }}>
      {/* Header */}
      <div className="dash-header" style={{ height: 56, borderBottom: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', padding: '0 clamp(14px,4vw,28px)', gap: 16, flexShrink: 0, background: M.cream, overflow: 'hidden' }}>
        <HomeLogo size={26} onClick={onHome} />
        {/* Nav tabs */}
        <div style={{ display: 'flex', gap: 2, marginLeft: 8, flexShrink: 0 }}>
          {[
            { label: '오늘', active: true, handler: null },
            { label: '타임라인', active: false, handler: onViewTimeline },
            { label: '그룹', active: false, handler: onChangeGroup },
          ].map(({ label, active, handler }) => (
            <div key={label} className="dash-nav-tab" onClick={handler || undefined} style={{
              padding: '6px 14px', borderRadius: 8, fontSize: 13, fontWeight: 600,
              fontFamily: M.fontBody, cursor: handler ? 'pointer' : 'default',
              color: active ? M.ink : M.ink3,
              background: active ? M.ivory : 'transparent',
              border: active ? `1px solid ${M.ink4}` : '1px solid transparent',
              transition: 'color 0.15s, background 0.15s', whiteSpace: 'nowrap',
            }}>{label}</div>
          ))}
        </div>
        <div style={{ flex: 1, minWidth: 0 }} />
        <div className="dash-group-badge" style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '4px 10px 4px 8px', borderRadius: 20, background: M.ivory, border: `1px solid ${M.ink4}`, flexShrink: 0 }}>
          <span style={{ fontSize: 12, flexShrink: 0 }}>🌸</span>
          <span className="dash-group-name" style={{ fontSize: 12, fontWeight: 600, color: M.ink2 }}>{group.name}</span>
        </div>
        <button className="dash-logout-btn" onClick={onLogout} style={{
          background: 'none', border: 'none', padding: '4px 8px', borderRadius: 6,
          fontSize: 12, color: M.ink3, cursor: 'pointer', fontFamily: M.fontBody,
          transition: 'color 0.15s', flexShrink: 0, whiteSpace: 'nowrap',
        }}>로그아웃</button>
      </div>

      {/* Banner */}
      <div style={{ padding: 'clamp(14px,3vw,20px) clamp(14px,4vw,40px) 12px', flexShrink: 0, borderBottom: `1px solid ${M.ink4}`, position: 'relative',
        background: `linear-gradient(180deg, rgba(168,85,247,0.05) 0%, transparent 100%)`,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <div style={{ width: 6, height: 6, borderRadius: '50%', background: M.coral, boxShadow: `0 0 6px 2px ${M.coral}88`, flexShrink: 0 }} />
            <span style={{
              fontFamily: M.fontMono, fontSize: 12, fontWeight: 700, letterSpacing: 0.5,
              color: M.ink,
              background: M.ivory, border: `1px solid ${M.ink4}`,
              padding: '3px 10px', borderRadius: 6,
            }}>
              {new Date().toLocaleDateString('ko-KR', { month: 'long', day: 'numeric', weekday: 'short' })}
            </span>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{
              fontFamily: M.fontMono, fontSize: 18, fontWeight: 700, letterSpacing: 1,
              background: `linear-gradient(90deg, ${M.coral}, ${M.rose})`,
              WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
            }}>{countdown}</div>
            <div style={{
              fontSize: 13, fontWeight: 700, fontFamily: M.fontMono,
              color: M.ink,
              background: M.ivory, border: `1px solid ${M.ink4}`,
              padding: '3px 10px', borderRadius: 6,
            }}>
              {uploaded}/{members.length}명
            </div>
          </div>
        </div>
        <h1 style={{
          fontFamily: M.fontSerif, fontSize: 'clamp(20px, 3vw, 32px)',
          color: M.ink, margin: 0, fontWeight: 700, letterSpacing: -0.5, lineHeight: 1.25,
          wordBreak: 'keep-all', maxWidth: 720,
        }}>
          {todayQuestion.text}
        </h1>
      </div>

      {/* Progress bar */}
      <div style={{ padding: '10px clamp(14px,4vw,40px)', flexShrink: 0 }}>
        <div style={{ height: 3, background: M.ivory, borderRadius: 2, display: 'flex', gap: 3, border: `1px solid ${M.ink4}` }}>
          {members.map((m) => (
            <div key={m.userId} style={{
              flex: 1, borderRadius: 2,
              background: m.upload ? `linear-gradient(90deg, ${M.coral}, ${M.rose})` : 'transparent',
              transition: 'background 0.4s',
            }} />
          ))}
        </div>
      </div>

      {/* Member cards */}
      <div className="dash-member-grid" style={{
        flex: 1, padding: 'clamp(10px,2vw,16px) clamp(12px,4vw,40px) 16px', overflowY: 'auto', overflowX: 'hidden',
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fill, minmax(min(160px, 100%), 1fr))',
        gap: 14,
        alignContent: 'start',
      }}>
        {members.map(m => {
          const isSelf = m.userId === user.id;
          return (
            <div key={m.userId} style={{
              background: M.paper, borderRadius: 18, overflow: 'hidden',
              display: 'flex', flexDirection: 'column', minWidth: 0,
              border: isSelf ? `1.5px solid ${M.coral}` : `1px solid ${M.ink4}`,
              boxShadow: isSelf ? `0 0 0 2px ${M.rose}33, 0 4px 16px ${M.coral}22` : '0 2px 8px rgba(0,0,0,0.2)',
            }}>
              <div style={{ padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 8 }}>
                <MemberAvatar userId={m.userId} name={m.name} size={26} />
                <div style={{
                  flex: 1, minWidth: 0, fontSize: 13, fontWeight: 600, color: M.ink,
                  overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  letterSpacing: -0.2,
                }}>
                  {m.name}{isSelf && <span style={{ fontSize: 12, color: M.ink3, fontWeight: 500 }}> (나)</span>}
                </div>
                {m.upload
                  ? <WebChip bg={M.sageSoft} color={M.sage}>✓</WebChip>
                  : <WebChip bg={M.ivory} color={M.ink3}>대기</WebChip>
                }
              </div>
              {m.upload ? (
                <>
                  <div style={{
                    aspectRatio: '9/16', overflow: 'hidden', background: '#000',
                    border: `2px solid ${M.coral}`,
                    boxShadow: `0 0 10px ${M.coral}99, 0 0 28px ${M.rose}55`,
                  }}>
                    <video
                      src={m.upload.url}
                      muted playsInline loop autoPlay
                      style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
                    />
                  </div>
                  {m.upload.caption ? (
                    <div style={{ padding: '8px 12px 10px', fontFamily: M.fontHand, fontSize: 13, color: M.ink2, lineHeight: 1.4 }}>
                      "{m.upload.caption}"
                    </div>
                  ) : null}
                  <div style={{
                    padding: '0 10px 12px',
                    display: 'grid',
                    gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
                    gap: 5,
                  }}>
                    {reactionOptions.map(([type, label]) => {
                      const reactions = m.reactions || [];
                      const count = reactions.filter(r => r.reactionType === type).length;
                      const active = reactions.some(r => r.userId === user.id && r.reactionType === type);
                      const names = reactions
                        .filter(r => r.reactionType === type)
                        .map(r => r.userId === user.id ? '나' : r.name)
                        .join(', ');
                      const hoverKey = `${m.userId}-${type}`;
                      const isHovered = hoveredReaction === hoverKey;
                      const showCancel = active && isHovered;
                      return (
                        <button
                          key={type}
                          type="button"
                          title={active ? '취소하려면 클릭' : (names || label)}
                          onClick={() => handleReaction(m, type)}
                          onMouseEnter={() => setHoveredReaction(hoverKey)}
                          onMouseLeave={() => setHoveredReaction(null)}
                          style={{
                            height: 32,
                            borderRadius: 16,
                            border: showCancel
                              ? `1px solid rgba(255,80,80,0.7)`
                              : active ? `1px solid ${M.coral}` : `1px solid ${M.ink4}`,
                            background: showCancel
                              ? 'rgba(255,60,60,0.12)'
                              : active ? M.coralSoft : M.ivory,
                            color: showCancel ? '#ff6060' : active ? M.coral : M.ink3,
                            fontSize: showCancel ? 11 : 14,
                            fontWeight: 600,
                            cursor: 'pointer',
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                            gap: 3,
                            padding: '0 4px',
                            minWidth: 0,
                            transition: 'background 0.12s, border-color 0.12s, color 0.12s',
                          }}
                        >
                          <span>{showCancel ? '✕' : label}</span>
                          {!showCancel && count > 0 && <span style={{ fontFamily: M.fontMono, fontSize: 11 }}>{count}</span>}
                        </button>
                      );
                    })}
                  </div>
                </>
              ) : (
                <div style={{
                  aspectRatio: 1, background: M.bg,
                  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: M.ink3,
                  gap: 8,
                }}>
                  <div style={{ fontSize: 24, opacity: 0.3 }}>○</div>
                  <div style={{ fontFamily: M.fontBody, fontSize: 12, color: M.ink3 }}>아직 안 올렸어요</div>
                </div>
              )}
            </div>
          );
        })}
      </div>

      {/* Bottom: action CTA */}
      <div style={{ flexShrink: 0, borderTop: `1px solid ${M.ink4}`, background: M.paper, padding: '12px clamp(12px,4vw,28px)' }}>
        {collage ? (
          <WebButton variant="accent" size="md" full onClick={onViewCollage} leading={<span>▶</span>}>
            오늘의 영상 보기
          </WebButton>
        ) : isComplete ? (
          <WebButton variant="accent" size="md" full onClick={onVoteTemplate} leading={<span>✦</span>}>
            템플릿 투표하기
          </WebButton>
        ) : canUpload && !hasMyUpload ? (
          <WebButton variant="accent" size="md" full onClick={onUpload} leading={<span>+</span>}>
            오늘의 영상 올리기
          </WebButton>
        ) : hasMyUpload && !isComplete ? (
          <div style={{ display:'flex', alignItems:'center', gap:10, padding:'12px 18px', borderRadius:12, background:M.sageSoft, border:`1px solid ${M.sage}44` }}>
            <span style={{ fontSize:14 }}>✓</span>
            <div style={{ fontSize:13, fontWeight:600, color:M.sage }}>업로드 완료 · 다른 멤버 기다리는 중</div>
          </div>
        ) : null}
      </div>
    </div>
  );
}

// ── Upload ───────────────────────────────────────────────────────────────────
function UploadScreen({ group, user, onDone, onBack, onHome }) {
  const M = window.M;
  const [question, setQuestion] = useState(null);
  const [file, setFile]         = useState(null);
  const [previewUrl, setPreviewUrl] = useState(null);
  const [duration, setDuration] = useState(null);
  const [caption, setCaption]   = useState('');
  const [loading, setLoading]   = useState(false);
  const [error, setError]       = useState('');
  const fileRef = useRef(null);

  useEffect(() => {
    window.API.getTodayQuestion().then(setQuestion).catch(() => {});
  }, []);

  function handleFileChange(e) {
    const f = e.target.files?.[0];
    if (!f) return;
    if (!f.type.startsWith('video/')) {
      setError('영상 파일(MP4, MOV, WEBM)을 선택해주세요.');
      return;
    }
    setError('');
    setFile(f);
    const url = URL.createObjectURL(f);
    setPreviewUrl(url);
    setDuration(null);
  }

  function handleDrop(e) {
    e.preventDefault();
    const f = e.dataTransfer.files?.[0];
    if (f && f.type.startsWith('video/')) {
      setError('');
      setFile(f);
      setPreviewUrl(URL.createObjectURL(f));
      setDuration(null);
    }
  }

  function handleVideoMeta(e) {
    const d = e.target.duration;
    setDuration(d);
    if (d > 10) setError('영상이 10초를 초과합니다. 앞 5초만 사용됩니다.');
    else setError('');
  }

  async function handleSubmit() {
    if (!file) { setError('영상을 선택해주세요.'); return; }
    setError(''); setLoading(true);
    try {
      const result = await window.API.upload(group.id, file, caption);
      onDone(result);
    } catch (err) {
      setError(err.message);
      setLoading(false);
    }
  }

  const durationLabel = duration != null
    ? `${duration.toFixed(1)}초 ${duration > 5 ? '(앞 5초 사용)' : ''}`
    : null;

  return (
    <div style={{ width:'100vw', height:'100vh', background:M.cream, display:'flex', flexDirection:'column' }}>
      <div style={{ height:56, borderBottom:`1px solid ${M.ink4}`, display:'flex', alignItems:'center', padding:'0 clamp(14px,4vw,32px)', gap:10, background:M.cream }}>
        <HomeLogo size={22} onClick={onHome} />
        <span style={{ color:M.ink4, fontSize:14 }}>/</span>
        <span style={{ fontSize:13, fontWeight:600, color:M.ink2, fontFamily:M.fontBody }}>영상 올리기</span>
      </div>

      <div className="upload-main-grid" style={{ flex:1, display:'grid', gridTemplateColumns:'minmax(0,1.2fr) minmax(0,1fr)', padding:'clamp(16px,3vw,28px) clamp(14px,4vw,40px) clamp(20px,4vw,40px)', gap:'clamp(16px,3vw,28px)', overflow:'auto' }}>
        {/* Left — drop/preview */}
        <div style={{ display:'flex', flexDirection:'column' }}>
          <div style={{ display:'flex', alignItems:'center', gap:8, marginBottom:18, flexWrap:'wrap' }}>
            <LabelBadge variant="coral" mb={0}>오늘의 질문</LabelBadge>
            <span style={{ fontSize:13, color:M.ink, wordBreak:'keep-all', fontFamily:M.fontBody }}>
              {question ? `"${question.text}"` : '불러오는 중...'}
            </span>
          </div>

          <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center' }}>
            {previewUrl ? (
              <div style={{
                background:M.paper, padding:'12px 12px 48px',
                borderRadius:4, boxShadow:M.shadowFloat,
                transform:'rotate(-1.5deg)', width:320, position:'relative',
                border:`1px solid ${M.ink4}`,
              }}>
                <video
                  src={previewUrl}
                  onLoadedMetadata={handleVideoMeta}
                  controls muted playsInline
                  style={{ width:'100%', aspectRatio:'9/16', objectFit:'cover', display:'block', background:'#000', borderRadius:2 }}
                />
                <div style={{
                  position:'absolute', bottom:10, left:0, right:0, textAlign:'center',
                  fontFamily:M.fontHand, fontSize:13, color:M.ink2,
                }}>
                  {caption || '한마디를 입력해보세요...'}
                </div>
                {durationLabel && (
                  <div style={{
                    position:'absolute', top:8, right:8,
                    background:'rgba(0,0,0,0.75)', color:'#fff',
                    fontSize:12, padding:'2px 8px', borderRadius:6, fontFamily:M.fontMono,
                  }}>{durationLabel}</div>
                )}
              </div>
            ) : (
              <div
                onDrop={handleDrop}
                onDragOver={e => e.preventDefault()}
                onClick={() => fileRef.current?.click()}
                style={{
                  width:320, aspectRatio:'9/16', background:M.paper,
                  border:`1.5px dashed ${M.ink4}`, borderRadius:14,
                  display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
                  cursor:'pointer', gap:12, transition:'border-color 0.15s',
                }}
                onMouseEnter={e => { e.currentTarget.style.borderColor = M.coral; e.currentTarget.style.boxShadow = `0 0 20px ${M.rose}22`; }}
                onMouseLeave={e => { e.currentTarget.style.borderColor = M.ink4; e.currentTarget.style.boxShadow = 'none'; }}
              >
                <div style={{ fontSize:36 }}>🎬</div>
                <div style={{ fontFamily:M.fontBody, fontSize:13, color:M.ink2, textAlign:'center', lineHeight:1.6, wordBreak:'keep-all' }}>
                  영상을 드래그하거나<br/>클릭해서 선택하세요
                </div>
                <div style={{ padding:'5px 14px', borderRadius:8, background:M.coralSoft, border:`1px solid ${M.coral}33`, fontSize:12, color:M.coral, fontFamily:M.fontBody }}>
                  권장 5초 이내
                </div>
              </div>
            )}
          </div>
          <input ref={fileRef} type="file" accept="video/*" onChange={handleFileChange} style={{ display:'none' }} />

          {previewUrl && (
            <div style={{ marginTop:14 }}>
              <WebButton variant="secondary" size="md" onClick={() => fileRef.current?.click()}>다른 영상 고르기</WebButton>
            </div>
          )}
        </div>

        {/* Right — caption + submit */}
        <div style={{ background:M.paper, borderRadius:22, padding:'clamp(18px,3vw,28px)', display:'flex', flexDirection:'column', border:`1px solid ${M.ink4}` }}>
          <h2 style={{ fontFamily:M.fontSerif, fontSize:'clamp(22px,2.8vw,30px)', color:M.ink, margin:'0 0 18px', fontWeight:700, letterSpacing:-0.3 }}>
            오늘 한 줄
          </h2>

          <div style={{ marginBottom:20 }}>
            <div style={{ fontSize:12, fontWeight:600, color:M.ink2, marginBottom:8, textTransform:'uppercase', letterSpacing:0.5, fontFamily:M.fontMono }}>한마디</div>
            <textarea
              value={caption} onChange={e => setCaption(e.target.value.slice(0, 10))}
              placeholder="오늘의 순간을 한마디로 ✦"
              style={{
                width:'100%', minHeight:80, padding:14, borderRadius:14,
                border:`1.5px solid ${M.ink4}`, background:M.ivory,
                fontFamily:M.fontDodum, fontSize:14, color:M.ink, outline:'none',
                resize:'none', lineHeight:1.6, boxSizing:'border-box',
                transition: 'border-color 0.18s',
              }}
            />
            <div style={{ textAlign:'right', fontSize:12, color:M.ink3, fontFamily:M.fontMono, marginTop:4 }}>
              {caption.length} / 10
            </div>
          </div>

          <div style={{ flex:1 }} />

          {error && (
            <div style={{ fontSize:13, color:M.coral, background:M.coralSoft, padding:'10px 14px', borderRadius:10, marginBottom:14, borderLeft:`3px solid ${M.coral}` }}>{error}</div>
          )}

          <div style={{ display:'flex', gap:10 }}>
            <WebButton variant="secondary" size="lg" full onClick={onBack}>취소</WebButton>
            <WebButton variant="accent" size="lg" full onClick={handleSubmit} disabled={loading || !file}>
              {loading ? '업로드 중...' : '완료'}
            </WebButton>
          </div>
        </div>
      </div>
    </div>
  );
}

function BrowserVideoCollage({ collage, members, group, maxWidth = 460 }) {
  const templates = getTemplates(group.capacity);
  const template = templates.find(t => t.id === collage?.templateId) || templates[0];
  const videos = (members || []).filter(m => m.upload);

  if (!template || videos.length === 0) return null;

  return (
    <div style={{
      width: '100%',
      maxWidth,
      aspectRatio: `${template.cw} / ${template.ch}`,
      position: 'relative',
      background: '#0a0a12',
      overflow: 'hidden',
      boxShadow: window.M.shadowFloat,
      borderRadius: 8,
    }}>
      {videos.slice(0, template.slots.length).map((member, index) => {
        const slot = template.slots[index];
        const borderColor = SLOT_COLORS[index % SLOT_COLORS.length];
        return (
          <React.Fragment key={member.userId}>
            <video
              src={member.upload.url}
              controls
              muted
              autoPlay
              loop
              playsInline
              style={{
                position: 'absolute',
                left: `${slot.x / template.cw * 100}%`,
                top: `${slot.y / template.ch * 100}%`,
                width: `${slot.w / template.cw * 100}%`,
                height: `${slot.h / template.ch * 100}%`,
                objectFit: 'cover',
                background: '#0a0a12',
              }}
            />
            <div style={{
              position: 'absolute',
              left: `${slot.x / template.cw * 100}%`,
              top: `${slot.y / template.ch * 100}%`,
              width: `${slot.w / template.cw * 100}%`,
              height: `${slot.h / template.ch * 100}%`,
              border: `4px solid ${borderColor}`,
              boxShadow: 'inset 0 0 0 1px rgba(24,8,16,0.28)',
              boxSizing: 'border-box',
              pointerEvents: 'none',
            }} />
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ── Collage ──────────────────────────────────────────────────────────────────
function CollageScreen({ group, user, collageData, onBack, onHome }) {
  const M = window.M;
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [mediaError, setMediaError] = useState('');
  const [regenerating, setRegenerating] = useState(false);
  const [shareLoading, setShareLoading] = useState(false);
  const [shareGuide, setShareGuide] = useState(false);

  useEffect(() => {
    let cancelled = false;
    window.API.getDashboard(group.id).then(d => {
      const collage = collageData?.url ? collageData : d.collage;
      if (!cancelled && collage) {
        setData({ collage, question: d.todayQuestion, members: d.members });
      }
    }).catch(() => {
      if (!cancelled && collageData?.url) setData({ collage: collageData, question: null, members: [] });
    }).finally(() => {
      if (!cancelled) setLoading(false);
    });
    return () => { cancelled = true; };
  }, [group.id, collageData?.id]);

  if (loading) return <LoadingScreen />;

  if (!data) {
    return (
      <div style={{ width:'100vw', height:'100vh', background:M.cream, display:'flex', flexDirection:'column' }}>
        <div style={{ height:56, borderBottom:`1px solid ${M.ink4}`, display:'flex', alignItems:'center', padding:'0 clamp(14px,4vw,32px)', gap:10, background:M.cream }}>
          <HomeLogo size={22} onClick={onHome} />
          <span style={{ color:M.ink4, fontSize:14 }}>/</span>
          <span style={{ fontSize:13, fontWeight:600, color:M.ink2, fontFamily:M.fontBody }}>오늘의 콜라주</span>
        </div>
        <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:16 }}>
          <div style={{ fontSize:32 }}>⏳</div>
          <div style={{ fontFamily:M.fontBody, fontSize:15, color:M.ink2 }}>아직 영상이 완성되지 않았어요</div>
        </div>
      </div>
    );
  }

  const { collage, question, members } = data;
  const isVideo = collage.isVideo !== false;
  const collageTemplate = getTemplates(group.capacity).find(t => t.id === collage.templateId);
  const collageAspect = collageTemplate ? `${collageTemplate.cw} / ${collageTemplate.ch}` : '9 / 16';
  const isPortraitCollage = !collageTemplate || collageTemplate.ch >= collageTemplate.cw;

  async function shareToInstagram() {
    if (!isVideo || shareLoading) return;
    setShareLoading(true);
    setShareGuide(false);
    try {
      // 모바일: 네이티브 공유 시트 (Instagram 포함)
      if (navigator.share && navigator.canShare) {
        const res = await fetch(collage.url);
        const blob = await res.blob();
        const file = new File([blob], 'moment.mp4', { type: 'video/mp4' });
        if (navigator.canShare({ files: [file] })) {
          await navigator.share({ files: [file], title: '오늘의 모먼트', text: '오늘의 모먼트 ✨' });
          return;
        }
      }
      // PC 폴백: 다운로드 + 안내
      const a = document.createElement('a');
      a.href = collage.url;
      a.download = `moment-${question?.date || 'today'}.mp4`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      setShareGuide(true);
      setTimeout(() => setShareGuide(false), 6000);
    } catch (e) {
      if (e.name !== 'AbortError') {
        setShareGuide(true);
        setTimeout(() => setShareGuide(false), 6000);
      }
    } finally {
      setShareLoading(false);
    }
  }

  async function handleRegenerate() {
    setRegenerating(true);
    setMediaError('');
    try {
      const regenerated = await window.API.generateCollage(group.id, collage.templateId, true);
      setData(current => current ? { ...current, collage: regenerated } : current);
    } catch (e) {
      setMediaError(e.message || '영상 재생성 중 오류가 발생했습니다.');
    } finally {
      setRegenerating(false);
    }
  }

  return (
    <div style={{ width:'100vw', height:'100vh', background:M.bg, display:'flex', flexDirection:'column', position:'relative', overflow:'hidden' }}>
      <Confetti />
      <div style={{ height:56, background:M.cream, borderBottom:`1px solid ${M.ink4}`, display:'flex', alignItems:'center', padding:'0 clamp(14px,4vw,32px)', gap:10, position:'relative', zIndex:3 }}>
        <HomeLogo size={22} onClick={onHome} />
        <span style={{ color:M.ink4, fontSize:14 }}>/</span>
        <span style={{ fontSize:13, fontWeight:600, color:M.ink2, fontFamily:M.fontBody }}>오늘의 콜라주</span>
      </div>

      <div style={{ flex:1, display:'grid', gridTemplateColumns:'1fr minmax(0,320px)', padding:'clamp(16px,3vw,28px) clamp(14px,4vw,40px)', gap:'clamp(14px,3vw,28px)', position:'relative', zIndex:2, overflow:'hidden', minHeight:0 }}>
        {/* Video / Image */}
        <div style={{ display:'flex', alignItems:'center', justifyContent:'center', minWidth:0, minHeight:0, overflow:'auto' }}>
          <div style={{
            width:'100%',
            maxWidth:isPortraitCollage ? 400 : 720,
            maxHeight:'calc(100vh - 150px)',
            aspectRatio:collageAspect,
            boxShadow:M.shadowFloat,
            borderRadius:10,
            overflow:'hidden',
            background:'#000',
            border:`1px solid ${M.ink4}`,
          }}>
            {isVideo ? (
              <video
                src={`${collage.url}${collage.url.includes('?') ? '&' : '?'}t=${encodeURIComponent(collage.createdAt || collage.id || Date.now())}`}
                controls
                autoPlay
                loop
                playsInline
                onError={() => setMediaError('생성된 영상 파일을 불러오지 못했습니다. 다시 생성해 주세요.')}
                style={{ width:'100%', height:'100%', objectFit:'contain', display:'block', background:'#0a0a12' }}
              />
            ) : (
              <div style={{
                width:'100%', height:'100%', display:'flex', flexDirection:'column',
                alignItems:'center', justifyContent:'center', gap:10, padding:28,
                background:M.ink, color:`${M.cream}88`, textAlign:'center',
              }}>
                <div style={{ fontFamily:M.fontTemplate, fontSize:20, letterSpacing:'0.04em' }}>MP4 재생성 필요</div>
              </div>
            )}
          </div>
        </div>

        {/* Side panel */}
        <div style={{ display:'flex', flexDirection:'column', gap:14, minHeight:0 }}>
          <div style={{ display:'flex', flexDirection:'column', gap:14, minHeight:0, overflow:'auto', paddingRight:4 }}>
            <div>
              <h2 style={{ fontFamily:M.fontSerif, fontSize:'clamp(26px,3.5vw,38px)', margin:0, fontWeight:700, letterSpacing:-0.5, lineHeight:1.1, wordBreak:'keep-all',
                ...(isVideo ? {
                  background: `linear-gradient(135deg, ${M.coral}, ${M.rose})`,
                  WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
                } : { color: M.ink }),
              }}>
                {isVideo ? <>오늘의<br/>모먼트 완성!</> : <>영상 재생성<br/>필요</>}
              </h2>
              {question && (
                <div style={{
                  marginTop:12, padding:'10px 14px', borderRadius:10,
                  background:M.coralSoft, border:`1px solid ${M.coral}33`,
                }}>
                  <LabelBadge variant="coral" mb={4}>오늘의 질문</LabelBadge>
                  <div style={{ fontSize:13, color:M.ink, lineHeight:1.6, wordBreak:'keep-all', fontFamily:M.fontBody }}>
                    {question.text}
                  </div>
                </div>
              )}
            </div>

            <div style={{ background:M.paper, borderRadius:12, padding:16, border:`1px solid ${M.ink4}` }}>
              <LabelBadge variant="neutral" mb={10}>참여 멤버</LabelBadge>
              <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                {(members || []).filter(m => m.upload).map(m => (
                  <div key={m.userId} style={{ display:'flex', flexDirection:'column', gap:4, paddingBottom:10, borderBottom:`1px solid ${M.ink4}44` }}>
                    <div style={{ display:'flex', alignItems:'center', gap:10 }}>
                      <MemberAvatar userId={m.userId} name={m.name} size={26} />
                      <div style={{ flex:1, fontSize:13 }}>
                        <span style={{ fontWeight:600, color:M.ink }}>{m.name}</span>
                        {m.upload?.caption && <span style={{ color:M.ink3, marginLeft:6, fontSize:12 }}>"{m.upload.caption}"</span>}
                      </div>
                    </div>
                    <ReactionSummary reactions={m.reactions} />
                  </div>
                ))}
              </div>
            </div>

            {mediaError && (
              <div style={{ padding:12, background:M.coralSoft, borderRadius:10, fontSize:20, color:M.coralDark, lineHeight:1.5 }}>
                {mediaError}
              </div>
            )}
          </div>

          <div style={{
            display:'flex', flexDirection:'column', gap:10, flexShrink:0,
            paddingTop:14, borderTop:`1px solid ${M.ink4}55`,
            background:M.bg,
          }}>
            {isVideo ? (
              <>
                {/* Instagram 공유 */}
                <button
                  onClick={shareToInstagram}
                  disabled={shareLoading}
                  style={{
                    width:'100%', height:48, border:'none', borderRadius:14, cursor:'pointer',
                    background: shareLoading ? 'rgba(255,255,255,0.08)' : 'linear-gradient(45deg,#f09433 0%,#e6683c 25%,#dc2743 50%,#cc2366 75%,#bc1888 100%)',
                    color:'#fff', fontSize:14, fontWeight:700, fontFamily:'inherit',
                    display:'flex', alignItems:'center', justifyContent:'center', gap:8,
                    transition:'opacity 0.15s', opacity: shareLoading ? 0.6 : 1,
                  }}
                >
                  <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
                    <path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/>
                  </svg>
                  {shareLoading ? '준비 중...' : 'Instagram에 공유'}
                </button>

                {/* PC 폴백 안내 */}
                {shareGuide && (
                  <div style={{ padding:'10px 14px', background:'rgba(188,24,136,0.1)', border:'1px solid rgba(188,24,136,0.3)', borderRadius:10, fontSize:12, color:'#e879c8', lineHeight:1.6, fontFamily:M.fontBody }}>
                    영상이 저장됐어요. Instagram 앱에서 <strong>릴스 업로드</strong>로 공유해보세요 📲
                  </div>
                )}

                <a href={collage.url} download={`moment-${question?.date || 'today'}.mp4`} style={{ textDecoration:'none' }}>
                  <WebButton variant="primary" size="lg" full>영상 저장</WebButton>
                </a>
              </>
            ) : (
              <div style={{ padding:12, background:M.sageSoft, borderRadius:10, fontSize:13, color:M.sage, lineHeight:1.5 }}>
                MP4 재생성 필요
              </div>
            )}

            <WebButton variant="secondary" size="lg" full onClick={handleRegenerate} disabled={regenerating}>
              {regenerating ? '영상 재생성 중...' : '영상 재생성'}
            </WebButton>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Timeline / Archive ────────────────────────────────────────────────────────
function TimelineScreen({ group, user, onBack, onHome }) {
  const M = window.M;
  const now = new Date();
  const [year, setYear] = useState(now.getFullYear());
  const [month, setMonth] = useState(now.getMonth() + 1);
  const [calendar, setCalendar] = useState(null);
  const [archive, setArchive] = useState([]);
  const [reminders, setReminders] = useState([]);
  const [selected, setSelected] = useState(null);
  const [detail, setDetail] = useState(null);
  const [loading, setLoading] = useState(true);
  const touchStartX = useRef(0);

  useEffect(() => { loadData(); }, [group.id, year, month]);

  async function loadData() {
    setLoading(true);
    try {
      const [cal, arc, rem] = await Promise.all([
        window.API.getArchiveCalendar(group.id, year, month),
        window.API.getArchive(group.id),
        window.API.getReminders(),
      ]);
      setCalendar(cal);
      setArchive(arc);
      setReminders(rem.filter(r => r.groupId === group.id));
    } catch (e) {
      console.error(e);
    } finally { setLoading(false); }
  }

  async function loadDetail(date) {
    setSelected(date); setDetail(null);
    try {
      setDetail(await window.API.getArchiveDetail(group.id, date));
    } catch (e) {}
  }

  function handleTouchStart(e) { touchStartX.current = e.touches[0].clientX; }
  function handleTouchEnd(e) {
    const diff = touchStartX.current - e.changedTouches[0].clientX;
    if (Math.abs(diff) > 50) { diff > 0 ? nextMonth() : prevMonth(); }
  }
  function closeDetail() { setSelected(null); setDetail(null); }

  function prevMonth() {
    if (month === 1) { setYear(y => y - 1); setMonth(12); }
    else setMonth(m => m - 1);
  }
  function nextMonth() {
    if (month === 12) { setYear(y => y + 1); setMonth(1); }
    else setMonth(m => m + 1);
  }

  const daysInMonth = new Date(year, month, 0).getDate();
  const startWeekday = (new Date(year, month - 1, 1).getDay() + 6) % 7;
  const trailingDays = 42 - startWeekday - daysInMonth;
  const successSet = new Set(calendar?.successDates || []);
  const todayStr = fmtLocalDate(now);
  const archiveByDate = Object.fromEntries(archive.map(a => [a.date, a.collageUrl]));

  return (
    <div style={{ width: '100vw', height: '100vh', background: M.cream, display: 'flex', flexDirection: 'column' }}>
      <div style={{ height: 56, borderBottom: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', padding: '0 clamp(14px,4vw,32px)', gap: 10, flexShrink: 0, background: M.cream }}>
        <HomeLogo size={22} onClick={onHome} />
        <span style={{ color:M.ink4, fontSize:14 }}>/</span>
        <span style={{ fontSize:13, fontWeight:600, color:M.ink2, fontFamily:M.fontBody }}>타임라인</span>
        <div style={{ flex: 1 }} />
        <button onClick={onBack} style={{
          background:'none', border:`1px solid ${M.ink4}`, padding:'5px 14px', borderRadius:8,
          fontSize:12, color:M.ink3, cursor:'pointer', fontFamily:M.fontBody, fontWeight:600,
          transition:'color 0.15s, border-color 0.15s',
        }}>← 오늘로</button>
      </div>

      <div className={`timeline-main-grid${detail ? ' timeline-detail-active' : ''}`} style={{ flex: 1, minHeight: 0, display: 'grid', gridTemplateColumns: '1.4fr 1fr', overflow: 'hidden' }}>
        {/* Left — calendar */}
        <div className="timeline-left" onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} style={{ padding: 'clamp(14px, 3vh, 28px) clamp(14px, 3.5vw, 40px)', borderRight: `1px solid ${M.ink4}`, overflow: 'hidden', minHeight: 0, display: 'flex', flexDirection: 'column' }}>
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 'clamp(10px, 2vh, 18px)', flexShrink: 0 }}>
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
                <span style={{
                  fontFamily: M.fontMono, fontSize: 12, fontWeight: 700, letterSpacing: 0.5,
                  color: M.ink, background: M.ivory, border: `1px solid ${M.ink4}`,
                  padding: '3px 10px', borderRadius: 6,
                }}>
                  {year}년 {month}월
                </span>
              </div>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }}>
                <h2 style={{ fontFamily: M.fontSerif, fontSize: 'clamp(28px, 5vh, 42px)', margin: 0, fontWeight: 700, letterSpacing: -0.5, lineHeight: 1.05,
                  background: `linear-gradient(135deg, #ffffff 50%, ${M.rose})`,
                  WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
                }}>
                  {month}월
                </h2>
                {calendar && (
                  <div style={{
                    fontFamily: M.fontMono,
                    fontSize: 12,
                    color: M.coral,
                    fontWeight: 700,
                    background: M.coralSoft,
                    border: `1px solid ${M.coral}33`,
                    borderRadius: 8,
                    padding: '3px 10px',
                    whiteSpace: 'nowrap',
                  }}>
                    {calendar.successDates.length}일 완성
                  </div>
                )}
              </div>
            </div>
            <div style={{ display: 'flex', gap: 6 }}>
              <div onClick={prevMonth} style={{ width: 32, height: 32, borderRadius: 8, background: M.ivory, border: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: M.ink2, cursor: 'pointer', fontSize: 16 }}>‹</div>
              <div onClick={nextMonth} style={{ width: 32, height: 32, borderRadius: 8, background: M.ivory, border: `1px solid ${M.ink4}`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: M.ink2, cursor: 'pointer', fontSize: 16 }}>›</div>
            </div>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7,1fr)', gap: 3, marginBottom: 4, flexShrink: 0 }}>
            {['MON','TUE','WED','THU','FRI','SAT','SUN'].map(d => (
              <div key={d} style={{ fontFamily: M.fontMono, fontSize: 10, color: M.ink3, textAlign: 'center', letterSpacing: 0.5, padding: '3px 0' }}>{d}</div>
            ))}
          </div>
          <div className="timeline-cal-grid" style={{ flex: 1, minHeight: 0, display: 'grid', gridTemplateColumns: 'repeat(7,minmax(0,1fr))', gridTemplateRows: 'repeat(6,minmax(0,1fr))', gap: 3 }}>
            {Array.from({ length: startWeekday }).map((_, i) => <div key={'p' + i} style={{ minHeight: 0 }} />)}
            {Array.from({ length: daysInMonth }, (_, i) => i + 1).map(d => {
              const ds = `${year}-${String(month).padStart(2,'0')}-${String(d).padStart(2,'0')}`;
              const isToday = ds === todayStr;
              const done = successSet.has(ds);
              const isSel = selected === ds;
              const thumb = archiveByDate[ds];
              return (
                <div key={d} onClick={() => done && loadDetail(ds)} style={{
                  minHeight: 0, borderRadius: 8, position: 'relative',
                  background: isToday
                    ? `linear-gradient(135deg, ${M.coral}, ${M.rose})`
                    : 'rgba(255,255,255,0.07)',
                  border: isSel
                    ? `2px solid ${M.coral}`
                    : done
                    ? `1.5px solid ${M.coral}88`
                    : `1px solid rgba(255,255,255,0.1)`,
                  boxShadow: isSel ? `0 0 8px ${M.coral}66` : 'none',
                  overflow: 'hidden', cursor: done ? 'pointer' : 'default',
                  transition: 'transform 0.1s, box-shadow 0.15s',
                }}>
                  {/* 콜라주 썸네일 — 오늘 포함 */}
                  {thumb && (
                    <video
                      src={thumb}
                      muted playsInline preload="metadata"
                      style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block', opacity: isToday ? 0.5 : 0.65 }}
                      onLoadedMetadata={e => { e.target.currentTime = 0.5; }}
                    />
                  )}
                  {/* 날짜 오버레이 */}
                  <div style={{
                    position: 'absolute', inset: 0,
                    display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                    fontFamily: M.fontSerif, fontSize: 13,
                    fontWeight: done || isToday ? 700 : 400,
                    color: '#fff',
                  }}>
                    <div style={thumb ? {
                      background: 'rgba(0,0,0,0.52)', borderRadius: 4, padding: '1px 5px', lineHeight: 1.4,
                    } : { color: isToday ? '#fff' : done ? '#fff' : 'rgba(255,255,255,0.45)' }}>{d}</div>
                    {isToday && <div style={{ fontSize: 9, color: 'rgba(255,255,255,0.85)', fontFamily: M.fontMono, marginTop: 1, letterSpacing: 0.5 }}>TODAY</div>}
                    {done && !isToday && !thumb && <div style={{ fontSize: 10, color: M.coral, marginTop: 1 }}>✓</div>}
                  </div>
                </div>
              );
            })}
            {Array.from({ length: trailingDays }).map((_, i) => <div key={'n' + i} style={{ minHeight: 0 }} />)}
          </div>

        </div>

        {/* Right — detail / archive list */}
        <div className="timeline-right" style={{ padding: 'clamp(16px,3vw,28px) clamp(14px,3vw,32px)', overflow: 'auto', background: M.paper, borderLeft: 'none' }}>
          {selected && detail ? (
            <div>
              <button className="timeline-mobile-back" onClick={closeDetail}>‹ 달력으로</button>
              <div style={{ fontFamily: M.fontMono, fontSize: 11, color: M.ink3, letterSpacing: 1.5, textTransform: 'uppercase' }}>{detail.question.date}</div>
              <h3 style={{ fontFamily: M.fontSerif, fontSize: 20, color: M.ink, margin: '4px 0 14px', fontWeight: 700, letterSpacing: -0.4, wordBreak: 'keep-all' }}>
                "{detail.question.text}"
              </h3>
              <div style={{ borderRadius: 10, overflow: 'hidden', marginBottom: 14, border: `1px solid ${M.ink4}` }}>
                {detail.collage.isVideo !== false ? (
                  <video src={detail.collage.url} controls playsInline style={{ width: '100%', height: 'auto', maxHeight: 'clamp(220px, 42vh, 340px)', display: 'block', background: '#000', objectFit: 'contain' }} />
                ) : (
                  <div style={{
                    width:'100%', aspectRatio:'9 / 16', display:'flex', flexDirection:'column',
                    alignItems:'center', justifyContent:'center', gap:8, padding:24,
                    background:M.ink, color:`${M.cream}88`, textAlign:'center',
                  }}>
                    <div style={{ fontSize:13, fontFamily:M.fontBody }}>MP4 재생성 필요</div>
                  </div>
                )}
              </div>
              {/* Individual upload thumbnails */}
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: 10, marginBottom: 16 }}>
                {detail.members.filter(m => m.upload).map(m => (
                  <div key={m.userId} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                    <div style={{ borderRadius: 8, overflow: 'hidden', border: `1px solid ${M.ink4}`, background: '#000', aspectRatio: '9/16', position: 'relative' }}>
                      <video
                        src={m.upload.url}
                        muted playsInline preload="metadata"
                        style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
                        onLoadedMetadata={e => { e.target.currentTime = 0.5; }}
                      />
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                      <MemberAvatar userId={m.userId} name={m.name} size={14} />
                      <span style={{ fontSize: 10, color: M.ink3, fontFamily: M.fontBody, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{m.name}</span>
                    </div>
                    {m.upload.caption && <div style={{ fontFamily: M.fontBody, fontSize: 10, color: M.ink2, lineHeight: 1.3, wordBreak: 'keep-all' }}>{m.upload.caption}</div>}
                    <ReactionSummary reactions={m.reactions} />
                  </div>
                ))}
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                {detail.collage.isVideo !== false ? (
                  <a href={detail.collage.url} download={`moment-${detail.question.date}.mp4`} style={{ textDecoration: 'none', flex: 1 }}>
                    <WebButton variant="accent" size="sm" full>저장</WebButton>
                  </a>
                ) : (
                  <WebButton variant="secondary" size="sm" full disabled style={{ flex: 1 }}>
                    MP4 생성 필요
                  </WebButton>
                )}
                <WebButton variant="secondary" size="sm" onClick={closeDetail}>닫기</WebButton>
              </div>
            </div>
          ) : archive.length === 0 ? (
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 10, color: M.ink3 }}>
              <div style={{ fontSize: 28 }}>📅</div>
              <div style={{ fontSize: 13, fontFamily: M.fontBody }}>아직 완성된 콜라주가 없어요</div>
            </div>
          ) : (
            <div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {archive.map(item => (
                  <div key={item.date} onClick={() => loadDetail(item.date)} style={{
                    display: 'flex', gap: 12, alignItems: 'center', cursor: 'pointer',
                    padding: '10px 12px', borderRadius: 12,
                    background: selected === item.date ? M.coralSoft : M.ivory,
                    border: selected === item.date ? `1px solid ${M.coral}44` : `1px solid ${M.ink4}`,
                    transition: 'background 0.15s, border-color 0.15s',
                  }}>
                    <div style={{ width: 40, height: 40, borderRadius: 8, overflow: 'hidden', flexShrink: 0, background: '#000' }}>
                      <video src={item.collageUrl} muted playsInline style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, color: M.ink, lineHeight: 1.4,
                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        fontFamily: M.fontBody, fontWeight: 500,
                      }}>"{item.question}"</div>
                      <div style={{ fontFamily: M.fontMono, fontSize: 11, color: M.ink3, marginTop: 2 }}>{item.date}</div>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Confetti (same as original design) ───────────────────────────────────────
function Confetti() {
  const M = window.M;
  const colors = [M.coral, M.sun, M.sage, M.rose, M.coralDark];
  const pieces = Array.from({ length: 38 }, (_, i) => ({
    left: (i * 13.7) % 100, delay: (i * 0.13) % 3,
    size: 6 + (i % 3) * 3, rot: (i * 47) % 360,
    color: colors[i % colors.length], shape: i % 3,
  }));
  return (
    <>
      <style>{`@keyframes fall { 0%{transform:translateY(-40px) rotate(0deg);opacity:0} 15%{opacity:1} 100%{transform:translateY(720px) rotate(540deg);opacity:0.6} }`}</style>
      <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden', zIndex: 1 }}>
        {pieces.map((p, i) => (
          <div key={i} style={{ position: 'absolute', left: p.left + '%', top: -20, width: p.size, height: p.size * (p.shape === 1 ? 0.4 : 1), background: p.color, opacity: 0.85, borderRadius: p.shape === 2 ? '50%' : 1, transform: `rotate(${p.rot}deg)`, animation: `fall ${4 + (i % 3)}s ${p.delay}s linear infinite` }} />
        ))}
      </div>
    </>
  );
}

// ── Root App ──────────────────────────────────────────────────────────────────
function App() {
  const [user, setUser] = useState(null);
  const [activeGroup, setActiveGroup] = useState(null);
  const [screen, setScreen] = useState('loading');
  const [collageData, setCollageData] = useState(null); // pass generated collage to CollageScreen
  const screenRef = useRef(screen);

  useEffect(() => { screenRef.current = screen; }, [screen]);

  // 화면 전환마다 히스토리 엔트리 추가 → 뒤로가기가 계속 작동
  useEffect(() => {
    if (screen !== 'loading') {
      window.history.pushState({ screen }, '');
    }
  }, [screen]);

  // 브라우저 뒤로가기 인터셉트
  useEffect(() => {
    function handlePopState() {
      // 현재 화면 엔트리를 즉시 재추가해 앱 이탈 방지
      window.history.pushState({ screen: screenRef.current }, '');
      const cur = screenRef.current;
      if (cur === 'upload' || cur === 'templateVote' || cur === 'collage' || cur === 'timeline') {
        setScreen('dashboard');
      } else if (cur === 'dashboard' || cur === 'waiting') {
        setActiveGroup(null); setCollageData(null); setScreen('groups');
      } else if (cur === 'groups') {
        setScreen('auth');
      }
    }
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []);

  useEffect(() => {
    const token = window.API.getToken();
    if (token) {
      window.API.me()
        .then(u => { setUser(u); showMainPage(); })
        .catch(() => { window.API.clearToken(); setScreen('auth'); });
    } else {
      setScreen('auth');
    }
  }, []);

  function showMainPage() {
    setActiveGroup(null);
    setCollageData(null);
    setScreen('groups');
  }

  function handleLogin(u) {
    setUser(u);
    showMainPage();
  }

  function handleLogout() {
    window.API.clearToken();
    setUser(null); setActiveGroup(null); setCollageData(null);
    setScreen('auth');
  }

  function handleHome() {
    setActiveGroup(null);
    setCollageData(null);
    setScreen('groups');
  }

  function handleEnterGroup(g) {
    setActiveGroup(g);
    setScreen(g.status === 'Active' ? 'dashboard' : 'waiting');
  }

  function handleUploadDone(result) {
    // After upload: go to dashboard (template vote FAB appears when all done)
    setScreen('dashboard');
  }

  function handleGenerated(collage) {
    // After video generation: store collage result and go to collage screen
    setCollageData(collage);
    setScreen('collage');
  }

  if (screen === 'loading') return <LoadingScreen />;
  if (screen === 'auth') return <AuthScreen onLogin={handleLogin} />;
  if (screen === 'groups') return (
    <GroupsScreen user={user} onEnterGroup={handleEnterGroup} onLogout={handleLogout} onHome={handleHome} />
  );
  if (screen === 'waiting') return (
    <WaitingScreen
      group={activeGroup} user={user}
      onGroupActivated={() => setScreen('dashboard')}
      onLogout={handleLogout}
      onChangeGroup={() => setScreen('groups')}
      onHome={handleHome}
    />
  );
  if (screen === 'dashboard') return (
    <DashboardScreen
      group={activeGroup} user={user}
      onUpload={() => setScreen('upload')}
      onViewCollage={() => { setCollageData(null); setScreen('collage'); }}
      onVoteTemplate={() => setScreen('templateVote')}
      onViewTimeline={() => setScreen('timeline')}
      onChangeGroup={() => { setActiveGroup(null); setScreen('groups'); }}
      onLogout={handleLogout}
      onHome={handleHome}
    />
  );
  if (screen === 'upload') return (
    <UploadScreen
      group={activeGroup} user={user}
      onDone={handleUploadDone}
      onBack={() => setScreen('dashboard')}
      onHome={handleHome}
    />
  );
  if (screen === 'templateVote') return (
    <TemplateVoteScreen
      group={activeGroup} user={user}
      onGenerated={handleGenerated}
      onBack={() => setScreen('dashboard')}
      onHome={handleHome}
    />
  );
  if (screen === 'collage') return (
    <CollageScreen
      group={activeGroup} user={user}
      collageData={collageData}
      onBack={() => setScreen('dashboard')}
      onHome={handleHome}
    />
  );
  if (screen === 'timeline') return (
    <TimelineScreen
      group={activeGroup} user={user}
      onBack={() => setScreen('dashboard')}
      onHome={handleHome}
    />
  );

  return <LoadingScreen />;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
