// ─── Color-소개팅 · User-side screens ─────────────────────────────────
// HomeScreen, ApplyScreen, ApplyDoneScreen
// 원본 prototype 디자인을 100% 유지하고, ApplyScreen submit + 사진 업로드만 실제 API와 연결

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

const APPLY_MESSAGE_MIN_LEN = 10;

/** 홈·접수 하단 문의 (폼 내 팔로우 안내와 동일 계정) */
const OFFICIAL_INSTA_URL = 'https://www.instagram.com/color_sogaeting/';
const OFFICIAL_INSTA_HANDLE = '@colorsogae';
const OFFICIAL_OPEN_KAKAO_URL = 'https://open.kakao.com/o/sEtOx7vi';

function UserFooterOfficialLinks() {
  return (
    <div style={{
      marginTop: 18,
      padding: '16px 14px',
      background: 'var(--bg-soft)',
      borderRadius: 14,
      border: '1px solid var(--line)',
    }}>
      <div style={{
        fontSize: 11, fontWeight: 700, color: 'var(--ink-500)', letterSpacing: '0.03em', marginBottom: 12,
      }}>
        공식 인스타그램 · 카카오톡 오픈채팅
      </div>
      <a
        href={OFFICIAL_INSTA_URL}
        target="_blank"
        rel="noopener noreferrer"
        style={{ display: 'block', fontSize: 14, fontWeight: 600, color: 'var(--ink-900)', marginBottom: 10 }}
      >
        인스타 {OFFICIAL_INSTA_HANDLE} <span style={{ fontSize: 12, opacity: 0.7 }}>↗</span>
      </a>
      <a
        href={OFFICIAL_OPEN_KAKAO_URL}
        target="_blank"
        rel="noopener noreferrer"
        style={{ display: 'block', fontSize: 14, fontWeight: 600, color: 'var(--ink-900)', marginBottom: 8 }}
      >
        오픈채팅으로 문의하기 <span style={{ fontSize: 12, opacity: 0.7 }}>↗</span>
      </a>
      <div style={{
        fontSize: 11, color: 'var(--ink-400)', fontFamily: 'var(--f-mono)', lineHeight: 1.5, wordBreak: 'break-all',
      }}>
        {OFFICIAL_OPEN_KAKAO_URL}
      </div>
    </div>
  );
}

function MiniBrand({ right }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '12px 22px 8px',
    }}>
      <div className="brand-row">
        <span className="word">Color</span>
        <span style={{ color: 'var(--ink-500)', fontWeight: 500, fontSize: 13 }}>소개팅</span>
      </div>
      {right}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 홈 화면
// ─────────────────────────────────────────────────────────────
const LANDING_FALLBACK = {
  cumulativeApplications: 1284,
  successfulMatches: 342,
  averageResponseLabel: '4.7일',
};

