function Upload({ nav, mission, msn, setMsn }) {
  const { Button, ProgressBar, ChipFilter, RestaurantCard, Badge, BottomSheet } = window.SpotureDesignSystem_f7ad96;
  const params = new URLSearchParams(location.search);
  const [step, setStep] = React.useState(Number(params.get('step')) || 1);
  const [gps, setGps] = React.useState(params.get('gps') !== '0');
  const acctVis = accountVis();
  const [vis, setVis] = React.useState(params.get('pvis') || 'public');
  const [visOpen, setVisOpen] = React.useState(params.get('pvisopen') === '1');
  const [q, setQ] = React.useState(params.get('nq') || '');
  const [gq, setGq] = React.useState(params.get('gq') || '');
  const [gpsPick, setGpsPick] = React.useState(null);
  const [region, setRegion] = React.useState(params.get('reg') || 'seoul');
  const [regOpen, setRegOpen] = React.useState(params.get('regopen') === '1');
  const [manOpen, setManOpen] = React.useState(params.get('manual') === '1');
  const [manual, setManual] = React.useState(null);
  const [ngPick, setNgPick] = React.useState(null);
  const [pinOpen, setPinOpen] = React.useState(params.get('pin') === '1');
  const [delOpen, setDelOpen] = React.useState(params.get('del') === '1');
  const [deleted, setDeleted] = React.useState(params.get('del') === 'done');
  const [leaveTo, setLeaveTo] = React.useState(params.get('leave') === '1' ? (mission ? 'missionDetail' : 'feed') : null);
  const [pinQ, setPinQ] = React.useState(params.get('pinq') || '');
  const msnBefore = (msn && msn.done) || 0;
  const msnAfter = Math.min(3, msnBefore + photoCount);
  React.useEffect(() => {
    if (!mission || step !== 3 || deleted || !setMsn) return;
    setMsn({ status: msnAfter >= 3 ? 'review' : 'progress', done: msnAfter });
  }, [mission, step, deleted]);
  const [pinned, setPinned] = React.useState(null);
  const [pinAddr, setPinAddr] = React.useState('서울 강남구 테헤란로 132');
  const [pinLabel, setPinLabel] = React.useState(null);
  const [pinDetail, setPinDetail] = React.useState('');
  const [moved, setMoved] = React.useState(null);
  const [kind, setKind] = React.useState('addr');
  const josa = (w) => { const c = w.charCodeAt(w.length - 1) - 44032; const j = (c >= 0 && c < 11172) ? c % 28 : 0; return (j !== 0 && j !== 8) ? '으로' : '로'; };
  const [meal, setMeal] = React.useState('저녁');
  const [picked, setPicked] = React.useState(0);
  const [active, setActive] = React.useState(0);
  const [addOpen, setAddOpen] = React.useState(false);
  const [caption, setCaption] = React.useState('');
  const [tagInput, setTagInput] = React.useState('');
  const [media, setMedia] = React.useState([
    { id: 'up-m0', kind: 'photo', src: '../../assets/feed/jorim-1.jpeg' }, { id: 'up-m1', kind: 'photo', src: '../../assets/feed/jorim-2.jpeg' }, { id: 'up-m2', kind: 'photo', src: '../../assets/feed/jorim-3.jpeg' }
  ]);
  const photoCount = media.filter(m => m.kind === 'photo').length;
  const videoCount = media.filter(m => m.kind === 'video').length;
  const candidates = [
    { name: '갈치조림', count: 9 }, { name: '생선조림', count: 5 }, { name: '조림국물', count: 4 },
    { name: '무조림', count: 3 }, { name: '공기밥', count: 2 }, { name: '깍두기', count: 2 }, { name: '막걸리', count: 1 }
  ];
  const [tags, setTags] = React.useState(mission ? { 0: ['밀키트 패키지'], 1: ['조리 과정'] } : { 0: ['갈치조림'], 1: ['공기밥'] });
  const activeTags = tags[active] || [];
  const addTag = (raw) => {
    const v = (raw || '').trim().replace(/^#+/, '').slice(0, 15);
    if (!v) return;
    setTags(s => { const cur = s[active] || []; return cur.includes(v) || cur.length >= 5 ? s : Object.assign({}, s, { [active]: cur.concat(v) }); });
    setTagInput('');
  };
  const onTagKey = (ev) => {
    if (ev.key === 'Enter' || ev.key === ',') { ev.preventDefault(); addTag(tagInput); }
    else if (ev.key === 'Backspace' && !tagInput && activeTags.length) removeTag(activeTags[activeTags.length - 1]);
  };
  const removeTag = (t) => setTags(s => ({ ...s, [active]: (s[active] || []).filter(x => x !== t) }));
  const removeMedia = (idx) => {
    if (media.length <= 1) return;
    setMedia(list => list.filter((_, k) => k !== idx));
    setTags(s => { const next = {}; Object.keys(s).forEach(k => { const n = Number(k); if (n < idx) next[n] = s[k]; else if (n > idx) next[n - 1] = s[k]; }); return next; });
    setActive(a => (idx < a || a > media.length - 2) ? Math.max(0, a - 1) : a);
  };
  const [colOpen, setColOpen] = React.useState(false);
  const [cols, setCols] = React.useState([]);
  const myCollections = [
    { name: '역삼 국밥 지도', count: 12, icon: 'soup' },
    { name: '혼밥 가능', count: 8, icon: 'user' },
    { name: '데이트 코스', count: 5, icon: 'heart' },
    { name: '야식 리스트', count: 17, icon: 'moon-stars' }
  ];
  const toggleCol = (n) => setCols(s => s.includes(n) ? s.filter(x => x !== n) : [...s, n]);
  const places = [
    { name: '할매순대국 강남점', category: '한식', distance: '45m' },
    { name: '역삼동 국밥집', category: '한식', distance: '180m' },
    { name: '청년다방 역삼점', category: '분식', distance: '80m' }
  ];
  const wider = [
    { name: '할매순대국 본점', category: '한식', distance: '1.2km' },
    { name: '역삼 참숯갈비', category: '한식', distance: '260m' },
    { name: '우나기야 역삼', category: '일식', distance: '90m' },
    { name: '스시노아지 역삼', category: '일식', distance: '210m' },
    { name: '나무가나길 커피', category: '카페', distance: '180m' },
    { name: '분짜하노이 역삼', category: '베트남', distance: '120m' }
  ];
  const hit = (p) => (p.name + ' ' + p.category).replace(/\s/g, '').includes(gq.replace(/\s/g, ''));
  const results = gq ? places.concat(wider).filter(hit) : [];
  const reg = window.SPOTURE_REGION(region);
  const regPlaces = window.SPOTURE_PLACES_BY_REGION[region] || [];
  const gpsRegion = 'seoul';
  const away = region !== gpsRegion;
  const gHit = (p) => (p.name + ' ' + p.category + ' ' + p.area).replace(/\s/g, '').includes(gq.replace(/\s/g, ''));
  const gList = away ? (gq ? regPlaces.filter(gHit) : regPlaces) : (gq ? results : places);
  const chosen = (away && manual ? manual.name : null) || gpsPick || (!away && picked != null ? places[picked].name : null);
  const nHit = (p) => (p.name + ' ' + p.category + ' ' + p.area).replace(/\s/g, '').includes(q.replace(/\s/g, ''));
  const nResults = q ? regPlaces.filter(nHit) : regPlaces;
  const ngChosen = manual ? manual.name : ngPick;
  const placeName = mission ? null : ((gps ? chosen : (ngChosen || null)) || pinned || null);
  const tagCount = Object.keys(tags).reduce((n, k) => n + (tags[k] || []).length, 0);
  const dirty = step < 3 && !deleted && (caption.trim().length > 0 || tagCount > 0 || cols.length > 0 || step === 2);
  const tryLeave = (dest) => { if (dirty) setLeaveTo(dest); else nav(dest); };
  const RegionRow = ({ auto }) => (
    <div onClick={() => setRegOpen(true)} style={{ display: 'flex', alignItems: 'center', gap: 12, background: 'var(--surface-1)', border: '1.5px solid ' + (auto && !away ? 'var(--border)' : 'var(--border-accent)'), borderRadius: 'var(--radius-tile)', padding: '13px 14px', boxShadow: 'var(--shadow-card)', cursor: 'pointer' }}>
      <div style={{ width: 36, height: 36, borderRadius: 12, flexShrink: 0, background: 'var(--bg-accent)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className={reg.domestic ? 'ti ti-map-pin' : 'ti ti-world'} style={{ fontSize: 17, color: 'var(--text-accent)' }}></i></div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)', display: 'block', marginBottom: 2 }}>{auto ? (away ? '사진 위치와 다른 지역을 검색 중' : '사진 위치에서 확인된 지역') : '검색할 지역'}</span>
        <span style={{ font: 'var(--text-body-strong)' }}>{reg.city} · {reg.country}</span>
      </div>
      <span style={{ font: 'var(--text-small)', color: 'var(--text-accent)', flexShrink: 0, display: 'inline-flex', alignItems: 'center', gap: 3 }}>변경<i className="ti ti-chevron-right" style={{ fontSize: 14 }}></i></span>
    </div>
  );
  const ManualCTA = () => (
    <div onClick={() => setManOpen(true)} style={{ display: 'flex', alignItems: 'center', gap: 12, background: 'var(--surface-2)', border: '1px dashed var(--border-strong)', borderRadius: 'var(--radius-card)', padding: 14, cursor: 'pointer' }}>
      <div style={{ width: 40, height: 40, borderRadius: 12, flexShrink: 0, background: 'var(--surface-1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className="ti ti-plus" style={{ fontSize: 19, color: 'var(--text-accent)' }}></i></div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2 }}>찾는 곳이 없나요? 직접 등록</span>
        <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>{reg.city}에 새 가게로 추가해요</span>
      </div>
      <i className="ti ti-chevron-right" style={{ fontSize: 16, color: 'var(--text-secondary)', flexShrink: 0 }}></i>
    </div>
  );
  const ManualChip = () => (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, background: 'var(--bg-accent)', border: '1.5px solid var(--text-accent)', borderRadius: 'var(--radius-card)', padding: 14 }}>
      <div style={{ width: 40, height: 40, borderRadius: 12, flexShrink: 0, background: 'var(--surface-1)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><i className="ti ti-writing" style={{ fontSize: 18, color: 'var(--text-accent)' }}></i></div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{manual.name}</span>
        <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>{manual.category} · {manual.area} · 직접 등록</span>
      </div>
      <i className="ti ti-x" onClick={() => setManual(null)} style={{ fontSize: 17, color: 'var(--text-secondary)', cursor: 'pointer', flexShrink: 0 }}></i>
    </div>
  );
  React.useEffect(() => { window.__uploadGuard = dirty ? tryLeave : null; return () => { window.__uploadGuard = null; }; }, [dirty, caption, tagCount, cols.length, step]);
  return (
    <div style={{ flex: '1 1 0%', minHeight: 0, display: 'flex', flexDirection: 'column', position: 'relative' }}>
      <StatusBar />
      <ScreenHeader title={step === 1 ? (mission ? '미션 인증 사진' : '사진 선택') : step === 2 ? '음식점 확인' : '등록 완료'} sub={mission ? '단계 ' + (step === 1 ? 1 : 2) + ' / 2 · 밀키트 미션' : step === 2 ? (gps ? '단계 2 · 위치 확인됨' : '단계 2 · 위치 정보 없음') : '단계 ' + step + ' / 3'} onBack={step === 1 ? () => tryLeave(mission ? 'missionDetail' : 'feed') : () => setStep(mission ? 1 : step - 1)} />
      <div style={{ padding: '0 20px 20px' }}><ProgressBar value={mission ? (step === 1 ? 50 : 100) : step * 33.4} /></div>
      <div style={{ flex: '1 1 0%', minHeight: 0, overflowY: 'auto', padding: '0 20px 20px' }}>
        {step === 1 && (
          <div>
            {mission && (
              <div onClick={() => nav('missionDetail')} style={{ display: 'flex', alignItems: 'center', gap: 12, background: 'var(--bg-ink)', borderRadius: 'var(--radius-card)', padding: 14, marginBottom: 14, cursor: 'pointer' }}>
                <div style={{ width: 40, height: 40, borderRadius: 12, background: 'rgba(255,255,255,.12)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><i className="ti ti-gift" style={{ fontSize: 19, color: 'var(--text-accent)' }}></i></div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ font: 'var(--text-micro)', color: 'var(--text-accent)', fontWeight: 700, letterSpacing: '.04em', display: 'block', marginBottom: 3 }}>MISSION</span>
                  <span style={{ font: 'var(--text-body-strong)', color: '#fff', display: 'block', marginBottom: 2 }}>이달의 밀키트 인증샷</span>
                  <span style={{ font: 'var(--text-small)', color: 'rgba(255,255,255,.6)' }}>{'#밀키트 태그 자동 부착 · 남은 인증 ' + Math.max(0, 3 - msnBefore) + '장'}</span>
                </div>
                <i className="ti ti-chevron-right" style={{ fontSize: 17, color: 'rgba(255,255,255,.5)', flexShrink: 0 }}></i>
              </div>
            )}
            <div style={{ position: 'relative', borderRadius: 'var(--radius-card)', overflow: 'hidden', background: 'var(--surface-1)', aspectRatio: '4/5', marginBottom: 12, boxShadow: 'var(--shadow-card)' }}>
              {media[active].src ? <img src={media[active].src} alt="" 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: 26, color: 'var(--text-muted)' }}></i></div>}
              <span style={{ position: 'absolute', top: 12, left: 12, font: '600 12px/1 var(--font-family)', color: '#fff', background: 'rgba(26,26,26,.66)', backdropFilter: 'blur(8px)', borderRadius: 999, padding: '8px 12px', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
                <i className={media[active].kind === 'video' ? 'ti ti-player-play' : 'ti ti-photo'} style={{ fontSize: 12 }}></i>
                {media[active].kind === 'video' ? '동영상' : (active + 1) + '/' + photoCount}
              </span>
              {media.length > 1 && (
                <div onClick={() => removeMedia(active)} style={{ position: 'absolute', top: 12, right: 12, display: 'inline-flex', alignItems: 'center', gap: 6, font: '600 12px/1 var(--font-family)', color: '#fff', background: 'rgba(26,26,26,.66)', backdropFilter: 'blur(8px)', borderRadius: 999, padding: '8px 12px', cursor: 'pointer' }}>
                  <i className="ti ti-trash" style={{ fontSize: 13 }}></i>이 사진 지우기
                </div>
              )}
            </div>
            <div style={{ background: 'var(--surface-1)', border: '1px solid ' + (activeTags.length ? 'var(--border-accent)' : 'var(--border)'), borderRadius: 'var(--radius-tile)', padding: '10px 12px', marginBottom: 12 }}>
              <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 7 }}>
                {activeTags.map(t => (
                  <span key={t} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, background: 'var(--bg-accent)', borderRadius: 999, padding: '7px 8px 7px 11px', font: '600 13px/1 var(--font-family)', color: 'var(--text-accent)' }}>
                    #{t}
                    <i className="ti ti-x" onClick={() => removeTag(t)} style={{ fontSize: 13, cursor: 'pointer', opacity: .7 }}></i>
                  </span>
                ))}
                {activeTags.length < 5 && (
                  <input value={tagInput} onChange={ev => setTagInput(ev.target.value.slice(0, 15))} onKeyDown={onTagKey} onBlur={() => addTag(tagInput)} placeholder={activeTags.length ? '하나 더' : '메뉴 태그 입력 · Enter로 추가'} style={{ flex: '1 1 96px', minWidth: 96, border: 'none', outline: 'none', background: 'transparent', font: 'var(--text-body)', color: 'var(--text-primary)', fontFamily: 'var(--font-family)', padding: '6px 2px' }} />
                )}
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8, paddingTop: 8, borderTop: '1px solid var(--border)' }}>
                <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)', flexShrink: 0 }}>{media[active].kind === 'video' ? '동영상' : '사진 ' + (active + 1)} · {activeTags.length}/5</span>
              </div>
            </div>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)', display: 'block', margin: '-6px 0 12px', paddingLeft: 4 }}>{mission ? '패키지 · 조리 과정 · 완성 한 상 사진에 각각 태그를 달아주세요' : '사진마다 메뉴 태그를 달면 이웃이 찾기 쉬워요'}</span>
            <div style={{ display: 'flex', gap: 8, marginBottom: 8, overflowX: 'auto', padding: '3px 3px 5px' }}>
              {media.map((m, i) => (
                <div key={m.id} onClick={() => setActive(i)} style={{ position: 'relative', width: 64, height: 64, borderRadius: 14, overflow: 'hidden', flexShrink: 0, cursor: 'pointer', background: 'var(--surface-1)', boxShadow: i === active ? 'inset 0 0 0 2px var(--text-accent)' : 'none' }}>
                  {m.src ? <img src={m.src} alt="" 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: 26, color: 'var(--text-muted)' }}></i></div>}
                  {m.kind === 'video' && <i className="ti ti-player-play" style={{ position: 'absolute', right: 4, bottom: 4, fontSize: 11, color: '#fff', background: 'rgba(26,26,26,.72)', borderRadius: '50%', padding: 3 }}></i>}
                  {(tags[i] || []).length > 0 && <span style={{ position: 'absolute', left: 5, top: 5, width: 7, height: 7, borderRadius: '50%', background: 'var(--text-accent)', boxShadow: '0 0 0 1.5px #fff' }}></span>}
                  {media.length > 1 && (
                    <span onClick={(e) => { e.stopPropagation(); removeMedia(i); }} style={{ position: 'absolute', right: 3, top: 3, width: 20, height: 20, borderRadius: '50%', background: 'rgba(26,26,26,.72)', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', zIndex: 2 }}><i className="ti ti-x" style={{ fontSize: 11, color: '#fff' }}></i></span>
                  )}
                  <span style={{ position: 'absolute', inset: 0 }}></span>
                </div>
              ))}
              <div onClick={() => setAddOpen(v => !v)} style={{ position: 'relative', width: 64, height: 64, borderRadius: 14, flexShrink: 0, border: '1.5px dashed ' + (addOpen ? 'var(--text-accent)' : 'var(--border-strong)'), display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 2, cursor: 'pointer' }}>
                <i className="ti ti-plus" style={{ fontSize: 16, color: addOpen ? 'var(--text-accent)' : 'var(--text-muted)' }}></i>
                <span style={{ font: 'var(--text-micro)', color: addOpen ? 'var(--text-accent)' : 'var(--text-muted)' }}>추가</span>
              </div>
            </div>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)', display: 'block', marginBottom: 22 }}>사진 {photoCount}/5 · 동영상 {videoCount}/1 (30초 이내){media.length <= 1 ? ' · 사진은 1장 이상 필요해요' : ''}</span>
            {!mission && (
              <React.Fragment>
                <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 10 }}>언제 먹었나요?</span>
                <div style={{ display: 'flex', gap: 8, marginBottom: 24 }}>
                  {['아침', '점심', '저녁', '야식'].map(m => <ChipFilter key={m} label={m} active={meal === m} onClick={() => setMeal(m)} />)}
                </div>
              </React.Fragment>
            )}
            <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 10 }}>어떤 점이 좋았나요?</span>
            <div style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: '16px 18px', boxShadow: 'var(--shadow-card)', marginBottom: 8 }}>
              <textarea value={caption} onChange={ev => setCaption(ev.target.value.slice(0, 300))} placeholder="맛, 분위기, 추천 메뉴를 자유롭게 적어주세요" rows={4} style={{ width: '100%', border: 'none', outline: 'none', background: 'transparent', resize: 'none', font: 'var(--text-body)', lineHeight: 1.6, color: 'var(--text-primary)', fontFamily: 'var(--font-family)' }}></textarea>
              <div style={{ display: 'flex', justifyContent: 'flex-end' }}><span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)' }}>{caption.length}/300</span></div>
            </div>
            <span style={{ font: 'var(--text-body-strong)', display: 'block', margin: '22px 0 10px' }}>어떤 컬렉션에 담을까요?</span>
            <div onClick={() => setColOpen(true)} style={{ display: 'flex', alignItems: 'center', gap: 10, background: 'var(--surface-1)', border: '1px solid ' + (cols.length ? 'var(--border-accent)' : 'var(--border)'), borderRadius: 'var(--radius-chip)', padding: '14px 16px', cursor: 'pointer', boxShadow: 'var(--shadow-card)' }}>
              <i className={cols.length ? 'ti ti-bookmarks' : 'ti ti-bookmark'} style={{ fontSize: 16, color: cols.length ? 'var(--text-accent)' : 'var(--text-muted)', flexShrink: 0 }}></i>
              <span style={{ flex: 1, minWidth: 0, font: 'var(--text-caption)', color: cols.length ? 'var(--text-accent)' : 'var(--text-secondary)', fontWeight: cols.length ? 600 : 400, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{cols.length ? cols.join(', ') : '컬렉션 선택 (선택 사항)'}</span>
              {cols.length > 1 && <span style={{ font: 'var(--text-micro)', color: 'var(--text-accent)', flexShrink: 0 }}>{cols.length}개</span>}
              <i className="ti ti-chevron-down" style={{ fontSize: 16, color: 'var(--text-muted)', flexShrink: 0 }}></i>
            </div>
            <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)', display: 'block', margin: '8px 0 0', paddingLeft: 4 }}>나중에 프로필 컬렉션에서도 담을 수 있어요</span>
          </div>
        )}
        {step === 2 && gps && (
          <div>
            <div style={{ marginBottom: 14 }}>
              <div style={{ background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: 16, boxShadow: 'var(--shadow-card)' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
                  <i className="ti ti-map-pin" style={{ fontSize: 18, color: 'var(--text-accent)' }}></i>
                  <span style={{ font: 'var(--text-body-strong)' }}>사진의 촬영 위치</span>
                </div>
                <div style={{ height: 130, borderRadius: 16, background: 'var(--surface-2)', position: 'relative', overflow: 'hidden' }}>
                  <div style={{ position: 'absolute', top: '42%', left: '46%', width: 16, height: 16, borderRadius: '50%', background: 'var(--text-accent)', boxShadow: '0 0 0 8px rgba(227,30,36,.16)' }}></div>
                </div>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', display: 'block', marginTop: 10 }}>서울 강남구 강남대로 123 근처</span>
              </div>
              <div style={{ margin: '14px 0 0' }}><RegionRow auto /></div>
              {away && (
                <div style={{ display: 'flex', gap: 10, background: 'var(--bg-warning)', border: '1px solid var(--border)', borderRadius: 'var(--radius-tile)', padding: '12px 14px', marginTop: 10 }}>
                  <i className="ti ti-info-circle" style={{ fontSize: 15, color: 'var(--text-warning)', flexShrink: 0, marginTop: 1 }}></i>
                  <span style={{ flex: 1, minWidth: 0, font: 'var(--text-small)', color: 'var(--text-secondary)', lineHeight: 1.6 }}>사진 위치는 서울이지만 {reg.city}에서 검색하고 있어요. <span onClick={() => { setRegion(gpsRegion); setGq(''); setGpsPick(null); setManual(null); setPicked(0); }} style={{ color: 'var(--text-accent)', fontWeight: 600, cursor: 'pointer' }}>사진 위치로 되돌리기</span></span>
                </div>
              )}
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: 'var(--surface-1)', borderRadius: 'var(--radius-chip)', padding: '14px 18px', boxShadow: 'var(--shadow-card)', margin: '14px 0 0' }}>
                <i className="ti ti-search" style={{ fontSize: 17, color: 'var(--text-muted)', flexShrink: 0 }}></i>
                <input value={gq} onChange={ev => { setGq(ev.target.value); setPicked(null); setGpsPick(null); }} placeholder={away ? reg.city + '에서 음식점 검색' : '음식점 이름이나 주소로 찾기'} style={{ flex: 1, minWidth: 0, border: 'none', outline: 'none', background: 'transparent', font: 'var(--text-body)', color: 'var(--text-primary)', fontFamily: 'var(--font-family)' }} />
                {gq && <i className="ti ti-x" onClick={() => { setGq(''); setGpsPick(null); }} style={{ fontSize: 16, color: 'var(--text-muted)', cursor: 'pointer', flexShrink: 0 }}></i>}
              </div>
              {away && manual && <div style={{ marginTop: 14 }}><ManualChip /></div>}
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, margin: '18px 0 0' }}>
                <span style={{ font: 'var(--text-body-strong)' }}>{gq ? '검색 결과' : away ? reg.city + '에서 많이 찾는 곳' : '이 중에 맞는 곳이 있나요?'}</span>
                {(gq || away) && <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)' }}>{gList.length}곳</span>}
              </div>
            </div>
            {window.SPOTURE_MANY_BRANCHES && window.SPOTURE_MANY_BRANCHES(gList, gq) && (
              <div style={{ display: 'flex', gap: 8, background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '10px 12px', marginBottom: 10 }}>
                <i className="ti ti-info-circle" style={{ fontSize: 15, color: 'var(--text-muted)', flexShrink: 0, marginTop: 1 }}></i>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', lineHeight: 1.6 }}>지점이 많아요. 점포명이나 동을 붙여 주세요</span>
              </div>
            )}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 14 }}>
              {gList.map((p, i) => {
                const byName = away || !!gq;
                return <RestaurantCard key={p.name} name={p.name} category={p.category} distance={away ? p.area : p.distance} imageId={'up-p' + i} selected={byName ? gpsPick === p.name : picked === i} onClick={() => { if (byName) { setGpsPick(p.name); setPicked(null); setManual(null); } else { setPicked(i); setGpsPick(null); } }} />;
              })}
              {gList.length === 0 && (
                <div style={{ textAlign: 'center', padding: '30px 0 6px' }}>
                  <i className="ti ti-map-pin-question" style={{ fontSize: 30, color: 'var(--text-muted)' }}></i>
                  <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '10px 0 14px', lineHeight: 1.6 }}>{gq ? reg.city + '에서 「' + gq + '」를 찾지 못했어요' : reg.city + '에 등록된 가게가 없어요'}{away && <React.Fragment><br />아직 등록되지 않은 가게일 수 있어요</React.Fragment>}</p>
                  {!away && <Button variant="secondary" size="sm" onClick={() => setPinOpen(true)}><i className="ti ti-map-2" style={{ fontSize: 16 }}></i>지도에서 직접 핀 찍기</Button>}
                </div>
              )}
            </div>
            {away && <div style={{ marginBottom: 14 }}><ManualCTA /></div>}
            {!gq && !away && (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                <Button variant="secondary" size="sm" onClick={() => setPinOpen(true)}><i className="ti ti-map-2" style={{ fontSize: 16 }}></i>지도에서 직접 핀 찍기</Button>
                <Button variant="ghost" size="sm" onClick={() => { setPicked(null); setGpsPick(null); setPinned(null); setStep(3); }}>위치 없이 올리고 이웃에게 물어보기</Button>
              </div>
            )}
          </div>
        )}
        {step === 2 && !gps && (
          <div>
            <div style={{ display: 'flex', gap: 12, background: 'var(--bg-warning)', border: '1px solid var(--border)', borderRadius: 'var(--radius-card)', padding: 16, marginBottom: 16 }}>
              <i className="ti ti-map-pin-off" style={{ fontSize: 19, color: 'var(--text-warning)', flexShrink: 0, marginTop: 1 }}></i>
              <div style={{ minWidth: 0 }}>
                <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 4 }}>사진에 위치 정보가 없어요</span>
                <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: 0, lineHeight: 1.6 }}>먼저 사진을 찍은 도시를 고르면 그 도시에서만 검색해요. 해외에서 찍은 사진도 찾을 수 있어요.</p>
              </div>
            </div>
            <div style={{ marginBottom: 10 }}><RegionRow /></div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: 'var(--surface-1)', borderRadius: 'var(--radius-chip)', padding: '14px 18px', boxShadow: 'var(--shadow-card)', marginBottom: 18 }}>
              <i className="ti ti-search" style={{ fontSize: 17, color: 'var(--text-muted)', flexShrink: 0 }}></i>
              <input value={q} onChange={ev => { setQ(ev.target.value); setNgPick(null); }} placeholder={reg.city + '에서 음식점 검색'} style={{ flex: 1, minWidth: 0, border: 'none', outline: 'none', background: 'transparent', font: 'var(--text-body)', color: 'var(--text-primary)', fontFamily: 'var(--font-family)' }} />
              {q && <i className="ti ti-x" onClick={() => { setQ(''); setNgPick(null); }} style={{ fontSize: 16, color: 'var(--text-muted)', cursor: 'pointer', flexShrink: 0 }}></i>}
            </div>
            {manual && <div style={{ marginBottom: 14 }}><ManualChip /></div>}
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 10 }}>
              <span style={{ font: 'var(--text-body-strong)' }}>{q ? '검색 결과' : reg.domestic ? '최근 방문한 곳' : reg.city + '에서 많이 찾는 곳'}</span>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)' }}>{nResults.length}곳</span>
            </div>
            {window.SPOTURE_MANY_BRANCHES && window.SPOTURE_MANY_BRANCHES(nResults, q) && (
              <div style={{ display: 'flex', gap: 8, background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '10px 12px', marginBottom: 10 }}>
                <i className="ti ti-info-circle" style={{ fontSize: 15, color: 'var(--text-muted)', flexShrink: 0, marginTop: 1 }}></i>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', lineHeight: 1.6 }}>지점이 많아요. 점포명이나 동을 붙여 주세요</span>
              </div>
            )}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 14 }}>
              {nResults.map((p, i) => <RestaurantCard key={p.name} name={p.name} category={p.category} distance={p.area} imageId={'up-n' + i} selected={ngChosen === p.name} onClick={() => { setNgPick(p.name); setManual(null); }} />)}
              {nResults.length === 0 && (
                <div style={{ textAlign: 'center', padding: '30px 0 6px' }}>
                  <i className="ti ti-map-pin-question" style={{ fontSize: 30, color: 'var(--text-muted)' }}></i>
                  <p style={{ font: 'var(--text-caption)', color: 'var(--text-secondary)', margin: '10px 0 0', lineHeight: 1.6 }}>{q ? reg.city + '에서 「' + q + '」를 찾지 못했어요' : reg.city + '에 등록된 가게가 없어요'}<br />아직 등록되지 않은 가게일 수 있어요</p>
                </div>
              )}
            </div>
            <div style={{ marginBottom: 14 }}><ManualCTA /></div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {reg.domestic && <Button variant="secondary" size="sm" onClick={() => setPinOpen(true)}><i className="ti ti-map-2" style={{ fontSize: 16 }}></i>지도에서 직접 핀 찍기</Button>}
              <Button variant="ghost" size="sm" onClick={() => { setPinned(null); setNgPick(null); setManual(null); setStep(3); }}>위치 없이 올리고 이웃에게 물어보기</Button>
            </div>
          </div>
        )}
        {step === 3 && deleted && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', paddingTop: 30 }}>
            <div style={{ width: 84, height: 84, borderRadius: '50%', background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 22 }}><i className="ti ti-trash" style={{ fontSize: 34, color: 'var(--text-muted)' }}></i></div>
            <span style={{ font: 'var(--text-title)', display: 'block', marginBottom: 8 }}>삭제했어요</span>
            <p style={{ font: 'var(--text-body)', color: 'var(--text-secondary)', margin: '0 0 22px', lineHeight: 1.6 }}>방금 올린 게시물이 피드에서 사라지고<br />사진도 서버에서 지워지고 있어요</p>
            <div style={{ alignSelf: 'stretch', display: 'flex', gap: 10, background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '13px 14px', textAlign: 'left' }}>
              <i className="ti ti-device-mobile" style={{ fontSize: 15, color: 'var(--text-secondary)', flexShrink: 0, marginTop: 1 }}></i>
              <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)', lineHeight: 1.6 }}>기기 사진은 그대로 있어요. 언제든 다시 올릴 수 있어요.</span>
            </div>
          </div>
        )}
        {step === 3 && !deleted && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', paddingTop: 30 }}>
            <div style={{ width: 84, height: 84, borderRadius: '50%', background: 'var(--bg-accent)', display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 22 }}><i className="ti ti-check" style={{ fontSize: 38, color: 'var(--text-accent)' }}></i></div>
            <span style={{ font: 'var(--text-title)', display: 'block', marginBottom: 8 }}>{mission ? '인증 사진 올렸어요!' : '등록 완료!'}</span>
            <p style={{ font: 'var(--text-body)', color: 'var(--text-secondary)', margin: '0 0 22px', lineHeight: 1.6 }}>{mission ? <React.Fragment>담당자 심사 후 응모권을 드려요<br />보통 1~2일 걸려요</React.Fragment> : manual ? <React.Fragment>새 가게로 등록했어요<br />확인 후 {reg.city} 지도에 반영돼요</React.Fragment> : placeName ? <React.Fragment>이웃들이 이 사진으로<br />맛집을 찾아갈 수 있어요</React.Fragment> : <React.Fragment>이웃들이 장소를 알려주면<br />위치가 자동으로 채워져요</React.Fragment>}</p>
            <div style={{ width: '100%', maxWidth: '100%', alignSelf: 'stretch', boxSizing: 'border-box', overflow: 'hidden', background: 'var(--surface-1)', borderRadius: 'var(--radius-card)', padding: 16, boxShadow: 'var(--shadow-card)', display: 'flex', gap: 14, alignItems: 'center', textAlign: 'left' }}>
              <div style={{ width: 66, height: 66, borderRadius: 16, overflow: 'hidden', flexShrink: 0, background: 'var(--surface-2)' }}><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></div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 4 }}>{mission ? '집에서 · 역삼동' : placeName || '장소 미지정'}</span>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-muted)', display: 'block', marginBottom: 6 }}>사진 {photoCount}장{videoCount > 0 ? ' · 동영상 ' + videoCount + '개' : ''}</span>
                {mission ? <Badge tone="neutral"><i className="ti ti-lock" style={{ fontSize: 12 }}></i>정확한 위치는 저장하지 않아요</Badge> : manual ? <Badge tone="warning"><i className="ti ti-writing" style={{ fontSize: 12 }}></i>직접 등록 · 확인 전 미확인 표시</Badge> : (placeName && !reg.domestic) ? <Badge tone="success"><i className="ti ti-world" style={{ fontSize: 12 }}></i>{reg.city} · {reg.country}</Badge> : placeName ? <Badge tone="success"><i className="ti ti-map-pin-check" style={{ fontSize: 12 }}></i>위치 확인됨</Badge> : <Badge tone="warning"><i className="ti ti-map-pin-question" style={{ fontSize: 12 }}></i>위치 확인 중</Badge>}
              </div>
            </div>
            {cols.length > 0 && (
              <div style={{ alignSelf: 'stretch', display: 'flex', alignItems: 'center', gap: 8, background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', padding: '12px 14px', marginTop: 10, textAlign: 'left' }}>
                <i className="ti ti-bookmarks" style={{ fontSize: 15, color: 'var(--text-accent)', flexShrink: 0 }}></i>
                <span style={{ font: 'var(--text-small)', color: 'var(--text-primary)', minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{cols.join(', ')}에 담겼어요</span>
              </div>
            )}
            {mission && (
              <div style={{ alignSelf: 'stretch', display: 'flex', alignItems: 'center', gap: 10, background: 'var(--bg-warning)', border: '1px solid var(--border)', borderRadius: 'var(--radius-tile)', padding: '12px 14px', marginTop: 10, textAlign: 'left' }}>
                <i className="ti ti-clock" style={{ fontSize: 15, color: 'var(--text-warning)', flexShrink: 0 }}></i>
                <span style={{ flex: 1, minWidth: 0, font: 'var(--text-small)', color: 'var(--text-primary)' }}>{msnAfter >= 3 ? '밀키트 미션 인증 3/3 · 담당자 심사가 시작됐어요' : '밀키트 미션 인증 ' + msnAfter + '/3 · 사진 ' + (3 - msnAfter) + '장만 더 올리면 심사가 시작돼요'}</span>
              </div>
            )}
          </div>
        )}
        {step === 2 && (
          <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 10 }}>
            <span style={{ font: 'var(--text-body-strong)' }}>공개 범위</span>
            <VisibilityRow value={vis} account={acctVis} onClick={() => setVisOpen(true)} />
            <VisibilityNotice account={acctVis} item={vis} onOpenSettings={() => nav('settings')} />
          </div>
        )}
        <div style={{ marginTop: 24, marginBottom: 86 }}><div>
        {step < 3 ? <Button variant="primary" disabled={step === 2 && (gps ? !chosen : !mission && !ngChosen)} onClick={() => { if (step === 2 && (gps ? !chosen : !mission && !ngChosen)) return; setStep(mission ? 3 : step + 1); }}>{mission ? '미션 인증 사진 올리기' : step === 1 ? '다음' : gps ? (chosen ? '이 음식점으로 등록' : '음식점을 선택해주세요') : (ngChosen ? '「' + ngChosen + '」' + josa(ngChosen) + ' 등록' : '음식점을 선택해주세요')}<i className="ti ti-arrow-right" style={{ fontSize: 18 }}></i></Button>
          : deleted ? <Button variant="primary" onClick={() => nav('feed')}>피드로 돌아가기</Button>
          : mission ? <Button variant="primary" onClick={() => nav('missionDetail')}>미션 현황 보기</Button>
            : <Button variant="primary" onClick={() => nav('feed')}>피드로 돌아가기</Button>}
        </div></div>
      </div>
      {leaveTo && <DiscardUploadConfirm photos={photoCount + videoCount} tagCount={tagCount} hasCaption={caption.trim().length > 0} cols={cols.length} step={step} onStay={() => setLeaveTo(null)} onLeave={() => { const d = leaveTo; setLeaveTo(null); window.__uploadGuard = null; nav(d); }} />}
      {visOpen && <VisibilitySheet current={vis} account={acctVis} onClose={() => setVisOpen(false)} onPick={(v) => { setVis(v); setVisOpen(false); }} />}
      {regOpen && <RegionPickerSheet current={region} onClose={() => setRegOpen(false)} onPick={(k) => { setRegion(k); setQ(''); setGq(''); setNgPick(null); setGpsPick(null); setManual(null); setPicked(k === gpsRegion ? 0 : null); setRegOpen(false); }} />}
      {manOpen && <ManualPlaceSheet region={region} initialName={q} onClose={() => setManOpen(false)} onChangeRegion={() => { setManOpen(false); setRegOpen(true); }} onDone={(p) => { setManual(p); setNgPick(null); setManOpen(false); }} />}
      {pinOpen && <PinPickerSheet onClose={() => setPinOpen(false)} onDone={(name) => { setPinned(name); setPinOpen(false); setStep(3); }} />}
      {delOpen && <DeletePostConfirm post={{ menu: (tags[0] || [])[0] || (mission ? '미션 인증샷' : null), place: mission ? null : placeName, mediaCount: photoCount + videoCount, likes: 0, comments: 0, mission }} onCancel={() => setDelOpen(false)} onConfirm={() => { setDelOpen(false); setDeleted(true); }} />}
      {colOpen && (
        <BottomSheet title="컬렉션에 담기" onClose={() => setColOpen(false)} footer={<Button variant="primary" onClick={() => setColOpen(false)}>{cols.length ? cols.length + '개 컬렉션에 담기' : '담지 않고 닫기'}</Button>}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {myCollections.map(c => {
              const on = cols.includes(c.name);
              return (
                <div key={c.name} onClick={() => toggleCol(c.name)} style={{ display: 'flex', alignItems: 'center', gap: 12, background: on ? 'var(--bg-accent)' : 'var(--surface-2)', border: '1.5px solid ' + (on ? 'var(--text-accent)' : 'transparent'), borderRadius: 'var(--radius-tile)', padding: 14, cursor: 'pointer' }}>
                  <div style={{ width: 38, height: 38, borderRadius: 12, background: 'var(--surface-1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><i className={'ti ti-' + c.icon} style={{ fontSize: 17, color: 'var(--text-accent)' }}></i></div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.name}</span>
                    <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>게시물 {c.count}개</span>
                  </div>
                  <span style={{ width: 22, height: 22, borderRadius: '50%', flexShrink: 0, background: on ? 'var(--text-accent)' : 'var(--surface-1)', border: on ? 'none' : '1.5px solid var(--border-strong)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{on && <i className="ti ti-check" style={{ fontSize: 13, color: '#fff' }}></i>}</span>
                </div>
              );
            })}
            <div onClick={() => setColOpen(false)} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, border: '1.5px dashed var(--border-strong)', borderRadius: 'var(--radius-tile)', padding: '16px 0', cursor: 'pointer' }}>
              <i className="ti ti-plus" style={{ fontSize: 16, color: 'var(--text-secondary)' }}></i>
              <span style={{ font: '600 14px/1 var(--font-family)', color: 'var(--text-secondary)' }}>새 컬렉션 만들기</span>
            </div>
          </div>
        </BottomSheet>
      )}
      {addOpen && (
        <BottomSheet title="무엇을 추가할까요?" onClose={() => setAddOpen(false)}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {[
              { icon: 'photo', label: '사진 선택', sub: photoCount + '/5 · 갤러리에서 고르기', full: photoCount >= 5 },
              { icon: 'camera', label: '사진 촬영', sub: '카메라로 바로 찍기', full: photoCount >= 5 },
              { icon: 'video', label: '동영상 선택', sub: videoCount + '/1 · 30초 이내', full: videoCount >= 1 }
            ].map(o => (
              <div key={o.label} onClick={() => { if (!o.full) setAddOpen(false); }} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '16px 18px', background: 'var(--surface-2)', borderRadius: 'var(--radius-tile)', cursor: o.full ? 'default' : 'pointer', opacity: o.full ? .42 : 1 }}>
                <div style={{ width: 40, height: 40, borderRadius: 12, background: 'var(--surface-1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><i className={'ti ti-' + o.icon} style={{ fontSize: 19, color: 'var(--text-primary)' }}></i></div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ font: 'var(--text-body-strong)', display: 'block', marginBottom: 2 }}>{o.label}</span>
                  <span style={{ font: 'var(--text-small)', color: 'var(--text-secondary)' }}>{o.sub}</span>
                </div>
                {o.full ? <span style={{ font: 'var(--text-micro)', color: 'var(--text-muted)', flexShrink: 0 }}>가득 참</span> : <i className="ti ti-chevron-right" style={{ fontSize: 17, color: 'var(--text-muted)', flexShrink: 0 }}></i>}
              </div>
            ))}
          </div>
        </BottomSheet>
      )}
    </div>
  );
}
window.Upload = Upload;
