// 웹 스크린 B — 상세 · 채팅방 · 호스트
function WScrDetail({S,set,go}){
  const m=MEETUPS[S.detailId]||(S.tempDetail&&S.tempDetail.id===S.detailId?S.tempDetail:null)||resolveMeetup(S.detailId,S)||null;
  if (!m) return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain"><MeetupNotFoundNotice go={go}/></div></div>;
  if (!meetupVisibleTo(m, S.viewAsUser)) return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain"><MeetupBlockedNotice go={go}/></div></div>;
  const full=effectiveJoined(m)>=m.capMax;
  const status=S.joined[m.id];
  const joined=status==='waitlisted', confirmed=status==='confirmed', pending=status==='pending';
  const knownCount = Math.min(4, m.capJoined||0);
  const previewMe = confirmed ? (S.profile.nickname||'나') : null;
  const anonCount = Math.max(0, knownCount - (previewMe?1:0));
  const [companionOpen, setCompanionOpen] = React.useState(false);
  // 삭제된 모임 접근 감지(낡은 목록·링크) → 존재하지 않는 모임 팝업
  const [meetupGone, setMeetupGone] = React.useState(false);
  React.useEffect(()=>{ const mid=serverMeetupId(S.detailId); if (!mid) return; let al=true; recordExists('meetups', mid).then(ok=>{ if (al && !ok) setMeetupGone(true); }); return ()=>{al=false;}; }, [S.detailId]);
  // 16단계: '확정자만 공개' 모임의 정확한 장소 — 자격(확정·호스트·운영진)일 때만 서버에서 가져옴(RLS가 최종 강제)
  const [placeDetail, setPlaceDetail] = React.useState(null);
  const amHost = !!(m && m.hostEmail && (S.acctEmail||'').toLowerCase()===m.hostEmail);
  React.useEffect(()=>{
    setPlaceDetail(null);
    if (!m || m.placeVisibility!=='confirmed') return;
    if (!(status==='confirmed' || amHost || isAdminUser(S))) return;
    let alive=true;
    fetchMeetupPlaceDetail(m.id).then(d=>{ if(alive) setPlaceDetail(d); });
    return ()=>{ alive=false; };
  }, [m&&m.id, status]);
  if (meetupGone) return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="overlay"><div className="sheet" role="dialog" aria-modal="true" style={{textAlign:'center'}}>
    <h2 style={{fontSize:17, margin:'0 0 8px'}}>존재하지 않는 모임입니다</h2>
    <p className="meta" style={{margin:'0 0 16px', lineHeight:1.6}}>주최자가 삭제했거나 운영정책 위반으로 삭제된 모임이에요.</p>
    <DSBtn label="홈으로 돌아가기" size="md" onClick={()=>go('home')}/>
  </div></div></div>;
  return <div className="wapp"><WNav S={S} go={go} set={set}/>
    <div className="wmain">
      <button className="wback" onClick={()=>go('home')}><Ic n="IconNormalChevronLeftNameChevronLeftTightFalseThick" s={14}/>목록으로</button>
      <div className="wtwo">
        <div>
          {(()=>{ const v=meetupVisual(m); const label=(m.cardTags&&m.cardTags[0]&&m.cardTags[0][0])||m.purpose||'';
            return <div className="detail-hero" style={{background:v.bg}} aria-hidden="true">
              {m.hostPhoto?<img src={m.hostPhoto} alt=""/>:<>
                <span className="dh-deco dh-c1"/><span className="dh-deco dh-c2"/><span className="dh-deco dh-c3"/><span className="dh-deco dh-c4"/>
                <span className="dh-puck"><ActIcon label={label} size={34} color={v.fg} fallbackIcon={v.icon}/></span>
                <svg className="dh-pin" width="22" height="28" viewBox="0 0 24 30"><path d="M12 0C5.4 0 0 5.4 0 12c0 9 12 18 12 18s12-9 12-18c0-6.6-5.4-12-12-12z" fill="#FF8A5B"/><circle cx="12" cy="12" r="5" fill="#fff"/></svg>
              </>}
            </div>; })()}
          {m.lat&&(m.placeVisibility!=='confirmed'||status==='confirmed')&&<MapEmbed lat={m.lat} lng={m.lng} height={280}/>}
          {m.lat&&m.placeVisibility==='confirmed'&&status!=='confirmed'&&<div className="notice blue" style={{marginBottom:0}}><Ic n="IconNormalLocation2NameLocationFillFillTrue" s={15}/><span>정확한 장소는 참가가 확정된 회원에게만 공개돼요. 지금은 동네 단위까지만 보여드려요.</span></div>}
          <h1 style={{fontSize:26,fontWeight:800,margin:(m.lat?'20px':'4px')+' 0 12px',letterSpacing:'-.01em'}}>{m.title}</h1>
          <div style={{display:'flex',gap:6,flexWrap:'wrap',marginBottom:16}}>{m.tags.map(t=><Chip key={t[0]} label={t[0]} tone={t[1]}/>)}</div>
          <div style={{display:'flex',gap:8,marginBottom:20}}>{m.expired?<Chip label="종료된 모임" tone=""/>:full&&<Chip label="정원 마감 · 대기 가능" tone="orange"/>}</div>
          {m.soloWelcome&&<div className="notice" style={{marginBottom:18}}><Ic n="IconNormalHeartNameHeartFillFalse" s={16} c="var(--c-violet)"/><span>혼자 참여하는 분도 자연스럽게 어울릴 수 있도록 주최자가 활동과 대화를 안내해요.</span></div>}
          <button className="card" style={{width:'100%',display:'flex',gap:14,alignItems:'center',cursor:'pointer',fontFamily:'inherit',textAlign:'left',marginBottom:12}} onClick={()=>{ if(isAdminEmail(m.hostEmail)) return; go('host'); }} disabled={isAdminEmail(m.hostEmail)} title={isAdminEmail(m.hostEmail)?'운영진 계정 정보는 공개되지 않아요 — 문의하기·신고하기로 소통해주세요':undefined}>
            <Av name={m.host.name} size={52} tone="p" photo={m.host.photo}/>
            <span style={{flex:1}}><b style={{fontSize:16}}>호스트 · {m.host.name}</b>{isAdminEmail(m.hostEmail)&&<span style={{marginLeft:6}}><AdminBadge/></span>}<span style={{display:'flex',gap:6,marginTop:6,flexWrap:'wrap'}}>{m.host.badges.slice(0,2).map(b=><Chip key={b} label={b} tone="green"/>)}</span></span>
            <span className="meta">{m.host.reviews==null?'':(m.host.reviews>0?`후기 ${m.host.reviews}`:'첫 모임')}</span>
          </button>
          {m.host.reviews==null?null:(m.host.reviews>0?<div className="row" style={{gap:16, flexWrap:'wrap', marginBottom:18}}>
            <span style={{fontSize:12.5, color:'var(--c-sub)'}}>설명과 일치해요 <b style={{color:'var(--c-text)'}}>{Math.min(99, 82+m.host.reviews%13)}%</b></span>
            <span style={{fontSize:12.5, color:'var(--c-sub)'}}>진행이 매끄러워요 <b style={{color:'var(--c-text)'}}>{Math.min(99, 79+m.host.reviews%15)}%</b></span>
            <span style={{fontSize:12.5, color:'var(--c-sub)'}}>안전하게 느꼈어요 <b style={{color:'var(--c-text)'}}>{Math.min(99, 85+m.host.reviews%10)}%</b></span>
          </div>:<div className="notice" style={{marginBottom:18}}><Ic n="IconNormalCircleInfo2NameCircleInfoFillFalse" s={15}/><span>이 호스트의 첫 모임이에요. 후기는 아직 없지만 운영진 확인을 거쳤어요.</span></div>)}
          <div className="row" style={{gap:14, marginBottom:18, flexWrap:'wrap'}}>
            <button style={{background:'none',border:'none',color:'var(--c-primary)',fontFamily:'inherit',fontSize:12.5,fontWeight:700,cursor:'pointer',padding:0}} onClick={()=>set({legalOpen:'safety'})}>안전수칙 보기</button>
            <button style={{background:'none',border:'none',color:'var(--c-faint)',fontFamily:'inherit',fontSize:12.5,fontWeight:700,cursor:'pointer',padding:0}} onClick={()=>guardAuth(S,set,go,()=>set({reportOpen:true, reportPrefill:m.title, reportContext:'listing'}))}>모임 신고</button>
            {isAdminUser(S)&&m.hostEmail&&(S.acctEmail||'').toLowerCase()!==m.hostEmail&&<button style={{background:'none',border:'none',color:'var(--c-red)',fontFamily:'inherit',fontSize:12.5,fontWeight:800,cursor:'pointer',padding:0}} onClick={()=>set({adminRemove:{kind:'meetup', id:m.id, ownerEmail:m.hostEmail, title:m.title}})}>운영진: 모임 삭제</button>}
            <button style={{background:'none',border:'none',color:'var(--c-primary)',fontFamily:'inherit',fontSize:12.5,fontWeight:700,cursor:'pointer',padding:0}} onClick={()=>guardAuth(S,set,go,()=>set({inquiryOpen: amHost?{meetupId:m.id, title:m.title, mode:'hostlist'}:{meetupId:m.id, title:m.title}}))}>{amHost?'받은 문의 보기':'주최자에게 문의하기'}</button>
          </div>
          <p style={{fontSize:15,lineHeight:1.7,color:'var(--c-sub)',margin:'0 0 20px'}}>{m.desc}</p>
          <div className="card" style={{marginBottom:18, padding:'16px 18px'}}>
            <b style={{fontSize:14.5}}>참가 예정 멤버</b>
            {S.authStatus==='authenticated'?<div className="row" style={{marginTop:12}}>
              <div style={{display:'flex'}}>
                {previewMe&&<span style={{marginLeft:0, border:'2px solid var(--c-bg)', borderRadius:'50%'}}><Av name={previewMe} size={38} tone="p"/></span>}
                {Array.from({length:anonCount}).map((_,i)=><span key={i} style={{marginLeft:(i||previewMe)?-12:0, border:'2px solid var(--c-bg)', borderRadius:'50%', width:38, height:38, display:'flex', alignItems:'center', justifyContent:'center', background:'var(--c-fill)'}}><Ic n="IconNormalPersonsNamePersonsFillFalse" s={16} c="var(--c-faint)"/></span>)}
              </div>
              <span className="meta" style={{marginLeft:10}}>정원 {m.capMax}명 중 {effectiveJoined(m)}명 참여 확정</span>
            </div>:<p className="meta" style={{marginTop:10}}>로그인하면 참가 예정 멤버를 볼 수 있어요 · 정원 {m.capMax}명 중 {effectiveJoined(m)}명 참여 확정</p>}
          </div>
          <CompanionInfoCard/>
          <div className="card" style={{marginBottom:18, padding:'16px 18px'}}>
            <b style={{fontSize:14.5}}>모임 이용 안내</b>
            <div style={{marginTop:10, display:'flex', flexDirection:'column', gap:9}}>
              <InfoRow icon="IconNormalTicketNameTicketFillFalse">예상 개인 비용 — 식사·음료 등은 현장에서 각자 결제해요</InfoRow>
              <InfoRow icon="IconNormalGlobe2NameGlobeFillFalse">사용 언어 — 한국어 (영어 병행 가능)</InfoRow>
              <InfoRow icon="IconNormalCircleInfo2NameCircleInfoFillFalse">음주 여부 — {(m.tags||[]).some(t=>t[0].includes('술 없는'))?'술 없는 모임':'가벼운 음주 가능'}</InfoRow>
              <InfoRow icon="IconNormalPersonsNamePersonsFillFalse">연령대 — {m.ageRange||'전 연령 환영'}</InfoRow>
              <InfoRow icon="IconNormalCheckNameCheckThickFalse">준비물 — {m.prep||'준비물 없음'}</InfoRow>
              <InfoRow icon="IconNormalImageNameImage">{m.photoPromoConsent?'사진 촬영 — 이 모임 사진은 모임 후기에 올라오면 운영진 SNS 홍보에 쓰일 수 있어요':'사진 촬영 — 참가자 동의 시에만 SNS 게시'}</InfoRow>
            </div>
          </div>
          <div className="card" style={{marginBottom:18, padding:'16px 18px'}}>
            <b style={{fontSize:14.5}}>취소 및 노쇼 안내</b>
            <div style={{marginTop:10, display:'flex', flexDirection:'column', gap:6}}>
              <span style={{fontSize:13.5, color:'var(--c-sub)'}}>모임 24시간 전까지 — 참가 취소 가능해요</span>
              <span style={{fontSize:13.5, color:'var(--c-sub)'}}>24시간 이내 취소·노쇼 — 다음 신청에 제한이 있을 수 있어요</span>
              <span style={{fontSize:13.5, color:'var(--c-sub)'}}>모임장 사정으로 취소된 경우 — 바로 알려드려요</span>
            </div>
          </div>
          <div className="notice orange"><Ic n="IconNormalBell2NameBellFillFalse" s={16}/><span>참가 확정 후, 행사 24시간 전에 참석 여부를 다시 한 번 확인해드려요. 미응답 시 대기자에게 자리가 넘어갈 수 있어요.</span></div>
        </div>
        <div className="wside">
          {m.sponsor&&<div className="chip orange" style={{width:'fit-content'}}><Ic n="IconNormalMegaphoneNameMegaphoneFillFalse" s={13}/>{m.sponsor}</div>}
          <div className="wpanel">
            <InfoRow icon="IconNormalCalendarNameCalendar">{m.whenFull||m.when}</InfoRow>
            <InfoRow icon="IconNormalLocation2NameLocationFillFalse">{m.placeVisibility==='confirmed'?((status==='confirmed'||amHost||isAdminUser(S))?`${m.region} · ${placeDetail||'상세 주소 불러오는 중…'}`:`${m.region} 인근 (참가 확정 후 상세 주소 공개)`):`${m.region} · ${m.place}`}</InfoRow>
            <InfoRow icon="IconNormalTicketNameTicketFillFalse">{m.fee}</InfoRow>
            <CapacityBar m={m}/>
            {joined&&<div className="notice blue" style={{marginBottom:14}}><Ic n="IconNormalCircleCheck2NameCircleCheckFillFillTrue" s={15}/><span><b>대기 {myWaitlistPosition(m.id,S)||1}번째로 등록됐어요.</b> 자리가 나면 바로 알림을 드려요.</span></div>}
            {pending&&<div className="notice orange" style={{marginBottom:14}}><Ic n="IconNormalCircleInfo2NameCircleInfoFillFalse" s={15}/><span><b>승인 대기 중이에요.</b> 모임장이 확인하면 알림으로 알려드려요.</span></div>}
            {confirmed&&<div className="notice blue" style={{marginBottom:14}}><Ic n="IconNormalCircleCheck2NameCircleCheckFillFillTrue" s={15}/><span><b>참가 확정!</b> 참가자 채팅방이 열렸어요. 행사 24시간 전 참석 재확인이 와요.</span></div>}
            {(m.hostEmail&&(S.acctEmail||'').toLowerCase()===m.hostEmail)
              ?<div className="card" style={{padding:'16px', textAlign:'center', background:'var(--c-fill-a)'}}>
                <p style={{fontSize:13.5, fontWeight:700, margin:'0 0 6px'}}>내가 주최하는 모임이에요</p>
                <p className="meta" style={{margin:'0 0 12px'}}>주최자는 정원에 자동으로 포함돼요. 신청자 승인·관리는 내가 개설한 모임에서 할 수 있어요.</p>
                <DSBtn label="신청자 관리로 가기" size="md" onClick={()=>go('mymeethosted')}/>
              </div>
              :joined||confirmed||pending
              ?<div style={{display:'flex', flexDirection:'column', gap:8}}>
                {confirmed?<DSBtn label="채팅방 열기" variant="solid" color="primary" onClick={()=>go('chat',{detailId:m.id})}/>
                :pending?<div style={{textAlign:'center', padding:'12px', borderRadius:10, background:'var(--c-fill-a)', color:'var(--c-faint)', fontSize:13.5, fontWeight:500}}>참가가 확정되면 대화방을 이용할 수 있어요 — 모임장 승인을 기다리는 중이에요</div>
                :<DSBtn label={`대기 등록 완료 · 순번 ${myWaitlistPosition(m.id,S)||1}번`} variant="outlined" color="assistive"/>}
                <button style={{background:'none', border:'1px solid var(--c-line)', borderRadius:10, padding:'10px', fontFamily:'inherit', fontSize:13, fontWeight:600, cursor:'pointer', color:'var(--c-red)'}} onClick={()=>cancelParticipation(m.id, S, set)}>신청 취소</button>
              </div>
              :m.expired?<div style={{textAlign:'center', padding:'12px', borderRadius:10, background:'var(--c-fill-a)', color:'var(--c-faint)', fontSize:13.5, fontWeight:500}}>종료된 모임이에요 — 더 이상 신청할 수 없어요</div>
              :S.authStatus!=='authenticated'?<div className="card" style={{padding:'16px', textAlign:'center', background:'var(--c-fill-a)'}}>
                <p style={{fontSize:13.5, fontWeight:700, margin:'0 0 12px'}}>모임에 참가하려면 로그인이 필요해요</p>
                <div style={{display:'flex', flexDirection:'column', gap:8}}>
                  <DSBtn label="로그인하기" size="md" onClick={()=>go('signup')}/>
                  <DSBtn label="아직 회원이 아니신가요? · 회원가입" variant="outlined" size="md" onClick={()=>go('signup')}/>
                </div>
              </div>
              :<DSBtn label={full?'대기 명단 신청하기 (정원 마감)':'참가 신청하기'} onClick={()=>guardAuth(S, set, go, ()=>set({rulesOpen:true}))}/>}
            <CompanionAskLink onOpen={()=>setCompanionOpen(true)} style={{padding:'8px 2px 0'}}/>
            {DEMO_MODE&&<WaitlistDemoPanel m={m} S={S} set={set}/>}
          </div>
        </div>
      </div>
    </div>
    {companionOpen&&<div className="overlay" onClick={()=>setCompanionOpen(false)}>
      <div className="sheet" onClick={e=>e.stopPropagation()}>
        <h2 style={{fontSize:18,margin:'0 0 10px'}}>친구와 함께 참여하고 싶나요?</h2>
        <p style={{fontSize:13.5,color:'var(--c-sub)',lineHeight:1.6,margin:'0 0 10px'}}>참가자는 각자 가입하고 신청하는 것을 권장해요. 가입하지 않은 지인과 함께하고 싶다면 모임 채팅에서 주최자에게 먼저 문의해주세요. 동반자는 주최자 승인 후 정원에 포함됩니다.</p>
        <p style={{fontSize:12,color:'var(--c-faint)',lineHeight:1.6,margin:'0 0 16px'}}>사전 승인 없이 지인을 데려오는 경우 현장에서 참여가 어려울 수 있어요.</p>
        {confirmed?<DSBtn label="모임 채팅에서 문의하기" onClick={()=>{setCompanionOpen(false); go('chat',{detailId:m.id});}}/>:<DSBtn label="확인했어요" variant="outlined" color="assistive" onClick={()=>setCompanionOpen(false)}/>}
      </div>
    </div>}
  </div>;
}
// 실계정 차단 버튼(24단계) — 호스트 페이지에서 이 주최자를 차단/해제
function HostBlockButton({hostEmail, hostName, S, set}) {
  const [blocked, setBlocked] = React.useState(()=>isBlockedEmail(hostEmail));
  const [confirm, setConfirm] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(()=>{ const f=()=>setBlocked(isBlockedEmail(hostEmail)); document.addEventListener('mm:blockschanged', f); return ()=>document.removeEventListener('mm:blockschanged', f); }, [hostEmail]);
  const doBlock = () => { setBusy(true); blockUser(S.acctEmail, hostEmail).then(r=>{ setBusy(false); setConfirm(false); if (r&&r.error){ set({toast:'차단에 실패했어요 — 잠시 후 다시 시도해주세요'}); return; } setBlocked(true); set({toast:'차단했어요 — 서로의 모임·글이 보이지 않아요'}); }); };
  const doUnblock = () => { setBusy(true); unblockUser(S.acctEmail, hostEmail).then(r=>{ setBusy(false); if (r&&r.error){ set({toast:'차단 해제에 실패했어요'}); return; } setBlocked(isBlockedEmail(hostEmail)); set({toast:'차단을 해제했어요'}); }); };
  if (blocked) return <button disabled={busy} style={{background:'none', border:'1px solid var(--c-line)', borderRadius:20, padding:'5px 12px', fontFamily:'inherit', fontSize:12, cursor:'pointer', color:'var(--c-sub)'}} onClick={doUnblock}>차단 해제</button>;
  return <>
    <button disabled={busy} style={{background:'none', border:'1px solid var(--c-line)', borderRadius:20, padding:'5px 12px', fontFamily:'inherit', fontSize:12, cursor:'pointer', color:'var(--c-red)'}} onClick={()=>setConfirm(true)}>차단</button>
    {confirm&&<div className="overlay" onClick={()=>setConfirm(false)}><div className="sheet" onClick={e=>e.stopPropagation()}>
      <h2 style={{fontSize:17, margin:'0 0 8px'}}>{hostName}님을 차단할까요?</h2>
      <p style={{fontSize:13.5, color:'var(--c-sub)', lineHeight:1.65, margin:'0 0 16px'}}>차단하면 서로의 모임·프로필·글이 보이지 않고, 서로의 모임에 참가하거나 문의할 수 없어요. 상대에게는 알리지 않아요. 언제든 해제할 수 있어요.</p>
      <div style={{display:'flex', gap:8}}>
        <div style={{flex:1}}><DSBtn label="취소" variant="outlined" color="assistive" size="md" onClick={()=>setConfirm(false)}/></div>
        <div style={{flex:1}}><DSBtn label={busy?'처리 중…':'차단하기'} size="md" disable={busy} onClick={doBlock}/></div>
      </div>
    </div></div>}
  </>;
}
function WScrHost({S,set,go}){
  const revs=[['시간·장소 안내가 정확했어요',24],['혼자 온 참가자를 잘 챙겼어요',19],['분위기가 편안했어요',15],['아이스브레이커가 재밌었어요',8]];
  const max=24;
  const m = MEETUPS[S.detailId] || (S.tempDetail && S.tempDetail.id===S.detailId ? S.tempDetail : null);
  const hostName = (m && m.host && m.host.name) || '호스트';
  const hostBadges = (m && m.host && m.host.badges) || ['인증 호스트'];
  // 실제로 이 호스트 이름으로 등록된 다른 모임들을 찾아서 보여줌(기존에는 무조건 같은 2개가 하드코딩돼 있었음)
  // 20단계: 실제 등록 호스트(hostEmail 보유)는 서버 실데이터로 표시 — 데모 모임은 기존 데모 표시 유지
  const hostEmail = ((m && m.hostEmail)||'').toLowerCase();
  const real = !!hostEmail;
  const [hd, setHd] = React.useState(null);
  React.useEffect(()=>{ if (!real) return; let al=true; fetchHostPublicData(hostEmail).then(d=>{ if (al) setHd(d); }); return ()=>{ al=false; }; }, [hostEmail]);
  const otherMeetups = real
    ? (((hd&&hd.meetups)||[]).filter(x=>('r_'+x.id)!==S.detailId).map(x=>({id:'r_'+x.id, title:x.title, when:x.when_label})))
    : Object.values(MEETUPS).filter(mm=>mm && mm.id!==S.detailId && mm.host && mm.host.name===hostName);
  return <div className="wapp"><WNav S={S} go={go} set={set}/>
    <div className="wmain" style={{maxWidth:800}}>
      <button className="wback" onClick={()=>go('detail')}><Ic n="IconNormalChevronLeftNameChevronLeftTightFalseThick" s={14}/>모임으로</button>
      <div className="row" style={{gap:18,margin:'8px 0 24px'}}>
        <Av name={hostName} size={72} tone="p" photo={real&&hd&&hd.profile&&hd.profile.photo_public?hd.profile.photo_url:undefined}/>
        <div><div className="row" style={{gap:10,alignItems:'center'}}><div style={{fontSize:22,fontWeight:800}}>{hostName}</div>{real&&isAdminEmail(hostEmail)&&<AdminBadge/>}{real&&hostEmail&&(S.acctEmail||'').toLowerCase()!==hostEmail&&!isAdminEmail(hostEmail)?<HostBlockButton hostEmail={hostEmail} hostName={hostName} S={S} set={set}/>:<AvoidUserButton hostName={hostName} S={S} set={set}/>}</div>{real?<div className="meta" style={{margin:'4px 0 10px'}}>{(hd&&hd.profile&&hd.profile.school)?hd.profile.school+' · ':''}실제 등록 호스트</div>:<div className="meta" style={{margin:'4px 0 10px'}}>2025.03부터 활동 · 강남/홍대 지역 위주</div>}<TrustBadges items={hostBadges}/><div style={{marginTop:10, display:'flex', flexDirection:'column', gap:6}}>{(real?['완료한 모임 '+((hd&&hd.doneCount)||0)+'개','참가자 후기 '+((hd&&hd.reviews.length)||0)+'개']:['호스트 모임 12회 운영','최근 노쇼 없음','학교 인증 완료']).map(label=><div key={label} className="row" style={{gap:7}}><Ic n="IconNormalCircleCheck2NameCircleCheckFillFillTrue" s={15} c="var(--c-green)"/><span style={{fontSize:14, fontWeight:600}}>{label}</span></div>)}</div></div>
      </div>
      {real&&hd&&hd.profile&&hd.profile.bio&&<p style={{margin:'-10px 0 20px', fontSize:14, color:'var(--c-sub)', lineHeight:1.65}}>{hd.profile.bio}</p>}
      <div className="wtwo">
        <div>
          {!real&&<><b style={{fontSize:16}}>참가자 후기 (태그형) <span className="meta" style={{fontWeight:400}}>— 별점 대신</span></b>
          <div style={{margin:'14px 0 24px'}}>{revs.map(r=><div key={r[0]} className="tagrev"><span style={{fontSize:13.5,width:230,flexShrink:0}}>{r[0]}</span><span className="bar"><i style={{'--w':(r[1]/max*100)+'%'}}></i></span><b style={{fontSize:13,color:'var(--c-primary)',width:24,textAlign:'right'}}>{r[1]}</b></div>)}</div></>}
          <b style={{fontSize:16}}>참가자 후기</b>
          {real
            ? <div style={{margin:'14px 0 24px'}}>
                {!hd?<p className="meta">후기를 불러오는 중…</p>
                :hd.reviews.length===0?<p className="meta" style={{lineHeight:1.6}}>아직 등록된 후기가 없어요 — 모임이 끝나면 참가자들의 후기가 여기에 쌓여요.</p>
                :hd.reviews.map((r,i)=><Reveal key={i} delay={i*80}><div className="card" style={{marginBottom:10}}>
                  <div className="row" style={{gap:8,marginBottom:6}}><Av name={r.author_name||'참가자'} size={28}/><b style={{fontSize:13.5}}>{r.author_name||'참가자'}</b><span className="meta">· {timeAgo(Date.parse(r.created_at))}</span></div>
                  <p style={{fontSize:13.5,color:'var(--c-sub)',margin:0,lineHeight:1.6}}>{r.body}</p>
                </div></Reveal>)}
              </div>
            : <div style={{margin:'14px 0 24px'}}>{[['닉네임B','8월 2일 웰컴파티 참여','처음 참가라 긴장했는데 호스트님이 먼저 말 걸어주셔서 금방 편해졌어요. 시간·장소 안내도 정확했고요.'],['닉네임F','강남 밤모임 참여','혼자 갔는데도 자연스럽게 어울릴 수 있었어요. 다음에도 이 호스트님 모임 또 갈 것 같아요.'],['닉네임H','홍대 브런치 모임 참여','정산이 깔끔하고 아이스브레이커도 재밌어서 어색함이 금방 풀렸어요.']].map((r,i)=><Reveal key={r[0]} delay={i*80}><div className="card" style={{marginBottom:10}}>
            <div className="row" style={{gap:8,marginBottom:6}}><Av name={r[0]} size={28}/><b style={{fontSize:13.5}}>{r[0]}</b><span className="meta">· {r[1]}</span></div>
            <p style={{fontSize:13.5,color:'var(--c-sub)',margin:0,lineHeight:1.6}}>{r[2]}</p>
          </div></Reveal>)}</div>}
          <b style={{fontSize:16}}>이 호스트가 연 모임{otherMeetups.length>1&&<span className="meta" style={{fontWeight:400}}> — 현재 {otherMeetups.length}개 동시 운영 중</span>}</b>
          {otherMeetups.length===0?<p className="meta" style={{margin:'10px 0'}}>지금 진행 중인 다른 모임은 없어요.</p>:<div style={{margin:'14px 0'}}>{otherMeetups.map(mm=><button key={mm.id} className="card row" style={{width:'100%',gap:12,marginBottom:10,cursor:'pointer',fontFamily:'inherit',textAlign:'left'}} onClick={()=>go('detail',{detailId:mm.id})}><Ph h={52} w={52} r={10} label=""/><span style={{flex:1,fontSize:14.5}}><b>{mm.title}{mm.when?' · '+mm.when:''}</b><br/><span className="meta">{mm.capMax?(effectiveJoined(mm)+'/'+mm.capMax+'명 참여'):'모임 보기'}</span></span></button>)}</div>}
        </div>
        <div className="wpanel">
          <b style={{fontSize:13.5,color:'var(--c-primary)'}}>운영 가이드 준수 체크리스트 (참가자에게 사전 공개)</b>
          {['예상 개인 비용 — 식사·음료 등은 현장에서 각자 결제해요','취소 조건 — 모임 24시간 전까지 취소 가능 (이후 취소·노쇼는 다음 신청에 불이익)', m.photoPromoConsent?'사진 촬영 여부 — 촬영 있음 / 후기에 올라온 사진은 운영진 SNS 홍보에 쓰일 수 있음':'사진 촬영 여부 — 촬영 있음 / 참가자 동의 시 SNS 게시'].map(c=><div key={c} className="row" style={{gap:8,marginTop:10,fontSize:13.5}}><Ic n="IconNormalCheckNameCheckThickFalse" s={16} c="var(--c-primary)"/>{c}</div>)}
        </div>
      </div>
    </div>
  </div>;
}
function WScrChat({S,set,go}){
  const {useRef, useEffect} = React;
  const hosted=/^my\d+$/.test(S.detailId);
  const hm=hosted?S.myHostedMeetups[+S.detailId.slice(2)]:null;
  const m=hosted?{id:S.detailId, title:(hm&&hm.title)||'새 모임', place:(hm&&hm.place)||'장소 미정', capJoined:(hm&&hm.applicants||[]).filter(a=>a.status==='confirmed').length}:(MEETUPS[S.detailId]||(S.tempDetail&&S.tempDetail.id===S.detailId?S.tempDetail:null)||resolveMeetup(S.detailId,S));
  if (!m) return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain"><ChatNotFoundNotice go={go}/></div></div>;
  if (!hosted && S.joined[m.id]!=='confirmed') return <div className="wapp"><WNav S={S} go={go} set={set}/><div className="wmain"><ChatNotFoundNotice go={go} reason="참가가 확정된 모임만 채팅방을 이용할 수 있어요."/></div></div>;
  const chatHostName = (m.host && m.host.name) || '호스트';
  const sysBanner = [{sys:true, text:`${m.title||'새 모임'} 모임의 채팅방입니다`}];
  const baseMsgs=[...sysBanner, ...(hosted?[{who:'나',host:true,text:`${hm&&hm.title?hm.title:'새 모임'} 채팅방을 열었어요 — 참가자들이 모이면 여기서 공지해주세요.`},{who:'나',host:true,text:'이용수칙과 개인 비용 안내를 미리 공지해보세요.'}]:(DEMO_MODE?[{who:chatHostName,host:true,text:'환영해요! 다들 처음이라 어색할 수 있는데, 걱정 마세요. 도착하시면 ‘웰컴 테이블’로 안내해드릴게요.'},{who:chatHostName,host:true,ice:true,text:'미리 답해볼까요? — 유학 시절 가장 웃겼던 에피소드는?'},{who:'닉네임A',text:'저 첫 참여인데 8시에 가면 될까요? 조금 떨리네요'},{who:'나',me:true,text:'저도 혼자 가요! 웰컴 테이블에서 봬요'}]:[]))];
  // 18단계: 서버 채팅 모드 — 서버에 존재하는 모임이면 메시지를 서버에서 읽고 쓴다(실시간+10초 폴링)
  const chatMid = hosted ? (hm&&hm.serverId) : (isServerMeetupId(m.id) ? m.id : null);
  const serverMode = !!(chatMid && isServerMeetupId(chatMid));
  const myEmail = (S.acctEmail||'').toLowerCase();
  const hostMail = hosted ? myEmail : ((m.hostEmail||'')+'').toLowerCase();
  const [srvMsgs, setSrvMsgs] = React.useState([]);
  React.useEffect(()=>{
    if (!serverMode) return;
    let alive = true;
    const load = () => fetchChatMessages(chatMid).then(rows=>{ if (alive && rows) setSrvMsgs(rows); });
    load();
    const unsub = subscribeChatMessages(chatMid, row=>{ setSrvMsgs(prev=> prev.some(r=>r.id===row.id) ? prev : [...prev, row]); });
    const iv = setInterval(load, 10000);
    return ()=>{ alive=false; clearInterval(iv); if (unsub) unsub(); };
  }, [chatMid]);
  const chatMsgsByMeetup = S.chatMsgsByMeetup||{};
  const myMsgs = chatMsgsByMeetup[m.id]||[];
  const msgs = serverMode ? [...sysBanner, ...srvMsgs.map(r=>({who:r.sender_name||r.sender_email, me:(r.sender_email||'').toLowerCase()===myEmail, host:(r.sender_email||'').toLowerCase()===hostMail, admin:isAdminEmail(r.sender_email), text:r.body}))] : [...baseMsgs, ...myMsgs];
  const send=()=>{
    const text = S.chatInput.trim(); if(!text) return;
    if (serverMode) {
      set({chatInput:''});
      sendChatMessage(chatMid, S.acctEmail, S.profile.nickname||'나', text).then(res=>{
        if (res && res.error) { set({toast:'메시지 전송에 실패했어요 — 참가 확정자만 채팅을 쓸 수 있어요. 잠시 후 다시 시도해주세요.', chatInput:text}); return; }
        if (res && res.data) setSrvMsgs(prev=> prev.some(r=>r.id===res.data.id) ? prev : [...prev, res.data]);
      });
      return;
    }
    set({chatMsgsByMeetup:{...chatMsgsByMeetup, [m.id]:[...myMsgs,{who:'나',me:true,text}]}, chatInput:''});
  };
  const companion = !hosted ? getCompanion(m.id) : null;
  const askCompanion = () => {
    setCompanion(m.id, {status:'requested'});
    const q = '친구 한 명과 함께 가고 싶은데 괜찮을까요?';
    if (serverMode) { sendChatMessage(chatMid, S.acctEmail, S.profile.nickname||'나', q).then(res=>{ if (res && res.data) setSrvMsgs(prev=> prev.some(r=>r.id===res.data.id) ? prev : [...prev, res.data]); }); return; }
    set({chatMsgsByMeetup:{...chatMsgsByMeetup, [m.id]:[...myMsgs, {who:'나', me:true, text:q}]}});
  };
  const listRef = useRef(null);
  // 새 메시지·입장 시 맨 아래로 — 레이아웃이 끝난 다음 프레임에 스크롤해야 확실히 내려감(즉시 실행하면 간헐적으로 실패)
  useEffect(()=>{ const el=listRef.current; if(!el) return; requestAnimationFrame(()=>{ el.scrollTop = el.scrollHeight; }); }, [msgs.length]);
  return <div className="wapp"><WNav S={S} go={go} set={set}/>
    <div className="wmain">
      <button className="wback" onClick={()=>go(hosted?'mymeet':'detail')}><Ic n="IconNormalChevronLeftNameChevronLeftTightFalseThick" s={14}/>{hosted?'내 모임으로':'모임으로'}</button>
      <div className="wchat">
        <div className="wchatmain">
          <div style={{padding:'14px 20px',borderBottom:'1px solid var(--c-line)',display:'flex',justifyContent:'space-between',alignItems:'center'}}><b style={{fontSize:15}}>{m.title}</b><span className="meta">참가자 {m.capJoined||0}명</span></div>
          <div className="wchatlist" ref={listRef}>
            {msgs.length===0&&<div style={{textAlign:'center', padding:'32px 16px'}}>
              <p style={{fontSize:14, fontWeight:700, margin:'0 0 4px'}}>아직 시작된 대화가 없어요.</p>
              <p className="meta" style={{margin:0}}>참가가 확정된 모임의 대화가 여기에 표시돼요.</p>
            </div>}
            {msgs.map((msg,i)=>msg.sys?<div key={i} style={{textAlign:'center',margin:'10px 0'}}><span style={{background:'var(--c-fill)',color:'var(--c-sub)',fontSize:11.5,fontWeight:600,padding:'6px 12px',borderRadius:20,display:'inline-block'}}>{msg.text}</span></div>:<div key={i} className={'msg'+(msg.me?' me':'')} style={{display:'flex', width:'100%', justifyContent:msg.me?'flex-end':'flex-start', gap:8, marginBottom:12}}>
              {!msg.me&&<Av name={msg.who} size={32} tone={msg.host?'p':undefined}/>}
              <div style={{maxWidth:'70%', marginLeft:msg.me?'auto':0, flexShrink:0, minWidth:0}}>
                {!msg.me&&<div className="meta" style={{marginBottom:3}}>{msg.who}{msg.host&&<span className="chip green" style={{fontSize:10,padding:'1px 6px',marginLeft:5}}>인증 호스트</span>}{msg.admin&&<span style={{marginLeft:5}}><AdminBadge small/></span>}{msg.ice&&<span className="chip violet" style={{fontSize:10,padding:'1px 6px',marginLeft:5}}>아이스브레이커</span>}</div>}
                <div className="bubble" style={msg.me?{background:'var(--c-primary)', color:'#fff', borderRadius:14, padding:'10px 14px', display:'inline-block', whiteSpace:'pre-wrap', wordBreak:'break-word'}:{background:'var(--c-fill)', borderRadius:14, padding:'10px 14px', display:'inline-block', whiteSpace:'pre-wrap', wordBreak:'break-word'}}>{msg.text}</div>
              </div>
            </div>)}
            {!hosted&&(!companion||companion.status==='none')&&<button type="button" onClick={askCompanion} style={{background:'none',border:'none',color:'var(--c-faint)',fontFamily:'inherit',fontSize:12.5,cursor:'pointer',padding:'8px 4px',textDecoration:'underline'}}>친구 동반 참가 문의하기</button>}
            {!hosted&&companion&&companion.status!=='none'&&<CompanionDecisionCard m={m} S={S} set={set}/>}
          </div>
          <div style={{padding:'2px 0 6px', textAlign:'center'}}><span className="meta" style={{fontSize:11}}>사진·링크 전송은 신고·안전을 위해 제한될 수 있어요</span></div>
          <div className="wchatinput">
            <button className="iconbtn" aria-label="신고하기" onClick={()=>set({reportOpen:true, reportPrefill:m.title})}><Ic n="IconNormalFlagNameFlagFillFalse" s={20} c="var(--c-faint)"/></button>
            <input value={S.chatInput} onChange={e=>set({chatInput:e.target.value})} onKeyDown={e=>{if(e.key==='Enter') send();}} placeholder="메시지 보내기" style={{flex:1,background:'var(--c-fill)',border:'none',borderRadius:999,padding:'10px 16px',fontSize:14,fontFamily:'inherit',outline:'none'}}/>
            <button className="iconbtn" aria-label="메시지 보내기" onClick={send}><Ic n="IconNormalSendNameSendFillFillTrue" s={20} c="var(--c-primary)"/></button>
          </div>
        </div>
        <div className="wchatside">
          <div className="row" style={{gap:6,marginBottom:8}}><Ic n="IconNormalPin2NamePinFillFillTrue" s={14} c="var(--c-red)"/><b style={{fontSize:13}}>모임 이용수칙 (요약) — 고정됨</b></div>
          <div style={{fontSize:12.5,color:'var(--c-sub)',lineHeight:1.65,marginBottom:8}}>서로 존중 · 과도한 스킨십 금지 · 비용은 각자 결제 · 노쇼 시 신청 제한 · 신고 기능 상시 이용 가능</div>
          <button style={{background:'none',border:'none',color:'var(--c-primary)',fontSize:12.5,fontFamily:'inherit',padding:0,cursor:'pointer',marginBottom:20}} onClick={()=>set({rulesOpen:true})}>전체 수칙 다시보기</button>
          {S.chatEnded&&<div>
            <div style={{fontSize:11.5,color:'var(--c-faint)',marginBottom:14}}>— 모임 종료 · 이 채팅방은 2주 후 보관됩니다 —</div>
            <div className="card" style={{marginBottom:14}}>
              <b style={{fontSize:13.5}}>채팅방 안에서의 연락처 공유 기능</b>
              <p className="meta" style={{margin:'6px 0 0'}}>준비 중이에요. 연락처는 모임에서 직접 만나 서로 괜찮을 때 나누는 걸 권장해요.</p>
            </div>
            <DSBtn label="오늘 만난 사람 보기" variant="outlined" onClick={()=>go('postmeetup',{detailId:m.id, completed:[...new Set([...S.completed, m.id])]})}/>
          </div>}
        </div>
      </div>
    </div>
  </div>;
}
// 데모 전용 · 대기열 승계 테스트 패널 — 공개 모드 DOM에는 렌더링되지 않음(호출부에서 DEMO_MODE로 게이팅)
function WaitlistDemoPanel({m, S, set}) {
  const waitingCount = ((S.waitlists&&S.waitlists[m.id])||[]).length;
  return <div style={{marginTop:14, background:'var(--c-fill-a)', border:'1px dashed var(--c-line)', borderRadius:12, padding:'12px 14px'}}>
    <div style={{fontSize:11, color:'var(--c-faint)', fontWeight:700, marginBottom:8}}>데모 전용 · 대기열 승계 테스트 (현재 대기 {waitingCount}명)</div>
    <div style={{display:'flex', flexWrap:'wrap', gap:8}}>
      <button type="button" onClick={()=>set({waitlists:fillDemoWaitlist(m.id, S, 2), toast:'데모 대기자 2명을 추가했어요'})} style={{background:'var(--c-bg)', border:'1px solid var(--c-line)', borderRadius:8, padding:'8px 12px', minHeight:40, fontFamily:'inherit', fontSize:12, fontWeight:600, cursor:'pointer'}}>대기열에 데모 인원 채우기</button>
      <button type="button" onClick={()=>releaseSlotAndPromote(m.id, S, set, false)} style={{background:'var(--c-bg)', border:'1px solid var(--c-line)', borderRadius:8, padding:'8px 12px', minHeight:40, fontFamily:'inherit', fontSize:12, fontWeight:600, cursor:'pointer'}}>데모: 다른 참가자 취소(자리 반환)</button>
    </div>
  </div>;
}
Object.assign(window, {WScrDetail, WScrHost, WScrChat, WaitlistDemoPanel});