function HomeScreen({ go }) {
  const [landing, setLanding] = useState(LANDING_FALLBACK);

  useEffect(() => {
    let cancelled = false;
    const load = () => {
      window.CSApi.getLandingDisplay()
        .then((r) => {
          if (cancelled || !r) return;
          setLanding({
            cumulativeApplications: Number(r.cumulativeApplications) || LANDING_FALLBACK.cumulativeApplications,
            successfulMatches: Number(r.successfulMatches) || LANDING_FALLBACK.successfulMatches,
            averageResponseLabel: String(r.averageResponseLabel || '').trim() || LANDING_FALLBACK.averageResponseLabel,
          });
        })
        .catch(() => {});
    };
    load();
    const onVis = () => {
      if (document.visibilityState === 'visible') load();
    };
    document.addEventListener('visibilitychange', onVis);
    const t = setInterval(load, 60_000);
    return () => {
      cancelled = true;
      document.removeEventListener('visibilitychange', onVis);
      clearInterval(t);
    };
  }, []);

  return (
    <div className="app">
      <div className="statusbar-spacer" />
      <MiniBrand />

      <div style={{ padding: '24px 22px 32px' }}>
        <div style={{
          fontFamily: 'var(--f-mono)', fontSize: 11, letterSpacing: '0.1em',
          color: 'var(--ink-400)', textTransform: 'uppercase', marginBottom: 18,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <span style={{ width: 6, height: 6, borderRadius: 999, background: '#ec4438' }} />
          무료 운영중 · v1.0.0
        </div>

        <h1 style={{
          margin: 0, fontSize: 44, lineHeight: 1.06, fontWeight: 700,
          letterSpacing: '-0.035em', color: 'var(--ink-900)',
        }}>
          돈 받고 하는<br />
          <span style={{ fontFamily: 'var(--f-serif)', fontStyle: 'italic', fontWeight: 400 }}>소개팅이</span>{' '}
          <br />
          <span style={{
            backgroundImage: 'linear-gradient(91deg, #161412 0%, #e23a2e 30%, #f5c419 55%, #2f6cf6 80%, #161412 100%)',
            WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent',
          }}>너무 별로라서.</span>
        </h1>

        <p style={{
          marginTop: 22, marginBottom: 0, fontSize: 15.5, lineHeight: 1.65,
          color: 'var(--ink-500)', maxWidth: 320,
        }}>
          남고 → 공대 → 군대 → IT회사 다닌<br />
          개발자가 직접 만든 무료 소개팅.<br />
          AI로 매칭을 최대한 빠르고 순차적으로 돌립니다.
        </p>
      </div>

      <div style={{
        margin: '0 22px 28px', padding: '22px 20px', borderRadius: 22,
        background: '#161412', color: '#fff', position: 'relative', overflow: 'hidden',
      }}>
        <div style={{
          fontFamily: 'var(--f-mono)', fontSize: 10, letterSpacing: '0.16em',
          textTransform: 'uppercase', opacity: 0.5, marginBottom: 14,
        }}>What's inside the name</div>
        <div style={{ fontSize: 22, lineHeight: 1.35, fontWeight: 600, letterSpacing: '-0.02em', marginBottom: 18 }}>
          매칭은 6개의 색으로,<br />단정하게 분류돼요.
        </div>
        <div style={{ display: 'flex', gap: 6, marginBottom: 14 }}>
          {[
            { c: 'linear-gradient(135deg, #9adfff, #c8b8ff, #ffc6f0)', label: 'Diamond' },
            { c: '#161412', label: 'Black' },
            { c: '#e23a2e', label: 'Red' },
            { c: '#2f6cf6', label: 'Blue' },
            { c: '#f5c419', label: 'Yellow' },
            { c: '#fff', label: 'White' },
          ].map((t, i) => (
            <div key={i} style={{
              flex: 1, height: 38, borderRadius: 8, background: t.c,
              border: t.label === 'White' || t.label === 'Black' ? '1px solid rgba(255,255,255,0.18)' : '0',
              boxShadow: t.label === 'Diamond' ? 'inset 0 0 8px rgba(255,255,255,0.5)' : 'none',
            }} />
          ))}
        </div>
        <div style={{
          display: 'flex', justifyContent: 'space-between',
          fontFamily: 'var(--f-mono)', fontSize: 9, letterSpacing: '0.08em',
          color: 'rgba(255,255,255,0.55)', textTransform: 'uppercase',
        }}>
          <span>Diamond</span><span>Black</span><span>Red</span><span>Blue</span><span>Yellow</span><span>White</span>
        </div>
      </div>

      <div style={{ padding: '0 22px', display: 'grid', gap: 8 }}>
        {[
          { k: '01', t: '무료', d: '돈 한 푼 안 받음. 진짜로.' },
          { k: '02', t: 'AI 기반 최적화 매칭', d: '수동 + AI알고리즘으로 최적화 매칭' },
          { k: '03', t: '솔직함', d: '프로필 그대로 매칭, 보정 안 함.' },
        ].map(it => (
          <div key={it.k} style={{
            display: 'flex', alignItems: 'center', gap: 14,
            background: '#fff', border: '1px solid var(--line)',
            borderRadius: 16, padding: '14px 16px',
          }}>
            <div style={{
              fontFamily: 'var(--f-mono)', fontSize: 11, color: 'var(--ink-400)',
              letterSpacing: '0.08em',
            }}>{it.k}</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.015em' }}>{it.t}</div>
              <div style={{ fontSize: 12.5, color: 'var(--ink-500)', marginTop: 2 }}>{it.d}</div>
            </div>
          </div>
        ))}
      </div>

      <div style={{
        margin: '24px 22px 0', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr',
        background: 'var(--bg-soft)', borderRadius: 18, padding: '16px 4px',
      }}>
        {[
          { n: landing.cumulativeApplications.toLocaleString(), l: '누적 신청' },
          { n: landing.successfulMatches.toLocaleString(), l: '성사 매칭' },
          { n: landing.averageResponseLabel, l: '평균 응답' },
        ].map(s => (
          <div key={s.l} style={{ textAlign: 'center' }}>
            <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>{s.n}</div>
            <div className="tiny" style={{ marginTop: 2 }}>{s.l}</div>
          </div>
        ))}
      </div>

      <div style={{
        padding: '34px 22px max(40px, calc(24px + env(safe-area-inset-bottom, 0px)))',
      }}>
        <button className="btn primary full" onClick={() => go('apply')}>
          <span>소개팅 접수하기</span>
          <span style={{
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            width: 28, height: 28, borderRadius: 999, background: '#fff', color: '#161412',
            fontSize: 16, lineHeight: 1, paddingBottom: 2,
          }}>↗</span>
        </button>
        <div className="tiny" style={{ textAlign: 'center', marginTop: 16, lineHeight: 1.55, color: 'var(--ink-500)' }}>
          작성 시간 약 4-6분 · 5개 사진 첨부 권장
        </div>
        <UserFooterOfficialLinks />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// 접수 폼
// ─────────────────────────────────────────────────────────────
const SALARIES = ['2***', '3***', '4***', '5***', '6***', '7***', '8***', '9***', '1억 이상'];
const MBTIS = ['ISTJ','ISFJ','INFJ','INTJ','ISTP','ISFP','INFP','INTP','ESTP','ESFP','ENFP','ENTP','ESTJ','ESFJ','ENFJ','ENTJ'];

const APPLY_MIN_BIRTH_YEAR = 1940;

/** 휴대폰 11자리 숫자만, 01로 시작 (예: 01012341234) */
const CONTACT_MOBILE_OK = /^01\d{9}$/;

/**
 * 업로드 전 사진을 브라우저에서 리사이즈·재인코딩.
 * - EXIF 회전은 <img>가 자동 처리(최신 브라우저)
 * - 긴 변 maxEdge 이하 + JPEG quality 로 압축
 * → 백엔드는 메타데이터만 받고, S3에는 이 작은 Blob이 직접 PUT 된다.
 */
async function resizeImageToJpeg(file, maxEdge = 1600, quality = 0.82) {
  const url = URL.createObjectURL(file);
  try {
    const img = await new Promise((resolve, reject) => {
      const i = new Image();
      i.onload = () => resolve(i);
      i.onerror = () => reject(new Error('이미지를 읽을 수 없습니다.'));
      i.src = url;
    });
    let w = img.naturalWidth || img.width;
    let h = img.naturalHeight || img.height;
    if (!w || !h) throw new Error('이미지 크기를 가져올 수 없습니다.');
    if (w > maxEdge || h > maxEdge) {
      const ratio = Math.min(maxEdge / w, maxEdge / h);
      w = Math.round(w * ratio);
      h = Math.round(h * ratio);
    }
    const canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    const ctx = canvas.getContext('2d');
    if (!ctx) throw new Error('Canvas 컨텍스트를 만들 수 없습니다.');
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, w, h);
    ctx.drawImage(img, 0, 0, w, h);
    const blob = await new Promise((resolve) =>
      canvas.toBlob((b) => resolve(b), 'image/jpeg', quality),
    );
    if (!blob) throw new Error('이미지 변환 결과가 비어있습니다.');
    return blob;
  } finally {
    URL.revokeObjectURL(url);
  }
}

/** 접수 폼 검증: 배열 순서 = 스크롤 우선순위(위에서 아래) */
function collectApplyIssues(form) {
  const issues = [];
  const add = (key, bad) => {
    if (bad) issues.push(key);
  };
  const cy = new Date().getFullYear();
  const maxBirth = cy - 15;
  const by = parseInt(String(form.birthYear ?? '').trim(), 10);
  const h = parseInt(String(form.height ?? '').trim(), 10);
  const w = parseInt(String(form.weight ?? '').trim(), 10);
  const hasPhotoFile = Array.isArray(form.photos) && form.photos.some(p => p && p.file);

  add('name', !String(form.name || '').trim());
  add('birthYear', Number.isNaN(by) || by < APPLY_MIN_BIRTH_YEAR || by > maxBirth);
  add('gender', !form.gender);
  add('role', !String(form.role || '').trim());
  add('company', !String(form.company || '').trim());
  add('height', Number.isNaN(h) || h < 120 || h > 230);
  add('weight', Number.isNaN(w) || w < 30 || w > 200);
  add('maritalStatus', !form.maritalStatus);
  add('contact', !CONTACT_MOBILE_OK.test(String(form.contact || '').trim()));
  add('kakao', !String(form.kakao || '').trim());
  add('insta', !String(form.insta || '').trim());
  add('region', !String(form.region || '').trim());
  add('workplace', !String(form.workplace || '').trim());
  add('smoke', !form.smoke);
  add('school', !String(form.school || '').trim());
  add('salary', !form.salary || !SALARIES.includes(form.salary));
  add('car', !form.car);
  add('marry', !form.marry);
  add('mbti', !form.mbti || !MBTIS.includes(form.mbti));
  add(
    'message',
    !String(form.message || '').trim() || String(form.message).trim().length < APPLY_MESSAGE_MIN_LEN,
  );
  add('photos', !hasPhotoFile);
  add('agree1', !form.agree1);
  add('agree2', !form.agree2);
  return issues;
}

function ApplyScreen({ go, onApplied }) {
  const [form, setForm] = useState({
    name: '', birthYear: '', gender: '', role: '', company: '', height: '', weight: '',
    maritalStatus: '',
    contact: '', kakao: '', insta: '',
    regionSido: '', regionSigungu: '', region: '',
    workplaceSido: '', workplaceSigungu: '', workplace: '',
    smoke: '', school: '', salary: '', car: '', marry: '', mbti: '',
    prefMbti: [], prefTier: [],
    prefRegion: '', prefRegions: [],
    prefAgeMin: 26, prefAgeMax: 32,
    prefAgeOpen: false, prefRegionOpen: false,
    intro: '', ideal: '', message: '',
    photos: [], agree1: false, agree2: false,
  });
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);
  const [invalidAnchors, setInvalidAnchors] = useState([]);
  const [attemptedSubmit, setAttemptedSubmit] = useState(false);

  const set = (k, v) => {
    setForm(f => ({ ...f, [k]: v }));
  };
  const toggle = (k, v) => {
    setForm(f => ({ ...f, [k]: f[k].includes(v) ? f[k].filter(x => x !== v) : [...f[k], v] }));
  };

  const appendPhoto = useCallback((photo) => {
    setForm(f => ({ ...f, photos: [...f.photos, photo] }));
  }, []);
  const removePhoto = useCallback((i) => {
    setForm(f => ({ ...f, photos: f.photos.filter((_, idx) => idx !== i) }));
  }, []);

  const err = (anchor) => invalidAnchors.includes(anchor);

  useEffect(() => {
    if (!attemptedSubmit) return;
    setInvalidAnchors(collectApplyIssues(form));
  }, [form, attemptedSubmit]);

  const trySubmit = () => {
    setAttemptedSubmit(true);
    const issues = collectApplyIssues(form);
    setInvalidAnchors(issues);
    if (issues.length) {
      requestAnimationFrame(() => {
        const el = document.querySelector(`[data-apply-anchor="${issues[0]}"]`);
        el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
      });
      return;
    }
    postApply();
  };

  const postApply = async () => {
    if (submitting) return;
    setSubmitting(true);
    setError(null);
    try {
      // 1) 사진을 클라이언트에서 리사이즈·JPEG 재인코딩
      //    (서버에는 작은 JSON만 보내기 위해 — WAF SizeRestrictions 우회)
      const resized = [];
      for (const p of form.photos) {
        if (!p.file) continue;
        const blob = await resizeImageToJpeg(p.file, 1600, 0.82);
        if (!blob || blob.size <= 0) {
          throw new Error('사진을 처리할 수 없습니다. 다시 선택해 주세요.');
        }
        resized.push({ blob, originalName: p.file.name || 'photo.jpg' });
      }
      if (resized.length === 0) {
        throw new Error('얼굴이 잘 나온 사진 1장 이상이 필요합니다.');
      }

      // 2) presigned PUT URL 발급
      const filesMeta = resized.map(r => ({ size: r.blob.size, mimeType: 'image/jpeg' }));
      const urlsRes = await window.CSApi.requestPhotoUploadUrls(filesMeta);
      const uploads = urlsRes.uploads || [];
      if (uploads.length !== resized.length) {
        throw new Error('업로드 URL 발급에 실패했습니다.');
      }

      // 3) 각 사진을 S3에 직접 PUT (별도 도메인 → ALB WAF 본문 제한 우회)
      await Promise.all(
        resized.map((r, i) => window.CSApi.uploadToS3(uploads[i].uploadUrl, r.blob)),
      );

      // 4) 작은 JSON 본문으로 회원 접수 호출 — 사진은 메타만 포함
      const payload = {
        name: form.name,
        birthYear: parseInt(form.birthYear, 10),
        gender: form.gender,
        role: form.role,
        company: form.company,
        height: parseInt(form.height, 10),
        weight: parseInt(form.weight, 10),
        maritalStatus: form.maritalStatus,
        contact: form.contact,
        kakao: form.kakao,
        insta: form.insta,
        region: form.region,
        workplace: form.workplace,
        smoke: form.smoke,
        school: form.school,
        salary: form.salary,
        car: form.car,
        marry: form.marry,
        mbti: form.mbti,
        prefMbti: form.prefMbti || [],
        prefTier: form.prefTier || [],
        prefRegion: form.prefRegion || '',
        prefRegions: form.prefRegionOpen ? [] : (form.prefRegions || []),
        prefAge: [form.prefAgeMin, form.prefAgeMax],
        prefAgeOpen: !!form.prefAgeOpen,
        prefRegionOpen: !!form.prefRegionOpen,
        intro: form.intro || '',
        ideal: form.ideal || '',
        message: form.message,
        agreePrivacy: !!form.agree1,
        agreeShare: !!form.agree2,
        photos: resized.map((r, i) => ({
          accessKey: uploads[i].accessKey,
          originalName: r.originalName,
          size: r.blob.size,
          mimeType: 'image/jpeg',
        })),
      };

      const res = await window.CSApi.apply(payload);
      if (onApplied) onApplied(res);
      setInvalidAnchors([]);
      setAttemptedSubmit(false);
      go('apply-done');
    } catch (err) {
      setError(err.message || '접수 중 오류가 발생했습니다.');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="app">
      <div className="statusbar-spacer" />

      <div style={{
        padding: '8px 22px 14px', background: 'var(--bg-app)',
        position: 'sticky', top: 0, zIndex: 5,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <button onClick={() => go('home')} style={{
            width: 36, height: 36, borderRadius: 999, background: '#fff',
            border: '1px solid var(--line)', display: 'flex',
            alignItems: 'center', justifyContent: 'center', fontSize: 18,
          }}>←</button>
          <div style={{ flex: 1 }}>
            <div className="tiny">소개팅 접수</div>
            <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.02em', marginTop: 2 }}>
              나에 대해 알려주세요
            </div>
          </div>
        </div>
      </div>

      <div style={{ padding: '8px 22px max(80px, calc(52px + env(safe-area-inset-bottom, 0px)))' }}>

        <SectionCard num="01" title="기본 정보">
          <Field label="이름" req anchor="name" showError={err('name')}>
            <input className="input" placeholder="실명으로 적어주세요" value={form.name} onChange={e => set('name', e.target.value)} />
          </Field>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <Field label="출생 연도" req hint="4자리 숫자, 예: 1997" anchor="birthYear" showError={err('birthYear')}>
              <input className="input" inputMode="numeric" placeholder="예: 1997" value={form.birthYear} onChange={e => set('birthYear', e.target.value)} />
            </Field>
            <Field label="성별" req anchor="gender" showError={err('gender')}>
              <div className="opts">
                {['남자', '여자'].map(g => (
                  <button key={g} className={form.gender === g ? 'on' : ''} onClick={() => set('gender', g)} style={{ flex: 1 }}>{g}</button>
                ))}
              </div>
            </Field>
          </div>

          <Field label="직무" req anchor="role" showError={err('role')}>
            <input className="input" placeholder="예: 백엔드 개발자 / UX 디자이너 / 회계" value={form.role} onChange={e => set('role', e.target.value)} />
          </Field>

          <Field label="회사" req hint="실명·분류 모두 OK" anchor="company" showError={err('company')}>
            <input className="input" placeholder="예: 삼성전자, 외국계, 공기업, 중소(50인) 등" value={form.company} onChange={e => set('company', e.target.value)} />
            <div className="tiny" style={{ marginTop: 6, textTransform: 'none', letterSpacing: 0, fontFamily: 'var(--f-sans)' }}>
              회사명을 적기 부담스럽다면 <b>외국계 / 공기업 / 대기업 / 중견 / 중소 / 스타트업 / 프리랜서</b> 등 분류로도 OK
            </div>
          </Field>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            <Field label="키" req anchor="height" showError={err('height')}>
              <div style={{ position: 'relative' }}>
                <input className="input" placeholder="175" value={form.height} onChange={e => set('height', e.target.value)} />
                <span style={{ position: 'absolute', right: 16, top: 14, color: 'var(--ink-400)', fontSize: 14, fontFamily: 'var(--f-mono)' }}>cm</span>
              </div>
            </Field>
            <Field label="몸무게" req anchor="weight" showError={err('weight')}>
              <div style={{ position: 'relative' }}>
                <input className="input" placeholder="68" value={form.weight} onChange={e => set('weight', e.target.value)} />
                <span style={{ position: 'absolute', right: 16, top: 14, color: 'var(--ink-400)', fontSize: 14, fontFamily: 'var(--f-mono)' }}>kg</span>
              </div>
            </Field>
          </div>

          <Field label="돌싱 유무" req anchor="maritalStatus" showError={err('maritalStatus')}>
            <div className="opts">
              {[
                { v: '미혼', l: '미혼 (결혼 이력 없음)' },
                { v: '돌싱', l: '돌싱' },
              ].map(o => (
                <button key={o.v} className={form.maritalStatus === o.v ? 'on' : ''} onClick={() => set('maritalStatus', o.v)} style={{ flex: 1 }}>{o.l}</button>
              ))}
            </div>
          </Field>

          <Field label="연락처" req hint="하이픈 없이 11자리" anchor="contact" showError={err('contact')}>
            <input
              className="input"
              type="tel"
              inputMode="numeric"
              autoComplete="tel-national"
              placeholder="01012341234"
              maxLength={11}
              value={form.contact}
              onChange={e => set('contact', String(e.target.value).replace(/\D/g, '').slice(0, 11))}
            />
          </Field>

          <Field label="카카오톡 ID" req hint="검색 허용 필수" anchor="kakao" showError={err('kakao')}>
            <input className="input" placeholder="kakaotalk_id" value={form.kakao} onChange={e => set('kakao', e.target.value)} />
            <div style={{
              marginTop: 8, padding: '10px 14px', background: '#fff7ec', borderRadius: 12,
              fontSize: 12, color: '#8c6a05', lineHeight: 1.5, border: '1px solid #f3e3b8',
            }}>
              💡 카톡 설정 → 친구 → ID로 친구 추가 허용 <b>꼭 켜주세요.</b><br />
              꺼져있으면 매칭 진행이 불가능해요.
            </div>
          </Field>

          <Field label="인스타그램 ID" req hint="아이디" anchor="insta" showError={err('insta')}>
            <input className="input" placeholder="your_instagram" value={form.insta} onChange={e => set('insta', e.target.value)} />
            <div style={{
              marginTop: 10, padding: '14px 16px', borderRadius: 14,
              background: 'linear-gradient(135deg, #fee5e2, #ffd7d3)',
              border: '1.5px solid #ec4438',
              position: 'relative', overflow: 'hidden',
            }}>
              <div style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                background: '#ec4438', color: '#fff',
                fontFamily: 'var(--f-mono)', fontSize: 10, fontWeight: 700,
                padding: '3px 8px', borderRadius: 999,
                letterSpacing: '0.08em', marginBottom: 8,
              }}>
                <span style={{ width: 6, height: 6, background: '#fff', borderRadius: 999 }} />
                필수 · MUST FOLLOW
              </div>
              <div style={{ fontSize: 13.5, color: '#7a1f17', lineHeight: 1.55, fontWeight: 500 }}>
                <b>@colorsogae</b> 인스타 계정을 <b>반드시 팔로우</b>해주세요.<br />
                <span style={{ fontWeight: 600, color: '#a8281d' }}>팔로우하지 않은 채로 접수 시 자동 폐기 처리</span>되며, 본인 동의 없이 즉시 삭제됩니다.
              </div>
              <a
                href="https://www.instagram.com/color_sogaeting/"
                target="_blank"
                rel="noopener noreferrer"
                style={{
                  marginTop: 10, display: 'inline-flex', alignItems: 'center', gap: 6,
                  background: '#161412', color: '#fff',
                  padding: '8px 12px', borderRadius: 999,
                  fontSize: 12, fontWeight: 600, textDecoration: 'none',
                }}>
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
                  <rect x="3" y="3" width="18" height="18" rx="5" stroke="#fff" strokeWidth="2"/>
                  <circle cx="12" cy="12" r="4" stroke="#fff" strokeWidth="2"/>
                  <circle cx="17.5" cy="6.5" r="1" fill="#fff"/>
                </svg>
                @colorsogae 팔로우하러 가기 ↗
              </a>
            </div>
          </Field>

          <Field label="거주지" req hint="시·도 → 시·군·구" anchor="region" showError={err('region')}>
            <RegionPicker
              sido={form.regionSido}
              sigungu={form.regionSigungu}
              onChange={({ sido, sigungu }) => {
                setForm(f => ({
                  ...f,
                  regionSido: sido,
                  regionSigungu: sigungu,
                  region: sido && sigungu ? `${sido} ${sigungu}` : sido || '',
                }));
              }}
            />
          </Field>
          <Field label="근무지" req hint="직장·근무 지역" anchor="workplace" showError={err('workplace')}>
            <RegionPicker
              sido={form.workplaceSido}
              sigungu={form.workplaceSigungu}
              onChange={({ sido, sigungu }) => {
                setForm(f => ({
                  ...f,
                  workplaceSido: sido,
                  workplaceSigungu: sigungu,
                  workplace: sido && sigungu ? `${sido} ${sigungu}` : sido || '',
                }));
              }}
            />
          </Field>
        </SectionCard>

        <SectionCard num="02" title="라이프 스타일">
          <Field label="흡연 여부" req anchor="smoke" showError={err('smoke')}>
            <div className="opts">
              {['비흡연자', '전자담배', '연초'].map(s => (
                <button key={s} className={form.smoke === s ? 'on' : ''} onClick={() => set('smoke', s)}>{s}</button>
              ))}
            </div>
          </Field>

          <Field label="최종 학력" req hint="목록에서 선택" anchor="school" showError={err('school')}>
            <select
              className="input"
              value={form.school}
              onChange={e => set('school', e.target.value)}
              style={{ appearance: 'none', WebkitAppearance: 'none', backgroundImage: 'none' }}
            >
              <option value="">선택해주세요</option>
              {(window.EDUCATION_OPTIONS || []).map(o => (
                <option key={o} value={o}>{o}</option>
              ))}
            </select>
          </Field>

          <Field label="연봉" req hint="세전 1개만 선택" anchor="salary" showError={err('salary')}>
            <div className="salary-grid opts">
              {SALARIES.map(s => (
                <button key={s} className={form.salary === s ? 'on' : ''} onClick={() => set('salary', s)}>{s}</button>
              ))}
            </div>
          </Field>

          <Field label="자차 여부" req anchor="car" showError={err('car')}>
            <div className="opts">
              {['있음', '없음'].map(c => (
                <button key={c} className={form.car === c ? 'on' : ''} onClick={() => set('car', c)} style={{ flex: 1 }}>{c}</button>
              ))}
            </div>
          </Field>

          <Field label="추후 결혼 희망 여부" req anchor="marry" showError={err('marry')}>
            <div className="opts">
              {['O', 'X', '상대에 따라', '모르겠음'].map(m => (
                <button key={m} className={form.marry === m ? 'on' : ''} onClick={() => set('marry', m)}>{m}</button>
              ))}
            </div>
          </Field>

          <Field label="MBTI" req anchor="mbti" showError={err('mbti')}>
            <div style={{
              display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 6,
            }} className="opts">
              {MBTIS.map(m => (
                <button key={m} className={form.mbti === m ? 'on' : ''} onClick={() => set('mbti', m)} style={{
                  fontFamily: 'var(--f-mono)', fontSize: 12, padding: '10px 4px',
                }}>{m}</button>
              ))}
            </div>
          </Field>
        </SectionCard>

        <SectionCard num="03" title="이상형 · 선호 조건">
          <div className="apply-pref-grid">
            <Field label="선호 MBTI" hint="여러 개 또는 무관">
              <div style={{
                display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 6,
              }} className="opts">
                {MBTIS.map(m => (
                  <button
                    type="button"
                    key={m}
                    className={form.prefMbti.includes(m) ? 'on' : ''}
                    onClick={() => toggle('prefMbti', m)}
                    style={{ fontFamily: 'var(--f-mono)', fontSize: 12, padding: '10px 4px' }}
                  >{m}</button>
                ))}
              </div>
              <div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
                <div className="opts">
                  <button
                    type="button"
                    className={form.prefMbti.length === 0 ? 'on' : ''}
                    onClick={() => set('prefMbti', [])}
                    style={{ fontSize: 13, fontWeight: 600 }}
                  >상관없음</button>
                </div>
                <span className="tiny" style={{ color: 'var(--ink-500)' }}>특정 유형만 고르려면 위에서 선택하세요.</span>
              </div>
            </Field>

            <Field label="선호 등급" hint="여러 개 또는 무관">
              <TierMultiSelect selected={form.prefTier} onToggle={(t) => toggle('prefTier', t)} />
              <div className="opts" style={{ marginTop: 8, width: '100%', display: 'block' }}>
                <button
                  type="button"
                  className={(form.prefTier || []).length === 0 ? 'on' : ''}
                  onClick={() => set('prefTier', [])}
                  style={{ width: '100%', fontSize: 13, fontWeight: 600 }}
                >등급 무관 (전체)</button>
              </div>
            </Field>
          </div>

          <div style={{
            marginTop: 12, padding: '10px 12px', borderRadius: 10,
            background: '#fff7ec', border: '1px solid #f3e3b8',
            fontSize: 12.5, color: '#8c6a05', lineHeight: 1.55,
          }}>
            ⚠️ <b>조건을 과하게 좁히면</b> 선정이 어려워요. 등급은 본인 기준 ±1~2단계 권장.
            {' '}
            <a href="https://www.instagram.com/color_sogaeting/" target="_blank" rel="noopener noreferrer" style={{ color: '#6e5304', fontWeight: 700 }}>@colorsogae</a>
            {' / '}
            <a href={OFFICIAL_OPEN_KAKAO_URL} target="_blank" rel="noopener noreferrer" style={{ color: '#6e5304', fontWeight: 700 }}>오픈채팅</a>
          </div>

          <Field label="선호 지역" hint={form.prefRegionOpen ? '무관으로 제출' : '여러 개 또는 무관'}>
            <div style={{ marginBottom: 8, display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
              <div className="opts">
                <button
                  type="button"
                  className={form.prefRegionOpen ? 'on' : ''}
                  onClick={() => {
                    setForm(f => ({
                      ...f,
                      prefRegionOpen: !f.prefRegionOpen,
                      prefRegions: !f.prefRegionOpen ? [] : f.prefRegions,
                    }));
                  }}
                  style={{ fontSize: 13, fontWeight: 600 }}
                >지역 무관</button>
              </div>
              {!form.prefRegionOpen && (form.prefRegions || []).length > 0 && (
                <span className="tiny" style={{ color: 'var(--ink-500)' }}>
                  {form.prefRegions.length}개 선택됨
                </span>
              )}
            </div>
            <div
              className="opts"
              style={{
                opacity: form.prefRegionOpen ? 0.4 : 1,
                pointerEvents: form.prefRegionOpen ? 'none' : 'auto',
                display: 'grid',
                gridTemplateColumns: 'repeat(auto-fill, minmax(72px, 1fr))',
                gap: 6,
              }}
            >
              {(window.SIDO_LIST || []).map(sido => {
                const on = (form.prefRegions || []).includes(sido);
                return (
                  <button
                    type="button"
                    key={sido}
                    className={on ? 'on' : ''}
                    onClick={() => {
                      setForm(f => {
                        const cur = new Set(f.prefRegions || []);
                        if (cur.has(sido)) cur.delete(sido);
                        else cur.add(sido);
                        return { ...f, prefRegions: Array.from(cur) };
                      });
                    }}
                    style={{ fontSize: 13, padding: '10px 4px' }}
                  >{sido}</button>
                );
              })}
            </div>
          </Field>

          <Field label="선호 나이" hint={form.prefAgeOpen ? '무관으로 제출' : `${form.prefAgeMin}살 — ${form.prefAgeMax}살`}>
            <div style={{ marginBottom: 4, display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
              <div className="opts">
                <button
                  type="button"
                  className={form.prefAgeOpen ? 'on' : ''}
                  onClick={() => {
                    setForm(f => {
                      const nextOpen = !f.prefAgeOpen;
                      return {
                        ...f,
                        prefAgeOpen: nextOpen,
                        prefAgeMin: nextOpen ? f.prefAgeMin : 26,
                        prefAgeMax: nextOpen ? f.prefAgeMax : 32,
                      };
                    });
                  }}
                  style={{ fontSize: 13, fontWeight: 600 }}
                >나이 무관</button>
              </div>
            </div>
            <RangeSlider
              min={20} max={50}
              minVal={form.prefAgeMin} maxVal={form.prefAgeMax}
              disabled={form.prefAgeOpen}
              onChange={(lo, hi) => { set('prefAgeMin', lo); set('prefAgeMax', hi); }}
            />
          </Field>

          <Field label="자기소개" hint={`${form.intro.length} / 500`}>
            <textarea
              className="textarea"
              maxLength={500}
              placeholder={"본인이 어떤 사람인지 자유롭게.\n취미·성격·평소 어떻게 시간 보내는지·만나면 뭐 하면 좋을지 등.\n진솔할수록 매칭 확률 ↑"}
              value={form.intro}
              onChange={e => set('intro', e.target.value)}
              style={{ minHeight: 140 }}
            />
          </Field>

          <Field label="이상형" hint={`${form.ideal.length} / 300`}>
            <textarea
              className="textarea"
              maxLength={300}
              placeholder={"어떤 톤의 사람이 좋은지 자유롭게.\n외모·성격·말투·취미 등 구체적일수록 좋아요."}
              value={form.ideal}
              onChange={e => set('ideal', e.target.value)}
            />
          </Field>
        </SectionCard>

        <SectionCard num="04" title="컬러소개팅에게 하는 말">
          <div style={{
            padding: '12px 14px', marginBottom: 12,
            background: 'linear-gradient(135deg, #fdf3e0, #fef0ef)',
            border: '1px solid #f3d8a8', borderRadius: 12,
            fontSize: 12.5, color: '#7a4a05', lineHeight: 1.55,
          }}>
            <span style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              background: '#161412', color: '#fff',
              fontFamily: 'var(--f-mono)', fontSize: 9.5, fontWeight: 700,
              padding: '2px 7px', borderRadius: 999,
              letterSpacing: '0.08em', marginBottom: 6,
            }}>
              <span style={{ width: 5, height: 5, background: '#5ad17a', borderRadius: 999 }} />
              ANTI-BOT
            </span>
            <div>
              <b>무분별하게 접수하는 봇을 걸러내기 위함입니다.</b><br />
              신청 목적 및 하고 싶은 말을 작성해주세요.<br />
              공백이거나 성의 없는 내용은 <b>자동 폐기 처리</b>됩니다.
            </div>
          </div>
          <Field
            label="하고 싶은 말"
            req
            hint={`최소 ${APPLY_MESSAGE_MIN_LEN}자 이상 · ${form.message.length} / 500`}
            anchor="message"
            showError={err('message')}
            errorText={
              err('message') && String(form.message || '').trim().length < APPLY_MESSAGE_MIN_LEN
                ? `봇·무성의 접수를 줄이기 위해 최소 ${APPLY_MESSAGE_MIN_LEN}자 이상이 필요합니다. (현재 ${String(form.message || '').trim().length}자)`
                : null
            }
          >
            <textarea
              className="textarea"
              maxLength={500}
              placeholder={"예: 시간 내서 운영해주실 운영자님께 감사합니다.\n\n종종 소개팅 관련해서 아는 지인들이 없고,\n제대로된 만남을 가져보고 싶어서 신청합니다.\n잘 부탁드립니다!"}
              value={form.message}
              onChange={e => set('message', e.target.value)}
              style={{ minHeight: 140 }}
            />
          </Field>
        </SectionCard>

        <SectionCard num="05" title="내 사진">
          <div className="tiny" style={{ marginBottom: 12 }}>
            최대 5장 · 얼굴이 잘 나온 사진 1장 필수
          </div>
          <div
            data-apply-anchor="photos"
            className={err('photos') ? 'apply-invalid-photo-grid' : undefined}
          >
            <PhotoGrid form={form} appendPhoto={appendPhoto} removePhoto={removePhoto} />
          </div>
        </SectionCard>

        <SectionCard num="06" title="개인정보 안내 · 동의">
          <div style={{
            background: '#fff', border: '1px solid var(--line)', borderRadius: 16,
            padding: 18, marginBottom: 14,
          }}>
            <div style={{ fontSize: 13.5, lineHeight: 1.7, color: 'var(--ink-700)' }}>
              제출하신 모든 개인정보는 <b>소개팅 매칭 용도로만</b> 사용됩니다.<br />
              매칭이 종료되거나 본인이 <b>요청하시면 언제든 즉시·완전 파기</b>됩니다.<br />
              제3자에게 절대 공유되지 않으며, 운영자 1인만 열람 가능합니다.<br />
              <span style={{ color: 'var(--ink-500)', fontSize: 12.5 }}>
                · 파기/수정 문의: <a href={OFFICIAL_OPEN_KAKAO_URL} target="_blank" rel="noopener noreferrer" style={{ color: 'var(--ink-700)', fontWeight: 600, textDecoration: 'underline' }}>카카오톡 오픈채팅</a>
              </span>
            </div>
          </div>

          <div data-apply-anchor="agree1" style={{ marginBottom: 8 }}>
            <Consent
              checked={form.agree1}
              invalid={err('agree1')}
              onToggle={() => set('agree1', !form.agree1)}
              label={<>개인정보 수집·이용에 동의합니다 <span style={{ color: 'var(--brand)' }}>(필수)</span></>}
            />
          </div>
          <div data-apply-anchor="agree2">
            <Consent
              checked={form.agree2}
              invalid={err('agree2')}
              onToggle={() => set('agree2', !form.agree2)}
              label={<>매칭 진행을 위한 상대에게 정보 제공에 동의합니다 <span style={{ color: 'var(--brand)' }}>(필수)</span></>}
            />
          </div>
          <div style={{ marginTop: 14 }}>
            <UserFooterOfficialLinks />
          </div>
        </SectionCard>
      </div>

      <div style={{
        position: 'sticky', bottom: 0, background: 'var(--bg-app)',
        padding: '10px 22px max(16px, calc(12px + env(safe-area-inset-bottom, 0px)))',
        backgroundImage: 'linear-gradient(180deg, transparent, var(--bg-app) 18px)',
      }}>
        {error && (
          <div style={{
            marginBottom: 10, padding: '10px 14px', borderRadius: 12,
            background: '#fee5e2', color: '#a8281d', fontSize: 13, fontWeight: 500,
          }}>
            ⚠️ {error}
          </div>
        )}
        <button
          className="btn brand full"
          onClick={trySubmit}
          disabled={submitting}
          style={{
            opacity: submitting ? 0.65 : 1,
            cursor: submitting ? 'wait' : 'pointer',
          }}
        >
          {submitting ? '접수 중…' : '소개팅 신청하기'}
        </button>
        <div className="tiny" style={{ textAlign: 'center', marginTop: 14, lineHeight: 1.55, color: 'var(--ink-500)' }}>
          작성 시간 약 4-6분 · 5개 사진 첨부 권장
        </div>
        {invalidAnchors.length > 0 && (
          <div className="tiny" style={{ textAlign: 'center', marginTop: 10, color: 'var(--ink-500)', lineHeight: 1.5 }}>
            빨간 테두리가 남아 있으면 아직 조건을 충족하지 않은 항목입니다. (하고 싶은 말은 공백 제외 10자 이상)
          </div>
        )}
      </div>
    </div>
  );
}

function SectionCard({ num, title, children }) {
  return (
    <div style={{ marginBottom: 18 }}>
      <div style={{
        display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 14,
        padding: '0 4px',
      }}>
        <span style={{
          fontFamily: 'var(--f-mono)', fontSize: 11, letterSpacing: '0.1em',
          color: 'var(--ink-400)',
        }}>{num}</span>
        <span style={{
          fontSize: 19, fontWeight: 700, letterSpacing: '-0.02em',
          color: 'var(--ink-900)',
        }}>{title}</span>
        <div style={{ flex: 1, height: 1, background: 'var(--line)', marginLeft: 6 }} />
      </div>
      <div>{children}</div>
    </div>
  );
}

function PhotoGrid({ form, appendPhoto, removePhoto }) {
  const inputRef = useRef(null);
  const pickPhoto = () => {
    if (form.photos.length >= 5) return;
    inputRef.current && inputRef.current.click();
  };
  const onFile = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      const photo = { id: Date.now(), file, preview: ev.target.result };
      appendPhoto(photo);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };
  const remove = (i) => removePhoto(i);

  return (
    <div className="photo-grid">
      <input ref={inputRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={onFile} />
      {Array.from({ length: 5 }).map((_, i) => {
        const has = form.photos[i];
        return (
          <div
            key={i}
            className={`photo-cell ${has ? 'filled' : ''}`}
            onClick={() => !has && pickPhoto()}
            style={has && has.preview ? { backgroundImage: `url(${has.preview})`, backgroundSize: 'cover', backgroundPosition: 'center' } : {}}
          >
            {has ? (
              <>
                <span className="num">{String(i + 1).padStart(2, '0')}</span>
                <span className="x" onClick={(e) => { e.stopPropagation(); remove(i); }}>×</span>
              </>
            ) : (
              <span>{i === 0 ? '+ 메인' : '+ 추가'}</span>
            )}
          </div>
        );
      })}
    </div>
  );
}

function RangeSlider({ min, max, minVal, maxVal, onChange, disabled }) {
  const ref = useRef(null);
  const handle = (which) => (e) => {
    if (disabled) return;
    e.preventDefault();
    const rect = ref.current.getBoundingClientRect();
    const move = (ev) => {
      const x = (ev.touches ? ev.touches[0].clientX : ev.clientX) - rect.left;
      const ratio = Math.max(0, Math.min(1, x / rect.width));
      const val = Math.round(min + ratio * (max - min));
      if (which === 'lo') onChange(Math.min(val, maxVal - 1), maxVal);
      else onChange(minVal, Math.max(val, minVal + 1));
    };
    const up = () => {
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
      window.removeEventListener('touchmove', move);
      window.removeEventListener('touchend', up);
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
    window.addEventListener('touchmove', move, { passive: false });
    window.addEventListener('touchend', up);
  };
  const loPct = ((minVal - min) / (max - min)) * 100;
  const hiPct = ((maxVal - min) / (max - min)) * 100;
  return (
    <div ref={ref} style={{
      position: 'relative', height: 56, padding: '24px 0',
      opacity: disabled ? 0.45 : 1, pointerEvents: disabled ? 'none' : 'auto',
    }}>
      <div style={{
        position: 'absolute', left: 0, right: 0, top: 28, height: 4,
        background: 'var(--bg-sunken)', borderRadius: 999,
      }} />
      <div style={{
        position: 'absolute', left: `${loPct}%`, right: `${100 - hiPct}%`, top: 28, height: 4,
        background: 'var(--ink-900)', borderRadius: 999,
      }} />
      {[{ p: loPct, h: 'lo' }, { p: hiPct, h: 'hi' }].map(t => (
        <div
          key={t.h}
          onMouseDown={handle(t.h)}
          onTouchStart={handle(t.h)}
          style={{
            position: 'absolute', left: `${t.p}%`, top: 18, width: 24, height: 24,
            transform: 'translateX(-50%)', background: '#fff', borderRadius: 999,
            border: '2px solid var(--ink-900)', cursor: 'grab',
            boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
          }}
        />
      ))}
    </div>
  );
}

function Consent({ checked, onToggle, label, invalid }) {
  return (
    <button
      onClick={onToggle}
      style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 12,
        padding: '14px 16px', background: checked ? '#fff' : 'var(--bg-soft)',
        border: `1px solid ${checked ? 'var(--ink-900)' : 'var(--line)'}`,
        borderRadius: 14, marginBottom: 8, textAlign: 'left',
        boxShadow: invalid ? '0 0 0 2px #ec4438' : undefined,
      }}
    >
      <div style={{
        width: 22, height: 22, borderRadius: 999,
        background: checked ? 'var(--ink-900)' : '#fff',
        border: `1.5px solid ${checked ? 'var(--ink-900)' : 'var(--ink-300)'}`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: '#fff', fontSize: 12, flexShrink: 0,
      }}>{checked ? '✓' : ''}</div>
      <div style={{ fontSize: 13.5, color: 'var(--ink-700)', lineHeight: 1.4 }}>{label}</div>
    </button>
  );
}

function Field({ label, req, hint, children, style, anchor, showError, errorText }) {
  return (
    <div className="field" data-apply-anchor={anchor || undefined} style={style}>
      <div className="field-label">
        <div>{label}{req && <span className="req">*</span>}</div>
        {hint && <div className="field-hint">{hint}</div>}
      </div>
      <div className={showError ? 'field-controls field-controls-invalid' : 'field-controls'}>
        {children}
      </div>
      {errorText ? (
        <div style={{ marginTop: 8, fontSize: 12.5, color: '#b71c1c', fontWeight: 500, lineHeight: 1.45 }}>
          {errorText}
        </div>
      ) : null}
    </div>
  );
}

function SectionTitle({ children, style }) {
  return (
    <div style={{
      fontFamily: 'var(--f-mono)', fontSize: 11, letterSpacing: '0.1em',
      color: 'var(--ink-400)', textTransform: 'uppercase', marginBottom: 14,
      ...style,
    }}>{children}</div>
  );
}

// ─────────────────────────────────────────────────────────────
// 완료 화면
// ─────────────────────────────────────────────────────────────
function ApplyDoneScreen({ go, applyResult }) {
  const code = applyResult?.memberCode || '신청완료';
  return (
    <div className="app" style={{ display: 'flex', flexDirection: 'column' }}>
      <div className="statusbar-spacer" />
      <div style={{
        flex: 1, display: 'flex', flexDirection: 'column',
        justifyContent: 'center', padding: '0 30px', textAlign: 'left',
      }}>
        <div style={{ display: 'flex', gap: 4, marginBottom: 28 }}>
          {[
            { c: 'linear-gradient(135deg, #9adfff, #c8b8ff, #ffc6f0)', name: 'D' },
            { c: '#161412', name: 'B' },
            { c: '#e23a2e', name: 'R' },
            { c: '#2f6cf6', name: 'L' },
            { c: '#f5c419', name: 'Y' },
            { c: '#fff', name: 'W' },
          ].map((t, i) => (
            <div key={i} style={{
              width: 26, height: 32, borderRadius: 7, background: t.c,
              border: t.name === 'W' || t.name === 'B' ? '1px solid var(--ink-200)' : '0',
              boxShadow: t.name === 'D' ? 'inset 0 0 6px rgba(255,255,255,0.6)' : 'none',
              animation: `pop 600ms ${i * 70}ms backwards ease`,
            }} />
          ))}
        </div>
        <div style={{
          fontFamily: 'var(--f-mono)', fontSize: 11, letterSpacing: '0.1em',
          color: 'var(--ink-400)', textTransform: 'uppercase', marginBottom: 14,
        }}>RECEIVED · #ID {code}</div>
        <h1 style={{
          margin: 0, fontSize: 38, lineHeight: 1.1, fontWeight: 700,
          letterSpacing: '-0.03em',
        }}>
          접수 완료.<br />
          <span style={{ fontFamily: 'var(--f-serif)', fontStyle: 'italic', fontWeight: 400, color: 'var(--ink-500)' }}>
            기다려 주세요.
          </span>
        </h1>
        <p style={{
          marginTop: 22, fontSize: 15, color: 'var(--ink-500)', lineHeight: 1.65,
        }}>
          접수 검토 후 약 <b style={{ color: 'var(--ink-900)' }}>3-7일</b> 안에<br />
          카톡으로 매칭 안내 드릴게요.<br /><br />
          파기/수정 요청은 카카오톡<br />
          <a href={OFFICIAL_OPEN_KAKAO_URL} target="_blank" rel="noopener noreferrer" style={{ color: 'var(--ink-900)', fontWeight: 700, textDecoration: 'underline' }}>오픈채팅으로 연락하기 ↗</a>
        </p>
      </div>
      <div style={{ padding: '14px 22px 32px' }}>
        <button className="btn ghost full" onClick={() => go('home')}>홈으로</button>
      </div>
      <style>{`@keyframes pop { from { opacity: 0; transform: translateY(8px) scale(0.8); } }`}</style>
    </div>
  );
}

/**
 * 거주지/근무지 시·도 + 시·군·구 두 단계 드롭다운.
 * - sido 변경 시 sigungu 초기화
 * - 시·도 = '해외/기타' 이면 시·군·구 자유 입력
 */
function RegionPicker({ sido, sigungu, onChange }) {
  const sidoList = window.SIDO_LIST || [];
  const sigunguMap = window.SIGUNGU_MAP || {};
  const list = (sido && sigunguMap[sido]) || [];
  const isOverseas = sido === '해외/기타';

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1.4fr', gap: 8 }}>
      <select
        className="input"
        value={sido || ''}
        onChange={(e) => onChange({ sido: e.target.value, sigungu: '' })}
        style={{ appearance: 'none', WebkitAppearance: 'none', backgroundImage: 'none' }}
      >
        <option value="">시·도 선택</option>
        {sidoList.map((s) => (
          <option key={s} value={s}>{s}</option>
        ))}
      </select>
      {isOverseas ? (
        <input
          className="input"
          placeholder="국가/도시 입력"
          value={sigungu || ''}
          onChange={(e) => onChange({ sido, sigungu: e.target.value })}
        />
      ) : (
        <select
          className="input"
          value={sigungu || ''}
          onChange={(e) => onChange({ sido, sigungu: e.target.value })}
          disabled={!sido || list.length === 0}
          style={{ appearance: 'none', WebkitAppearance: 'none', backgroundImage: 'none' }}
        >
          <option value="">{sido ? '시·군·구 선택' : '시·도 먼저'}</option>
          {list.map((g) => (
            <option key={g} value={g}>{g}</option>
          ))}
        </select>
      )}
    </div>
  );
}

function TierMultiSelect({ selected, onToggle }) {
  const tiers = [
    { v: 'diamond', l: 'Diamond', desc: 'RANK 6', color: 'linear-gradient(135deg, #9adfff, #c8b8ff, #ffc6f0)' },
    { v: 'black', l: 'Black', desc: 'RANK 5', color: '#161412' },
    { v: 'red', l: 'Red', desc: 'RANK 4', color: '#e23a2e' },
    { v: 'blue', l: 'Blue', desc: 'RANK 3', color: '#2f6cf6' },
    { v: 'yellow', l: 'Yellow', desc: 'RANK 2', color: '#f5c419' },
    { v: 'white', l: 'White', desc: 'RANK 1', color: '#fff' },
  ];
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6,
    }}>
      {tiers.map(t => {
        const on = selected.includes(t.v);
        return (
          <button
            type="button"
            key={t.v}
            onClick={() => onToggle(t.v)}
            style={{
              padding: '12px 8px 10px', borderRadius: 12,
              background: '#fff',
              border: on ? '2px solid var(--ink-900)' : '1px solid var(--line)',
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
              position: 'relative',
              transition: 'transform 100ms ease',
            }}
          >
            {on && (
              <span style={{
                position: 'absolute', top: 6, right: 6,
                width: 16, height: 16, borderRadius: 999,
                background: 'var(--ink-900)', color: '#fff',
                fontSize: 10, display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>✓</span>
            )}
            <div style={{
              width: 28, height: 28, borderRadius: 999, background: t.color,
              border: t.v === 'white' ? '1px solid var(--ink-300)' : (t.v === 'black' ? '1px solid #2a2522' : '1px solid rgba(0,0,0,0.06)'),
              boxShadow: t.v === 'diamond' ? 'inset 0 0 6px rgba(255,255,255,0.6)' : 'none',
            }} />
            <div style={{
              fontFamily: 'var(--f-mono)', fontSize: 11, fontWeight: 700,
              letterSpacing: '0.04em', color: 'var(--ink-900)',
            }}>{t.l}</div>
            <div style={{
              fontFamily: 'var(--f-mono)', fontSize: 9, color: 'var(--ink-400)',
              letterSpacing: '0.06em',
            }}>{t.desc}</div>
          </button>
        );
      })}
    </div>
  );
}

Object.assign(window, { HomeScreen, ApplyScreen, ApplyDoneScreen, SectionTitle });
