function FeedBanner({ icon, title, sub, cta, tone = 'accent', onClick }) {
  const inkTone = tone === 'ink';
  return (
    <div onClick={onClick} style={{ display: 'flex', alignItems: 'center', gap: 12, background: inkTone ? 'var(--bg-ink)' : 'var(--surface-1)', border: inkTone ? 'none' : '1.5px dashed var(--text-accent)', borderRadius: 'var(--radius-card)', padding: '16px 18px', cursor: 'pointer', boxShadow: inkTone ? 'var(--shadow-float)' : 'none' }}>
      <div style={{ width: 40, height: 40, borderRadius: '50%', background: inkTone ? 'rgba(255,255,255,.14)' : 'var(--bg-accent)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <i className={'ti ti-' + icon} style={{ fontSize: 19, color: inkTone ? '#fff' : 'var(--text-accent)' }}></i>
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <span style={{ font: 'var(--text-body-strong)', color: inkTone ? '#fff' : 'var(--text-primary)', display: 'block', marginBottom: 2 }}>{title}</span>
        <span style={{ font: 'var(--text-small)', color: inkTone ? 'rgba(255,255,255,.66)' : 'var(--text-secondary)' }}>{sub}</span>
      </div>
      <span style={{ font: '600 13px/1 var(--font-family)', color: '#fff', background: 'var(--text-accent)', borderRadius: 999, padding: '10px 14px', flexShrink: 0 }}>{cta}</span>
    </div>
  );
}

function BadgeProgress({ onClick }) {
  return (
    <div onClick={onClick} style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: 18, boxShadow: 'var(--shadow-card)', cursor: 'pointer' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
        <div style={{ width: 42, height: 42, borderRadius: '50%', background: 'var(--bg-accent)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><i className="ti ti-soup" style={{ fontSize: 20, color: 'var(--text-accent)' }}></i></div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2 }}>국밥전문가까지 7 / 10</span>
          <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>국밥집 3곳만 더 올리면 배지를 받아요</span>
        </div>
        <i className="ti ti-chevron-right" style={{ fontSize: 17, color: 'var(--text-muted)', flexShrink: 0 }}></i>
      </div>
      <div style={{ width: '100%', height: 8, borderRadius: 999, background: 'var(--surface-2)', overflow: 'hidden' }}>
        <div style={{ width: '70%', height: '100%', borderRadius: 999, background: 'var(--text-accent)' }}></div>
      </div>
    </div>
  );
}

function UnknownStrip({ items, onPick }) {
  return (
    <div>
      <SectionTitle>이웃을 도와주세요</SectionTitle>
      <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '-6px 20px 12px' }}>아직 장소를 못 찾은 사진이에요. 아시는 곳이 있으면 알려주세요.</p>
      <div style={{ display: 'flex', gap: 10, padding: '0 20px 2px', overflowX: 'auto' }}>
        {items.map((it, i) => (
          <div key={it.user} onClick={() => onPick(it)} style={{ width: 132, flexShrink: 0, cursor: 'pointer' }}>
            <div style={{ width: '100%', aspectRatio: '1', borderRadius: 18, overflow: 'hidden', background: 'var(--surface-1)', marginBottom: 8, position: 'relative' }}>
              {it.src ? <img src={it.src} alt={it.hint} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> : <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--surface-2)' }}><i className="ti ti-photo" style={{ fontSize: 24, color: 'var(--text-muted)' }}></i></div>}
              <span style={{ position: 'absolute', left: 8, top: 8, width: 26, height: 26, borderRadius: '50%', background: 'rgba(255,255,255,.92)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className="ti ti-map-pin-question" style={{ fontSize: 13, color: 'var(--text-warning)' }}></i></span>
            </div>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-primary)', display: 'block', fontWeight: 600 }}>{it.user}</span>
            <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>{it.hint}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function HotStrip({ items, onPick }) {
  return (
    <div>
      <SectionTitle action="전체보기" onAction={() => onPick()}>이번 주 이 동네 핫한 곳</SectionTitle>
      <div style={{ display: 'flex', gap: 12, padding: '0 20px 2px', overflowX: 'auto' }}>
        {items.map((r, i) => (
          <div key={r.name} onClick={() => onPick(r)} style={{ width: 176, flexShrink: 0, background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', overflow: 'hidden', boxShadow: 'var(--shadow-card)', cursor: 'pointer' }}>
            <div style={{ width: '100%', height: 108, background: 'var(--surface-2)', position: 'relative', overflow: 'hidden' }}>
              {r.srcs && r.srcs.length > 1 ? (
                <div style={{ position: 'absolute', inset: 0, display: 'grid', gridTemplateColumns: '2fr 1fr', gridTemplateRows: '1fr 1fr', gap: 2, overflow: 'hidden' }}>
                  {[r.srcs[0], r.srcs[1], r.srcs[2] || r.srcs[1]].map((s, k) => (
                    <div key={k} style={{ position: 'relative', minWidth: 0, minHeight: 0, overflow: 'hidden', gridRow: k === 0 ? 'span 2' : 'auto', background: 'var(--surface-2)' }}>
                      <img src={s} alt={r.name} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                    </div>
                  ))}
                </div>
              ) : r.srcs ? <img src={r.srcs[0]} alt={r.name} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                : <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'var(--surface-2)' }}><i className="ti ti-photo" style={{ fontSize: 24, color: 'var(--text-muted)' }}></i></div>}
              <span style={{ position: 'absolute', left: 10, top: 10, font: '700 12px/1 var(--font-family)', color: '#fff', background: 'var(--text-accent)', borderRadius: 999, padding: '6px 10px' }}>{i + 1}위</span>
            </div>
            <div style={{ padding: '12px 14px 14px' }}>
              <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 4, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name}</span>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', display: 'block', marginBottom: 8 }}>{r.category} · {r.distance}</span>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-primary)', display: 'inline-flex', alignItems: 'center', gap: 3 }}><i className="ti ti-heart" style={{ fontSize: 11, color: 'var(--text-accent)' }}></i>{r.likes}</span>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)' }}>사진 {r.photos}</span>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function CollectionStrip({ items, onPick }) {
  return (
    <div>
      <SectionTitle action="전체보기" onAction={() => onPick()}>이웃이 만든 컬렉션</SectionTitle>
      <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '-6px 20px 12px' }}>이 동네를 잘 아는 사람들이 모아둔 공개 컬렉션이에요.</p>
      <div style={{ display: 'flex', gap: 12, padding: '0 20px 2px', overflowX: 'auto' }}>
        {items.map((c, i) => (
          <div key={c.name} onClick={() => onPick(c)} style={{ width: 214, flexShrink: 0, background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: 14, boxShadow: 'var(--shadow-card)', cursor: 'pointer' }}>
            <div style={{ display: 'flex', marginBottom: 12 }}>
              {[0, 1, 2].map(k => (
                <div key={k} style={{ width: 52, height: 52, borderRadius: 13, background: 'var(--surface-2)', border: '2px solid var(--surface-1)', marginLeft: k ? -14 : 0, position: 'relative', overflow: 'hidden', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{c.srcs && c.srcs[k] ? <img src={c.srcs[k]} alt={c.name} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} /> : <i className="ti ti-photo" style={{ fontSize: 16, color: 'var(--text-muted)' }}></i>}</div>
              ))}
              <span style={{ marginLeft: 'auto', alignSelf: 'flex-start', display: 'inline-flex', alignItems: 'center', gap: 4, font: 'var(--text-micro)', color: 'var(--text-accent)', background: 'var(--bg-accent)', borderRadius: 999, padding: '5px 9px', whiteSpace: 'nowrap' }}><i className="ti ti-map-pin" style={{ fontSize: 10 }}></i>내 주변 {c.near}곳</span>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginBottom: 4 }}>
              <i className={'ti ti-' + c.icon} style={{ fontSize: 13, color: 'var(--text-accent)', flexShrink: 0 }}></i>
              <span style={{ font: 'var(--text-body-strong)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</span>
            </div>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', display: 'block', marginBottom: 8 }}>@{c.owner}</span>
            <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>게시물 {c.count}개 · 장소 {c.places}곳</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function MissionPostsStrip({ items, onPick, onAll }) {
  return (
    <div>
      <SectionTitle action="전체보기" onAction={onAll}>이웃의 밀키트 인증샷</SectionTitle>
      <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '-6px 20px 12px' }}>이번 미션에 참여한 이웃들의 한 상이에요.</p>
      <div style={{ display: 'flex', gap: 10, padding: '0 20px 2px', overflowX: 'auto' }}>
        {items.map((p, i) => (
          <div key={p.user} onClick={() => onPick(p, i)} style={{ width: 148, flexShrink: 0, cursor: 'pointer' }}>
            <div style={{ width: '100%', aspectRatio: '3/4', borderRadius: 'var(--radius-card)', background: 'var(--surface-2)', position: 'relative', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 8, boxShadow: 'var(--shadow-card)' }}>
              {p.src ? <img src={p.src} alt={p.menu} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} /> : <i className="ti ti-photo" style={{ fontSize: 22, color: 'var(--text-muted)' }}></i>}
              <span style={{ position: 'absolute', left: 8, top: 8, font: '700 10px/1 var(--font-family)', color: '#fff', background: 'var(--text-accent)', borderRadius: 999, padding: '5px 8px', letterSpacing: '.03em' }}>MISSION</span>
              <span style={{ position: 'absolute', left: 8, bottom: 8, font: 'var(--text-micro)', color: '#fff', background: 'rgba(26,26,26,.68)', borderRadius: 999, padding: '4px 8px' }}>#{p.menu}</span>
              {p.shots && <span style={{ position: 'absolute', right: 8, top: 8, font: 'var(--text-micro)', color: '#fff', background: 'rgba(26,26,26,.68)', borderRadius: 999, padding: '4px 8px' }}>1/{p.shots}</span>}
            </div>
            <span style={{ font: 'var(--text-small)', fontWeight: 600, color: 'var(--text-primary)', display: 'block', marginBottom: 3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>@{p.user}</span>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)', display: 'inline-flex', alignItems: 'center', gap: 3 }}><i className="ti ti-heart" style={{ fontSize: 10 }}></i>{p.likes}</span>
              <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>{p.area}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function MissionPostCard({ post, onOpen, onLike, liked, onComments }) {
  const { Avatar } = window.SpotureDesignSystem_f7ad96;
  return (
    <div style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', overflow: 'hidden', boxShadow: 'var(--shadow-card)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '14px 16px' }}>
        <Avatar user={post.user} size={36} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>@{post.user}</span>
          <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>{post.area} · {post.time || '오늘'}</span>
        </div>
        <span style={{ flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 4, font: '700 10px/1 var(--font-family)', color: '#fff', background: 'var(--text-accent)', borderRadius: 999, padding: '6px 10px', letterSpacing: '.03em' }}><i className="ti ti-gift" style={{ fontSize: 11 }}></i>MISSION</span>
      </div>
      <div onClick={onOpen} style={{ width: '100%', aspectRatio: '4/3', background: 'var(--surface-2)', position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', overflow: 'hidden' }}>
        {post.src ? <img src={post.src} alt={post.menu} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} /> : <i className="ti ti-photo" style={{ fontSize: 26, color: 'var(--text-muted)' }}></i>}
        <span style={{ position: 'absolute', right: 10, top: 10, font: 'var(--text-micro)', color: '#fff', background: 'rgba(26,26,26,.68)', borderRadius: 999, padding: '5px 9px' }}>1/{post.shots || 3}</span>
        <span style={{ position: 'absolute', left: 10, bottom: 10, font: 'var(--text-micro)', color: '#fff', background: 'rgba(26,26,26,.68)', borderRadius: 999, padding: '5px 9px' }}>#{post.menu}</span>
      </div>
      <div style={{ padding: '13px 16px 15px' }}>
        <span style={{ font: 'var(--text-caption)', color: 'var(--text-primary)', display: 'block', marginBottom: 12, lineHeight: 1.6 }}>이달의 밀키트 인증샷 미션에 참여했어요. {post.menu} 밀키트로 한 상 차렸습니다.</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <span onClick={onLike} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, font: 'var(--text-small)', color: liked ? 'var(--text-accent)' : 'var(--text-secondary)', cursor: 'pointer' }}><i className={'ti ti-heart'} style={{ fontSize: 17 }}></i>{post.likes + (liked ? 1 : 0)}</span>
          <span onClick={onComments} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, font: 'var(--text-small)', color: 'var(--text-secondary)', cursor: 'pointer' }}><i className="ti ti-message-circle" style={{ fontSize: 17 }}></i>{post.comments}</span>
        </div>
      </div>
    </div>
  );
}

function MissionBanner({ onClick, onContinue, onDetail, onSecondary, joined, done, total, status }) {
  const st = status === 'progress' && done >= total ? 'review' : status;
  const review = st === 'review', rejected = st === 'rejected', settled = st === 'done' || st === 'announced';
  const chip = review ? ['심사 중', 'var(--text-warning)'] : rejected ? ['반려 1장', 'var(--text-danger)'] : settled ? ['응모 완료', 'var(--text-success)'] : joined ? ['참여 중', 'rgba(255,255,255,.16)'] : null;
  const line = review ? '사진 ' + total + '/' + total + ' 제출 · 결과 대기'
    : rejected ? '인증 ' + (total - 1) + '/' + total + ' 통과 · 1장 재촬영 필요'
    : st === 'announced' ? '응모권 2매 · 결과 발표됨'
    : st === 'done' ? '응모권 2매 · 8월 25일 추첨'
    : joined ? '인증 ' + done + '/' + total + ' · 남은 기간 6일'
    : '참여자 추첨 · 남은 기간 6일';
  const body = review ? '운영자가 사진을 확인하고 있어요. 보통 1~2일 안에 결과를 알려드려요.'
    : rejected ? '완성 한 상 사진에서 밀키트 패키지가 보이지 않았어요. 그 1장만 다시 올리면 돼요.'
    : st === 'announced' ? '추첨이 끝났어요. 내 결과와 당첨자 명단을 확인해 보세요.'
    : st === 'done' ? '심사를 모두 통과했어요. 추첨 결과는 알림으로 보내드려요.'
    : joined ? (total - done) + '장만 더 올리면 심사에 들어가요'
    : '사진 3장으로 밀키트 6종에 응모하세요';
  const primary = review ? '내 인증 사진 보기' : rejected ? '반려 사유 보기' : st === 'announced' ? '결과 확인하기' : st === 'done' ? '내 응모 내역' : joined ? '이어서 인증하기' : '지금 인증하기';
  const secondary = rejected ? '다시 올리기' : review || settled ? null : joined ? '미션 자세히' : '참여 방법 보기';
  const dotColor = k => rejected ? (k === total - 1 ? 'var(--text-danger)' : 'var(--text-success)')
    : review ? 'var(--text-warning)' : settled ? 'var(--text-success)' : k < done ? 'var(--text-accent)' : 'rgba(255,255,255,.16)';
  return (
    <div onClick={onClick} style={{ background: 'var(--bg-ink)', borderRadius: 'var(--radius-card)', overflow: 'hidden', boxShadow: 'var(--shadow-float)', cursor: 'pointer' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: 18, paddingBottom: joined ? 14 : 18 }}>
        <div style={{ width: 62, height: 62, borderRadius: 16, overflow: 'hidden', flexShrink: 0, background: 'rgba(255,255,255,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
          <i className="ti ti-photo" style={{ fontSize: 22, color: 'rgba(255,255,255,.5)' }}></i>
          {(review || rejected || settled) && (
            <span style={{ position: 'absolute', right: -3, bottom: -3, width: 24, height: 24, borderRadius: 999, background: review ? 'var(--text-warning)' : rejected ? 'var(--text-danger)' : 'var(--text-success)', border: '2px solid var(--bg-ink)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <i className={'ti ti-' + (review ? 'clock' : rejected ? 'photo-x' : 'ticket')} style={{ fontSize: 12, color: '#fff' }}></i>
            </span>
          )}
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
            <span style={{ font: 'var(--text-micro)', color: 'var(--text-accent)', fontWeight: 700, letterSpacing: '.04em' }}>SPONSORED MISSION</span>
            {chip && <span style={{ font: 'var(--text-micro)', fontWeight: 700, color: '#fff', background: chip[1], borderRadius: 999, padding: '3px 8px', whiteSpace: 'nowrap' }}>{chip[0]}</span>}
          </div>
          <span style={{ font: 'var(--text-body-strong)', color: '#fff', display: 'block', marginBottom: 3 }}>이달의 밀키트 인증샷</span>
          <span style={{ font: 'var(--text-small)', color: 'rgba(255,255,255,.62)' }}>{line}</span>
        </div>
        <i className="ti ti-chevron-right" style={{ fontSize: 18, color: 'rgba(255,255,255,.6)', flexShrink: 0 }}></i>
      </div>
      <div style={{ padding: '0 18px 18px' }}>
        {joined && (
          <div style={{ display: 'flex', gap: 6, marginBottom: 12 }}>
            {Array.from({ length: total }, (_, k) => (
              <div key={k} style={{ flex: 1, height: 5, borderRadius: 999, background: dotColor(k) }}></div>
            ))}
          </div>
        )}
        <span style={{ font: 'var(--text-small)', color: 'rgba(255,255,255,.62)', display: 'block', marginBottom: 12, lineHeight: 1.6 }}>{body}</span>
        <div style={{ display: 'flex', gap: 8 }}>
          <span onClick={(e) => { e.stopPropagation(); (review || settled ? onDetail : rejected ? onDetail : onContinue)(); }} style={{ flex: 1, textAlign: 'center', font: '600 13px/1 var(--font-family)', color: 'var(--bg-ink)', background: '#fff', borderRadius: 999, padding: '13px 0', cursor: 'pointer' }}>{primary}</span>
          {secondary && <span onClick={(e) => { e.stopPropagation(); (rejected ? onContinue : onDetail)(); }} style={{ flex: 1, textAlign: 'center', font: '600 13px/1 var(--font-family)', color: '#fff', background: 'rgba(255,255,255,.14)', borderRadius: 999, padding: '13px 0', cursor: 'pointer' }}>{secondary}</span>}
        </div>
      </div>
    </div>
  );
}

function Feed({ nav, msn, setMsn }) {
  const { ChipFilter, PhotoCard, RestaurantCard, Toast, BottomSheet, Button, Avatar } = window.SpotureDesignSystem_f7ad96;
  const fp = new URLSearchParams(location.search);
  const [scope, setScope] = React.useState(fp.get('scope') === 'following' ? '팔로잉' : '내 주변');
  const [sort, setSort] = React.useState('거리순');
  const scrollRef = React.useRef(null);
  const [scopeOpen, setScopeOpen] = React.useState(fp.get('scope') === 'open');
  const fpParam = fp.get('pull');
  const [pull, setPull] = React.useState(fpParam === 'ready' ? 88 : fpParam === 'pulling' ? 44 : fpParam === 'refreshing' ? 64 : fpParam === 'done' ? 56 : 0);
  const held = React.useRef(fpParam === 'refreshing' || fpParam === 'done');
  const [phase, setPhase] = React.useState(fpParam === 'refreshing' ? 'refreshing' : fpParam === 'done' ? 'done' : 'idle');
  const drag = React.useRef(null);
  const THRESH = 72;
  const onPullDown = (ev) => {
    if (phase === 'refreshing' || !scrollRef.current || scrollRef.current.scrollTop > 0) return;
    drag.current = { y: ev.clientY };
  };
  const onPullMove = (ev) => {
    const d = drag.current;
    if (!d) return;
    const dy = ev.clientY - d.y;
    if (dy <= 0) { setPull(0); return; }
    if (dy > 24) setScopeOpen(true);
    setPull(Math.min(120, dy * 0.55));
  };
  const onPullUp = () => {
    if (!drag.current) return;
    drag.current = null;
    held.current = false;
    if (pull >= THRESH) { setPhase('refreshing'); setPull(64); }
    else setPull(0);
  };
  React.useEffect(() => {
    if (held.current) return;
    if (phase === 'refreshing') { const t = setTimeout(() => { setPhase('done'); setPull(56); }, 1300); return () => clearTimeout(t); }
    if (phase === 'done') { const t = setTimeout(() => { setPhase('idle'); setPull(0); }, 1500); return () => clearTimeout(t); }
  }, [phase]);
  const ready = pull >= THRESH;
  const label = phase === 'refreshing' ? '새로 볶고 있어요' : phase === 'done' ? '새로고침 완료' : ready ? '놓으면 새로고침돼요' : '당겨서 새로고침';
  const fill = phase === 'idle' ? Math.min(1, pull / THRESH) : 1;
  const [followed, setFollowed] = React.useState({});
  const [cat, setCat] = React.useState('전체');
  const [toast, setToast] = React.useState(true);

  const notice = fp.get('notice');
  const toastKinds = ['toast', 'like', 'comment', 'reportToast', 'report', 'approvedToast', 'follow', 'missionApprovedToast', 'missionRejectedToast', 'missionNewToast', 'missionRemindToast', 'noticeToast', 'eventToast', 'maintenanceToast'];
  const [inapp, setInapp] = React.useState(toastKinds.includes(notice) ? ({ toast: 'comment', reportToast: 'report', report: 'report', approvedToast: 'approved', missionApprovedToast: 'missionApproved', missionRejectedToast: 'missionRejected', missionNewToast: 'missionNew', missionRemindToast: 'missionRemind', noticeToast: 'notice', eventToast: 'event', maintenanceToast: 'maintenance' }[notice] || notice) : null);
  const [alert, setAlert] = React.useState(['badge', 'approved', 'permission', 'missionApproved', 'missionRejected', 'missionNew', 'missionRemind'].includes(notice) ? notice : null);
  const sharePost = { user: '국밥러버92', isMine: true, place: '금강산초다리 역삼점', menu: '갈치조림', mediaCount: 4, srcs: ['../../assets/feed/jorim-1.jpeg', '../../assets/feed/jorim-2.jpeg', '../../assets/feed/jorim-3.jpeg', '../../assets/feed/mukeunji-jjim.jpeg'] };
  const demoPost = { place: '금강산초다리 역삼점', menu: '갈치조림', mediaCount: 4, likes: 128, comments: 24, time: '2시간 전', meal: '점심', caption: '갈치가 두툼하고 양념이 짜지 않아요.' };
  const [sheet, setSheet] = React.useState(fp.get('sheet') === 'hide' ? { type: 'hide', post: demoPost } : (fp.get('sheet') === 'share' || fp.get('save')) ? { type: 'sharePost', post: sharePost } : fp.get('sheet') === 'menu' ? { type: 'menu', post: demoPost } : fp.get('suggest') === '1' ? { type: 'suggest', post: { user: '야식탐험가' } } : fp.get('viewer') ? { type: 'viewer', post:
    fp.get('viewer') === 'unknown' ? { user: fp.get('mine') === '1' ? '국밥러버92' : '토박이야식', isMine: fp.get('mine') === '1', meal: '야식', category: '한식', likes: 17, comments: 9, imageId: 'feed-4', src: '../../assets/feed/gopchang.jpeg', tags: { 0: ['곱창', '막창'] } }
    : fp.get('viewer') === 'estimated' ? (fp.get('mine') === '0'
      ? { user: '야식탐험가', badges: [{ icon: 'moon-stars', label: '야식탐험가' }], isMine: false, estimatedLocation: '청년다방', reportCount: 2, meal: '점심', category: '분식', likes: 2, comments: 1, mediaCount: 2, imageId: 'feed-3', tags: { 0: ['로제떡볶이'], 1: ['순대'] } }
      : { user: '국밥러버92', isMine: true, estimatedLocation: '청년다방', reportCount: 2, meal: '점심', category: '분식', likes: 2, comments: 1, mediaCount: 2, imageId: 'feed-3', tags: { 0: ['로제떡볶이'], 1: ['순대'] } })
    : { user: '국밥러버92', place: '할매순대국 강남점', city: '서울 강남구', area: '역삼동', distance: '45m', caption: '20년째 같은 자리에서 하는 집인데 국물이 진짜 진해요. 순대는 따로 주문했고 깍두기가 계속 들어가서 두 번 리필했습니다. 점심에는 줄이 길어서 11시 반쯤 가는 게 좋아요.', meal: '저녁', category: '한식', likes: 128, comments: 24, mediaCount: 4, imageId: 'feed-0', tags: { 0: ['순대국', '공기밥'], 1: ['깍두기'], 2: ['내장탕'] } } } : fp.get('setplacedone') === '1' ? { type: 'setplaceDone', name: '할매순대국 강남점', info: { addr: '서울 강남구 테헤란로 27', detail: '2층' } } : fp.get('setplace') ? { type: 'setplace', post: { time: '2시간 전' } } : fp.get('confirm') === '1' ? { type: 'confirm' } : fp.get('direct') === '1' ? { type: 'direct' } : null);
  const [liked, setLiked] = React.useState({});
  const [area, setArea] = React.useState({ name: '역삼동', city: '강남구', radius: '500m', gps: true });
  const [areaSheet, setAreaSheet] = React.useState(false);
  const [areaToast, setAreaToast] = React.useState(null);
  const [delToast, setDelToast] = React.useState(null);
  const [removed, setRemoved] = React.useState({});
  const [hiddenMap, setHiddenMap] = React.useState({});
  const feed = [
    { user: '국밥러버92', badges: [{ icon: 'soup', label: '국밥전문가' }, { icon: 'map-pin-check', label: '위치제보왕' }, { icon: 'building-store', label: '동네박사' }], place: '금강산초다리 역삼점', city: '서울 강남구', area: '역삼동', distance: '60m', tags: { 0: ['갈치조림', '공기밥'], 1: ['계란찜'], 2: ['된장찌개'] }, meal: '저녁', category: '한식', likes: 128, comments: 24, mediaCount: 4, caption: '갈치가 두툼하고 양념이 짜지 않아요. 무가 푹 배어서 밥을 두 공기 먹었습니다. 점심에는 줄이 길어서 11시 반쯤 가는 게 좋아요.', src: '../../assets/feed/jorim-1.jpeg' },
    { user: '야식탐험가', badges: [{ icon: 'moon-stars', label: '야식탐험가' }, { icon: 'flame', label: '야식러버' }], place: '우나기야 역삼', city: '서울 강남구', area: '역삼동', distance: '90m', tags: { 0: ['히츠마부시'], 1: ['장어간모'], 2: ['우나기덮밥', '곱빼기'], 3: ['녹차'] }, meal: '저녁', category: '일식', likes: 91, comments: 12, mediaCount: 6, hasVideo: true, caption: '장어가 두툼하고 소스가 안 달아서 좋았어요. 밥 양이 적은 편이라 곱빼기 추천.', src: '../../assets/feed/unagi-don.jpeg' },
    { user: '카페인탐정', badges: [{ icon: 'coffee', label: '카페인탐정' }], place: '스시노아지 역삼', city: '서울 강남구', area: '역삼동', distance: '210m', tags: { 0: ['런치 오마카세'], 1: ['연어초밥'] }, meal: '점심', category: '일식', likes: 44, comments: 6, mediaCount: 2, caption: '런치 특선이 가성비 좋아요.', src: '../../assets/feed/sushi.jpeg' },
    { user: '야식탐험가', badges: [{ icon: 'moon-stars', label: '야식탐험가' }, { icon: 'flame', label: '야식러버' }], estimatedLocation: '묵은지김치찌개', reportCount: 2, status: '승인 대기중', tags: { 0: ['묵은지찜'], 1: ['계란말이'] }, meal: '점심', category: '한식', likes: 2, comments: 1, mediaCount: 2, caption: '묵은지가 두껍게 들어가서 밥이랑 먹기 좋았어요. 간판을 못 찍어서 위치는 추정으로 올립니다.', src: '../../assets/feed/mukeunji-jjim.jpeg' },
    { user: '국밥러버92', isMine: true, tags: { 0: ['곱창구이'], 1: ['볶음밥'] }, meal: '점심', category: '한식', likes: 3, comments: 0, mediaCount: 2, caption: '간판이 안 찍혀서 어디였는지 저도 헷갈려요. 역삼역 3번 출구 골목이었어요.', src: '../../assets/feed/gopchang.jpeg' },
    { user: '토박이야식', tags: { 0: ['로스카츠'] }, meal: '야식', category: '일식', likes: 17, comments: 9, caption: '튀김옷이 얇아서 느끼하지 않아요. 밤 11시까지 해서 야식으로 자주 갑니다.', src: '../../assets/feed/tonkatsu.jpeg' },
    { user: '떡볶이덕후', badges: [{ icon: 'bowl-chopsticks', label: '분식마스터' }, { icon: 'award', label: '게시물 50개' }], place: '역삼 참숯갈비', city: '서울 강남구', area: '역삼동', distance: '260m', tags: { 0: ['한우 모둠'], 1: ['된장찌개'], 2: ['냉면'] }, meal: '저녁', category: '한식', likes: 210, comments: 38, mediaCount: 3, caption: '숯불에 바로 구워주시는데 마블링이 사진보다 훨씬 예뻤어요. 2인에 된장찌개까지 시켜서 배부르게 먹었고, 주차는 건물 뒤편에 두 대 정도만 가능해요.', src: '../../assets/feed/hanwoo-raw.jpeg' },
    { user: '카페인탐정', badges: [{ icon: 'coffee', label: '카페인탐정' }], place: '해운대 오션뷰 브루잉', city: '부산 해운대구', area: '중동', distance: '다른 지역', tags: { 0: ['베리 스무디'] }, meal: '오후', category: '카페 · 음료', likes: 302, comments: 41, time: '방금', mediaCount: 1, caption: '해운대 앞바다 보면서 마신 베리 스무디. 얼음 갈린 게 거칠지 않고 산미가 딱 좋았어요. 창가 2인석은 오후 3시쯤 비니까 그때 노려보세요.\n음료는 5,800원.', src: '../../assets/feed/berry-smoothie.jpeg' }
  ];
  const oceanPost = feed.find(f => f.place === '해운대 오션뷰 브루잉');
  const hot = [
    { name: '금강산초다리 역삼점', category: '한식 · 생선조림', distance: '60m', photos: 34, likes: 214, srcs: ['../../assets/feed/jorim-1.jpeg', '../../assets/feed/jorim-2.jpeg', '../../assets/feed/jorim-3.jpeg'] },
    { name: '나무가나길 커피', category: '카페 · 베이커리', distance: '180m', photos: 52, likes: 331, srcs: ['../../assets/feed/cafe-1.jpeg', '../../assets/feed/cafe-2.jpeg', '../../assets/feed/cafe-3.jpeg'] },
    { name: '분짜하노이 역삼', category: '베트남 · 분짜', distance: '120m', photos: 18, likes: 96, srcs: ['../../assets/feed/buncha-1.jpeg', '../../assets/feed/buncha-3.jpeg', '../../assets/feed/buncha-2.jpeg'] }
  ];
  const missionPosts = [
    { user: '집밥연구소', menu: '스테이크 플래터', area: '역삼동', likes: 64, comments: 8, shots: 3, mediaCount: 3, src: '../../assets/feed/steak-platter.jpeg' },
    { user: '주말요리사', menu: '차돌짬뽕', area: '삼성동', likes: 41, comments: 5, shots: 3, mediaCount: 3, src: '../../assets/feed/jjamppong.jpeg' },
    { user: '야식탐험가', menu: '묵은지찜', area: '논현동', likes: 88, comments: 12, shots: 3, mediaCount: 3, src: '../../assets/feed/mukeunji-jjim.jpeg' },
    { user: '버거홀릭', menu: '수제버거', area: '역삼동', likes: 33, comments: 4, shots: 3, mediaCount: 3, src: '../../assets/feed/feed-1.jpeg' },
    { user: '동네박사', menu: '붓카케 우동', area: '대치동', likes: 57, comments: 7, shots: 3, mediaCount: 3, src: '../../assets/feed/udon.jpeg' }
  ];
  const unknown = [
    { user: '밥순이', hint: '점심 · 일식', src: '../../assets/feed/udon.jpeg' },
    { user: '국밥마니아', hint: '저녁 · 일식', src: '../../assets/feed/tonkatsu.jpeg' },
    { user: '역삼동토박이', hint: '점심 · 중식', src: '../../assets/feed/jjamppong.jpeg' }
  ];
  const only = fp.get('only');
  const card = (p, i) => (removed[i] || hiddenMap[i] || (only === 'mine-nolocation' && !(p.isMine && !p.place && !p.estimatedLocation))) ? null : (
    <PhotoCard key={p.user + i} {...p} imageId={'feed-' + i} liked={!!liked[i]} likes={p.likes + (liked[i] ? 1 : 0)}
      onLike={(force) => setLiked(l => ({ ...l, [i]: force ? true : !l[i] }))}
      onMenu={() => setSheet({ type: 'menu', post: { ...p, hidden: !!hiddenMap[i] }, idx: i })}
      onComments={() => setSheet({ type: 'comments', post: p })}
      onBadges={() => setSheet({ type: 'badges', post: p })}
      onUser={() => p.user === '국밥러버92' ? nav('profile') : nav('userProfile', { name: p.user })}
      onOpen={() => setSheet({ type: 'viewer', post: { ...p, imageId: 'feed-' + i }, idx: i })}
      onMap={() => (p.place && !p.estimatedLocation) ? nav('restaurantDetail') : (p.isMine && !p.estimatedLocation) ? setSheet({ type: 'setplace', post: p, idx: i }) : setSheet({ type: 'suggest', post: p })} />
  );
  return (
    <div style={{ flex: '1 1 0%', minHeight: 0, display: 'flex', flexDirection: 'column', position: 'relative' }}>
      <StatusBar />
      <div ref={scrollRef} onPointerDown={onPullDown} onPointerMove={onPullMove} onPointerUp={onPullUp} onPointerCancel={onPullUp} style={{ flex: '1 1 0%', minHeight: 0, overflowY: 'auto', paddingBottom: 104, touchAction: 'pan-y' }}>
        <div style={{ height: pull, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: drag.current ? 'none' : 'height .22s cubic-bezier(.22,.8,.28,1)' }}>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, opacity: Math.min(1, pull / 40) }}>
              <div style={{ position: 'relative', width: 52, height: 34, display: 'flex', alignItems: 'flex-end', justifyContent: 'center', animation: phase === 'refreshing' ? 'sppan .42s ease-in-out infinite' : 'none' }}>
              {[0, 1, 2].map(k => (
                <span key={k} style={{ position: 'absolute', bottom: 15, left: 8 + k * 9, width: 7, height: 7, borderRadius: '50%', background: phase === 'done' ? 'var(--text-success)' : 'var(--text-accent)', opacity: fill > (k * 0.3) ? 1 : .18, animation: phase === 'refreshing' ? 'sptoss .5s ease-in-out ' + (k * 0.1) + 's infinite' : 'none', transition: 'opacity .2s ease' }}></span>
              ))}
              <span style={{ position: 'absolute', right: 0, bottom: 11, width: 20, height: 4, borderRadius: 999, background: 'var(--surface-ink)', transformOrigin: 'left center', transform: 'rotate(-14deg)' }}></span>
              <div style={{ width: 34, height: 17, borderRadius: '0 0 17px 17px', background: 'var(--surface-ink)', marginRight: 14, flexShrink: 0 }}></div>
              {phase === 'done' && <i className="ti ti-check" style={{ position: 'absolute', left: 14, bottom: 3, fontSize: 12, color: '#fff' }}></i>}
            </div>
            <span style={{ font: 'var(--text-micro)', color: 'var(--text-secondary)', whiteSpace: 'nowrap' }}>{label}</span>
          </div>
        </div>
        <div style={{ maxHeight: scopeOpen ? 60 : 0, overflow: 'hidden', transition: 'max-height .24s cubic-bezier(.22,.8,.28,1)', marginBottom: scopeOpen ? 16 : 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 18, padding: '10px 20px 14px', borderBottom: '1px solid var(--border)' }}>
          {['내 주변', '팔로잉'].map(s => (
            <span key={s} onClick={() => setScope(s)} style={{ font: scope === s ? '700 17px/1.2 var(--font-family)' : '400 17px/1.2 var(--font-family)', color: scope === s ? 'var(--text-primary)' : 'var(--text-muted)', cursor: 'pointer', paddingBottom: 12, marginBottom: -15, borderBottom: scope === s ? '2px solid var(--text-accent)' : '2px solid transparent' }}>{s}</span>
          ))}
        </div>
        </div>
        {scope === '내 주변' ? (<React.Fragment>
        <div style={{ padding: '0 20px 18px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, background: 'var(--surface-1)', borderRadius: 'var(--radius-chip)', padding: 6, boxShadow: 'var(--shadow-card)' }}>
            {['거리순', '최신순', '인기순'].map(s => (
              <span key={s} onClick={() => setSort(s)} style={{ flex: 1, textAlign: 'center', font: sort === s ? '600 14px/1 var(--font-family)' : '400 14px/1 var(--font-family)', color: sort === s ? '#fff' : 'var(--text-secondary)', background: sort === s ? 'var(--bg-ink)' : 'transparent', borderRadius: 999, padding: '12px 0', cursor: 'pointer', transition: 'all .16s ease' }}>{s}</span>
            ))}
          </div>
          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 6, marginTop: 12, paddingLeft: 4 }}>
            <i className="ti ti-current-location" style={{ fontSize: 15, color: 'var(--text-accent)', flexShrink: 0, marginTop: 2 }}></i>
            <span style={{ flex: '1 1 0%', minWidth: 0, font: 'var(--text-caption)', color: 'var(--text-secondary)', lineHeight: 1.45, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', textWrap: 'pretty' }}>{area.city} {area.name} 반경 {area.radius}{area.gps ? '' : ' · 직접 선택'}</span>
            <span onClick={() => setAreaSheet(true)} style={{ flexShrink: 0, font: 'var(--text-caption)', color: 'var(--text-primary)', borderBottom: '1px solid var(--text-primary)', cursor: 'pointer', marginLeft: 4, fontWeight: 600, lineHeight: 1.45, whiteSpace: 'nowrap' }}>지역 변경</span>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8, padding: '0 20px 18px', overflowX: 'auto' }}>
          {['전체', '한식', '분식', '카페·디저트', '멕시칸', '야식'].map(c => <ChipFilter key={c} label={c} active={cat === c} onClick={() => setCat(c)} />)}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          <FeedBanner icon="map-pin-question" title="내 게시물에 제보 3건이 기다려요" sub="맞는 곳을 고르면 위치가 채워져요" cta="확인" onClick={() => setSheet({ type: 'confirm' })} />
          {card(feed[0], 0)}
          {card(oceanPost, feed.indexOf(oceanPost))}
          {card(feed[1], 1)}
        </div>
        <div style={{ margin: '22px 0' }}>
          <UnknownStrip items={unknown} onPick={(it) => setSheet({ type: 'suggest', post: it })} />
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          {card(feed[2], 2)}
          {card(feed[3], 3)}
        </div>
        <div style={{ margin: '22px 0' }}>
          <HotStrip items={hot} onPick={(r) => nav(r ? 'restaurantDetail' : 'hotPlaces')} />
        </div>
        <div style={{ margin: '22px 0' }}>
          <CollectionStrip items={[
            { name: '역삼 수제버거 지도', owner: '버거홀릭', count: 12, places: 9, near: 3, icon: 'bowl-chopsticks', srcs: ['../../assets/feed/burger-set.jpeg', '../../assets/feed/burger-bacon.jpeg', '../../assets/feed/burger-double.jpeg'] },
            { name: '혼밥 가능한 곳', owner: '야식탐험가', count: 8, places: 8, near: 2, icon: 'user', srcs: ['../../assets/feed/udon.jpeg', '../../assets/feed/tonkatsu.jpeg', '../../assets/feed/jjamppong.jpeg'] },
            { name: '늦게까지 하는 카페', owner: '카페인탐정', count: 15, places: 12, near: 5, icon: 'coffee', srcs: ['../../assets/feed/cafe-1.jpeg', '../../assets/feed/cafe-2.jpeg', '../../assets/feed/cafe-3.jpeg'] }
          ]} onPick={(c) => nav(c ? 'collectionDetailOther' : 'publicCollections')} />
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          <BadgeProgress onClick={() => nav('profile')} />
          {card(feed[4], 4)}
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8, flexWrap: 'wrap' }}>
              <span style={{ font: 'var(--text-micro)', fontWeight: 700, color: 'var(--text-muted)', letterSpacing: '.04em', marginRight: 2 }}>DEMO · 카드 상태</span>
              {[['참여 중 1/3', 'progress', 1], ['심사 중', 'review', 3], ['반려', 'rejected', 3], ['응모 완료', 'done', 3], ['결과 발표', 'announced', 3]].map(([label, st, d]) => {
                const on = ((msn && msn.status) || 'progress') === st;
                return <span key={st} onClick={() => setMsn && setMsn({ status: st, done: d })} style={{ font: '600 11px/1 var(--font-family)', color: on ? '#fff' : 'var(--text-secondary)', background: on ? 'var(--bg-ink)' : 'var(--surface-1)', border: '1px solid var(--border)', borderRadius: 999, padding: '7px 10px', cursor: 'pointer' }}>{label}</span>;
              })}
            </div>
            <MissionBanner joined done={(msn && msn.done) || 1} total={3} status={(msn && msn.status) || 'progress'} onContinue={() => nav('uploadMission')} onDetail={() => nav((msn && msn.status) === 'rejected' ? 'missionRejection' : 'missionDetail')} onClick={() => nav('missionDetail')} />
          </div>
          <MissionBanner joined={false} done={0} total={3} status="new" onContinue={() => nav('uploadMission')} onDetail={() => nav('missionDetail')} onClick={() => nav('missionDetail')} />
        </div>
        <div style={{ margin: '22px 0' }}>
          <MissionPostsStrip items={missionPosts} onAll={() => nav('missionPosts')} onPick={(p, i) => setSheet({ type: 'viewer', post: { ...p, mission: true, imageId: 'mp-' + i } })} />
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          <MissionPostCard post={{ ...missionPosts[2], time: '3시간 전' }} liked={!!liked.mp} onLike={() => setLiked(l => ({ ...l, mp: !l.mp }))}
            onOpen={() => setSheet({ type: 'viewer', post: { ...missionPosts[2], mission: true, imageId: 'mp-2' } })}
            onComments={() => setSheet({ type: 'comments', post: missionPosts[2] })} />
          {card(feed[5], 5)}
        </div>
        </React.Fragment>) : (<React.Fragment>
        <div style={{ display: 'flex', gap: 14, padding: '6px 20px 20px', overflowX: 'auto', overflowY: 'visible', scrollbarWidth: 'none' }}>
          {[{ n: '국밥마니아', new: true }, { n: '야식탐험가', new: true }, { n: '카페인탐정', new: true }, { n: '역삼동토박이' }, { n: '면요리연구소' }, { n: '분식왕' }].map(u => (
            <div key={u.n} onClick={() => nav('userProfile', { name: u.n })} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 7, flexShrink: 0, width: 60, cursor: 'pointer' }}>
              <div style={{ margin: 4, borderRadius: '50%', flexShrink: 0, boxShadow: u.new ? '0 0 0 2px var(--bg-app), 0 0 0 4px var(--text-accent)' : 'none' }}><Avatar user={u.n} size={54} /></div>
              <span style={{ font: 'var(--text-micro)', color: u.new ? 'var(--text-primary)' : 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 60, textAlign: 'center' }}>{u.n}</span>
            </div>
          ))}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '0 20px 18px' }}>
          <i className="ti ti-users" style={{ fontSize: 15, color: 'var(--text-accent)' }}></i>
          <span style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)' }}>팔로잉 128명 · 오늘 새 글 3개</span>
          <span onClick={() => nav('followsFollowing')} style={{ font: 'var(--text-caption)', color: 'var(--text-primary)', borderBottom: '1px solid var(--text-primary)', cursor: 'pointer', marginLeft: 'auto', fontWeight: 600 }}>팔로잉 관리</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          {card(oceanPost, feed.indexOf(oceanPost))}
          {card(feed[1], 1)}
          {card(feed[3], 3)}
        </div>
        <div style={{ margin: '22px 0 22px' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', padding: '0 20px 12px' }}>
            <span style={{ font: 'var(--text-body-strong)' }}>이런 이웃은 어때요?</span>
            <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>같은 동네 · 취향 비슷</span>
          </div>
          <div style={{ display: 'flex', gap: 10, padding: '0 20px', overflowX: 'auto', scrollbarWidth: 'none' }}>
            {[{ n: '순대국순례자', s: '역삼동 · 국밥 62개', b: 'soup' }, { n: '밤9시의라멘', s: '선릉 · 야식 41개', b: 'moon-stars' }, { n: '커피는하루세잔', s: '역삼동 · 카페 88개', b: 'coffee' }].map(u => (
              <div key={u.n} style={{ flexShrink: 0, width: 168, background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: 16, boxShadow: 'var(--shadow-card)', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 9 }}>
                <div onClick={() => nav('userProfile', { name: u.n })} style={{ cursor: 'pointer', display: 'flex' }}><Avatar user={u.n} size={52} /></div>
                <div style={{ textAlign: 'center', minWidth: 0, width: '100%' }}>
                  <div style={{ font: 'var(--text-body-strong)', marginBottom: 3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{u.n}</div>
                  <div style={{ font: 'var(--text-micro)', color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{u.s}</div>
                </div>
                <span onClick={() => setFollowed(f => ({ ...f, [u.n]: !f[u.n] }))} style={{ width: '100%', textAlign: 'center', font: '600 13px/1 var(--font-family)', color: followed[u.n] ? 'var(--text-secondary)' : '#fff', background: followed[u.n] ? 'var(--surface-2)' : 'var(--text-accent)', border: followed[u.n] ? '1px solid var(--border)' : 'none', borderRadius: 999, padding: '10px 0', cursor: 'pointer', boxSizing: 'border-box' }}>{followed[u.n] ? '팔로잉' : '팔로우'}</span>
              </div>
            ))}
          </div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 22, padding: '0 20px' }}>
          {card(feed[4], 4)}
          <MissionPostCard post={{ ...missionPosts[0], time: '어제' }} liked={!!liked.fmp} onLike={() => setLiked(l => ({ ...l, fmp: !l.fmp }))}
            onOpen={() => setSheet({ type: 'viewer', post: { ...missionPosts[0], mission: true, imageId: 'fmp-0' } })}
            onComments={() => setSheet({ type: 'comments', post: missionPosts[0] })} />
        </div>
        <div style={{ margin: '26px 20px 0', padding: '26px 20px', background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', boxShadow: 'var(--shadow-card)', textAlign: 'center' }}>
          <div style={{ width: 52, height: 52, borderRadius: '50%', background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 13px' }}><i className="ti ti-check" style={{ fontSize: 23, color: 'var(--text-muted)' }}></i></div>
          <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 6 }}>오늘 올라온 글은 다 봤어요</span>
          <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '0 0 16px', lineHeight: 1.6 }}>내 주변 탭에서 아직 안 본 동네 사진을 볼 수 있어요.</p>
          <span onClick={() => setScope('내 주변')} style={{ display: 'inline-block', font: '600 14px/1 var(--font-family)', color: '#fff', background: 'var(--bg-ink)', borderRadius: 999, padding: '13px 22px', cursor: 'pointer' }}>내 주변 보기</span>
        </div>
        </React.Fragment>)}
      </div>
      {inapp && <InAppToast kind={inapp} onClose={() => setInapp(null)} onAction={() => { setInapp(null); if (inapp && inapp.startsWith('mission')) nav(inapp === 'missionRejected' ? 'missionRejection' : 'missions'); else if (inapp === 'report') setSheet({ type: 'confirm' }); else if (inapp === 'like' || inapp === 'approved') setSheet({ type: 'viewer', post: { ...feed[0], imageId: 'feed-0' } }); else if (inapp === 'comment') setSheet({ type: 'comments', post: feed[0] }); else if (inapp === 'follow') nav('profile'); }} />}
      {alert && <InAppAlert kind={alert} onClose={() => setAlert(null)} onPrimary={() => { setAlert(null); if (alert && alert.startsWith('mission')) nav(alert === 'missionRejected' ? 'missionRejection' : alert === 'missionApproved' ? 'missionDetail' : 'missions'); else if (alert === 'report') setSheet({ type: 'confirm' }); else if (alert === 'badge') nav('profile'); else if (alert === 'approved') setSheet({ type: 'viewer', post: { ...feed[0], imageId: 'feed-0' } }); }} />}
      {toast && <Toast message="새 사진이 등록됐어요. 위치를 확인해 주세요." icon='map-pin' onClose={() => setToast(false)} />}
      {areaSheet && <FeedAreaSheet area={area} onClose={() => setAreaSheet(false)} onApply={a => { setArea(a); setAreaSheet(false); setAreaToast(a); }} />}
      {areaToast && <Toast message={(areaToast.gps ? '현재 위치' : areaToast.city + ' ' + areaToast.name) + ' 반경 ' + areaToast.radius + ' 피드로 바꿨어요'} icon='map-pin' onClose={() => setAreaToast(null)} />}
      {sheet && sheet.type === 'confirm' && <ConfirmLocationSheet onClose={() => setSheet(null)} onBlock={(p) => setSheet({ type: 'block', post: { user: p } })} onDirect={() => setSheet({ type: 'direct' })} onDone={() => { setSheet(null); setToast(true); }} />}
      {sheet && sheet.type === 'direct' && <SetPlaceSheet mode="register" onClose={() => setSheet({ type: 'confirm' })} onDone={(name) => setSheet({ type: 'suggestDone', name, mode: 'register' })} />}
      {sheet === 'report' && (
        <BottomSheet title="위치 제보 3건" onClose={() => setSheet(null)} footer={<div><Button variant="primary" onClick={() => setSheet(null)}>이 위치로 확정하기</Button><div style={{ marginTop: 8 }}><Button variant="ghost" size="sm" onClick={() => setSheet(null)}>직접 검색해서 등록할게요</Button></div></div>}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 18 }}>
            <RestaurantCard name="청년다방 역삼점" category="분식" distance="80m" proposals={2} selected imageId="rep-1" />
            <RestaurantCard name="역삼동 국밥집" category="한식" distance="180m" proposals={1} imageId="rep-2" />
          </div>
        </BottomSheet>
      )}
      {sheet && sheet.type === 'suggest' && <SetPlaceSheet mode="suggest" post={sheet.post} onClose={() => setSheet(null)} onDone={(name) => setSheet({ type: 'suggestDone', name, mode: 'suggest' })} />}
      {sheet && sheet.type === 'suggestDone' && <SuggestDoneModal name={sheet.name} mode={sheet.mode} onClose={() => setSheet(null)} />}
      {sheet && sheet.type === 'badges' && <BadgeTooltip post={sheet.post} onClose={() => setSheet(null)} />}
      {sheet && sheet.type === 'viewer' && <PostViewer post={sheet.post} onClose={() => setSheet(null)} onComments={() => setSheet({ type: 'comments', post: sheet.post })} onMenu={() => setSheet({ type: 'menu', post: sheet.post, idx: sheet.idx, fromViewer: true })} onPlace={() => nav('restaurantDetail')} onSuggest={() => setSheet({ type: 'suggest', post: sheet.post })} onConfirm={() => setSheet({ type: 'confirm' })} onUser={() => { setSheet(null); sheet.post.user === '국밥러버92' ? nav('profile') : nav('userProfile', { name: sheet.post.user }); }} />}
      {sheet && sheet.type === 'menu' && (sheet.post && sheet.post.user === '국밥러버92'
        ? <MyPostMenuSheet post={sheet.post} {...(sheet.fromViewer ? { liked: !!liked[sheet.idx], onLike: () => setLiked(l => ({ ...l, [sheet.idx]: !l[sheet.idx] })), onComments: () => setSheet({ type: 'comments', post: sheet.post }) } : {})} onClose={() => setSheet(null)} onEdit={() => { setSheet(null); nav('editPost', { post: sheet.post }); }} onHide={() => setSheet({ type: 'hide', post: sheet.post, idx: sheet.idx })} onPhotos={() => setSheet({ type: 'photos', post: sheet.post, idx: sheet.idx })} onShare={() => setSheet({ type: 'sharePost', post: sheet.post })} onDelete={() => setSheet({ type: 'delete', post: sheet.post, idx: sheet.idx })} />
        : <PostMenuSheet post={sheet.post} {...(sheet.fromViewer ? { liked: !!liked[sheet.idx], onLike: () => setLiked(l => ({ ...l, [sheet.idx]: !l[sheet.idx] })), onComments: () => setSheet({ type: 'comments', post: sheet.post }) } : {})} onClose={() => setSheet(null)} onReport={() => setSheet({ type: 'report2', post: sheet.post })} onShare={() => setSheet({ type: 'sharePost', post: sheet.post })} />)}
      {sheet && sheet.type === 'hide' && <HidePostConfirm post={sheet.post} hidden={!!sheet.post.hidden} onCancel={() => setSheet({ type: 'menu', post: sheet.post, idx: sheet.idx })} onConfirm={(v) => { if (sheet.idx != null) setHiddenMap(m => ({ ...m, [sheet.idx]: v === 'private' })); setSheet(null); setDelToast('공개 범위를 ' + visMeta(effVis(accountVis(), v)).label + '로 바꿨어요'); }} />}
      {sheet && sheet.type === 'photos' && <EditPhotosSheet post={sheet.post} onClose={() => setSheet({ type: 'menu', post: sheet.post, idx: sheet.idx })} onDone={(n, left) => { setSheet(null); setDelToast('사진 ' + n + '장을 지웠어요 · ' + left + '장 남음'); }} onDeletePost={() => setSheet({ type: 'delete', post: sheet.post, idx: sheet.idx })} />}
      {sheet && sheet.type === 'delete' && <DeletePostConfirm post={sheet.post} onCancel={() => setSheet({ type: 'menu', post: sheet.post, idx: sheet.idx })} onConfirm={() => { if (sheet.idx != null) setRemoved(r => ({ ...r, [sheet.idx]: true })); setSheet(null); setDelToast('게시물을 삭제했어요'); }} />}
      {sheet && sheet.type === 'setplace' && <SetPlaceSheet post={sheet.post} onClose={() => setSheet(null)} onDone={(name, info) => setSheet({ type: 'setplaceDone', name, info: info || {} })} />}
      {sheet && sheet.type === 'setplaceDone' && <SetPlaceDoneModal name={sheet.name} addr={sheet.info.addr} detail={sheet.info.detail} onClose={() => setSheet(null)} onView={() => { setSheet(null); nav('restaurantDetail'); }} />}
      {delToast && <Toast message={delToast} icon={delToast.indexOf('지정') > -1 ? 'map-pin-check' : delToast.indexOf('숨') > -1 ? 'eye-off' : delToast.indexOf('수정') > -1 ? 'check' : 'trash'} onClose={() => setDelToast(null)} />}
      {sheet && sheet.type === 'sharePost' && <SharePostSheet post={sheet.post} onClose={() => setSheet(null)} />}
      {sheet && sheet.type === 'report2' && <ReportSheet post={sheet.post} onClose={() => setSheet(null)} onDone={() => { setSheet(null); setToast(true); }} />}
      {sheet && sheet.type === 'comments' && <CommentsSheet post={sheet.post} onClose={() => setSheet(null)} onReport={(c) => setSheet({ type: 'report2', post: c })} onBlock={(c) => setSheet({ type: 'block', post: c })} />}
      {sheet && sheet.type === 'block' && <BlockConfirm name={(sheet.post && sheet.post.user) || '사용자'} onCancel={() => setSheet(null)} onConfirm={() => setSheet(null)} />}
    </div>
  );
}
window.Feed = Feed;
