/* global React */
// Screens part 1 — Login, Home, Category, Product Detail
const { useState: useS1, useEffect: useE1, useContext: useC1, Fragment: F1 } = React;

// ============================================================
// 01 LOGIN — WeChat authorize
// ============================================================
function LoginScreen() {
  // 沿用 /chat 的验证码登录流 (自动注册不存在的账号)
  const [mode, setMode] = useS1('email'); // 'email' | 'tel'
  const [email, setEmail] = useS1('');
  const [tel, setTel] = useS1('');
  const [countryIdx, setCountryIdx] = useS1(0); // 默认 CN
  const [captcha, setCaptcha] = useS1('');
  const [loading, setLoading] = useS1(false);
  const [sending, setSending] = useS1(false);
  const [countdown, setCountdown] = useS1(0);
  const [err, setErr] = useS1('');
  const [info, setInfo] = useS1('');
  const { user, isAuthed } = window.useAuth();

  // 三方登录补邮箱弹窗 state
  const [bindModal, setBindModal] = useS1(null); // null | { tempToken, provider, nickname }
  const [bindEmail, setBindEmail] = useS1('');
  const [bindCaptcha, setBindCaptcha] = useS1('');
  const [bindSending, setBindSending] = useS1(false);
  const [bindLoading, setBindLoading] = useS1(false);
  const [bindCountdown, setBindCountdown] = useS1(0);
  const [bindErr, setBindErr] = useS1('');
  const [bindInfo, setBindInfo] = useS1('');

  useE1(() => {
    if (bindCountdown <= 0) return;
    const t = setTimeout(() => setBindCountdown(c => c - 1), 1000);
    return () => clearTimeout(t);
  }, [bindCountdown]);

  async function loginWithOAuth(provider) {
    setErr(''); setInfo('');
    try {
      // V1 stub: 模拟拿到 openid + nickname + avatar
      // (真实接入时,这里改成调微信 JS-SDK / 支付宝 SDK 的授权流程拿 openid)
      const ts = Date.now();
      const mockOpenid = `${provider}_${ts}_${Math.random().toString(36).slice(2,8)}`;
      const mockNick = provider === 'wechat' ? '微信用户' + String(ts).slice(-4) : '支付宝用户' + String(ts).slice(-4);
      const d = await window.auth.oauthLogin({
        provider, openid: mockOpenid, nickname: mockNick, avatar: '',
      });
      if (d.needBindEmail && d.tempToken) {
        // 弹补邮箱表单
        setBindModal({ tempToken: d.tempToken, provider, nickname: d.user?.name || mockNick });
        setBindEmail(''); setBindCaptcha(''); setBindErr(''); setBindInfo('');
      } else {
        // 已绑过邮箱,直接进首页
        window.location.hash = '#/home';
      }
    } catch (e) { setErr(e.message || '三方登录失败'); }
  }

  async function bindSendCode() {
    setBindErr(''); setBindInfo(''); setBindSending(true);
    try {
      if (!bindEmail) { setBindErr('请填邮箱'); return; }
      await window.auth.sendEmailCode(bindEmail);
      setBindInfo('验证码已发送,请查收');
      setBindCountdown(60);
    } catch (e) { setBindErr(e.message || '发送失败'); }
    finally { setBindSending(false); }
  }

  async function bindSubmit() {
    setBindErr(''); setBindInfo(''); setBindLoading(true);
    try {
      if (!bindEmail || !bindCaptcha) { setBindErr('邮箱和验证码都需要'); return; }
      await window.auth.bindEmailWithTempToken({
        tempToken: bindModal.tempToken, email: bindEmail, captcha: bindCaptcha,
      });
      // 完成后等于走了一次 email-login 新建账号
      setBindModal(null);
      window.location.hash = '#/home';
    } catch (e) { setBindErr(e.message || '绑定失败'); }
    finally { setBindLoading(false); }
  }

  // 倒计时 60s
  useE1(() => {
    if (countdown <= 0) return;
    const t = setTimeout(() => setCountdown(c => c - 1), 1000);
    return () => clearTimeout(t);
  }, [countdown]);

  const country = (window.PHONE_COUNTRIES || [{ code: '86', iso: 'CN' }])[countryIdx] || { code: '86', iso: 'CN' };

  async function sendCode() {
    setErr(''); setInfo(''); setSending(true);
    try {
      if (mode === 'email') {
        if (!email) { setErr('请填邮箱'); return; }
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { setErr('邮箱格式不对'); return; }
        await window.auth.sendEmailCode(email);
      } else {
        if (!tel) { setErr('请填手机号'); return; }
        if (!/^\d{6,15}$/.test(tel)) { setErr('手机号格式不对'); return; }
        await window.auth.sendTelCode(tel, country.code, country.iso);
      }
      setInfo('验证码已发送,请查收');
      setCountdown(60);
    } catch (e) { setErr(e.message || '发送失败'); }
    finally { setSending(false); }
  }

  async function submit() {
    setErr(''); setInfo(''); setLoading(true);
    try {
      if (!captcha) { setErr('请填验证码'); return; }
      if (mode === 'email') {
        if (!email) { setErr('请填邮箱'); return; }
        await window.auth.loginEmail({ email, captcha });
      } else {
        if (!tel) { setErr('请填手机号'); return; }
        await window.auth.loginTel({ tel, captcha, areaCode: country.code, isoAlpha2: country.iso });
      }
      window.location.hash = '#/home';
    } catch (e) { setErr(e.message || '登录失败'); }
    finally { setLoading(false); }
  }

  return (
    <div className="appbody" style={{ background: '#fff', display: 'flex', flexDirection: 'column' }}>
      {/* hero */}
      <div style={{ position: 'relative', padding: '48px 24px 24px', background: 'linear-gradient(180deg, var(--brand-soft) 0%, #fff 100%)', overflow: 'hidden' }}>
        <div style={{ position: 'absolute', top: 36, right: -40, width: 200, height: 200, borderRadius: '50%', background: 'url(assets/sinsagom-1.jpg) center/cover', opacity: 0.85 }} />
        <div style={{ position: 'relative' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 28, letterSpacing: '-0.03em' }}>
            ap<span style={{ color: 'var(--brand)' }}>M</span>
            <span style={{ fontSize: 12, fontWeight: 600, marginLeft: 6, color: 'var(--fg-3)', letterSpacing: '0.08em' }}>SEOUL</span>
          </div>
          <div style={{ marginTop: 12, fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 800, lineHeight: 1.2, letterSpacing: '-0.02em', maxWidth: 200 }}>
            把<span style={{ color: 'var(--brand)' }}>首尔东大门</span><br />装进口袋
          </div>
        </div>
      </div>

      <div style={{ padding: '24px 20px', flex: 1, display: 'flex', flexDirection: 'column', gap: 14 }}>
        {isAuthed && !user?.isGuest ? (
          <div style={{ background: '#FAFAFA', borderRadius: 12, padding: 18, textAlign: 'center' }}>
            <div style={{ fontSize: 13, color: 'var(--fg-2)' }}>已登录</div>
            <div style={{ fontSize: 18, fontWeight: 700, marginTop: 6 }}>{user.name || user.email}</div>
            <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 4, fontFamily: 'var(--font-mono)' }}>{user.email}</div>
            <div style={{ marginTop: 14, display: 'flex', gap: 8, justifyContent: 'center' }}>
              <a href="#/home" className="btn primary" style={{ padding: '10px 20px', textDecoration: 'none' }}>去首页</a>
              <button className="btn ghost" onClick={() => { window.auth.logout(); }}>退出登录</button>
            </div>
          </div>
        ) : (
          <>
            {/* tab: 邮箱 / 手机号 */}
            <div style={{ display: 'flex', borderBottom: '1px solid var(--border-1)' }}>
              <button onClick={() => setMode('email')} style={{ flex: 1, padding: '10px 0', background: 'none', border: 'none', borderBottom: '2px solid ' + (mode === 'email' ? 'var(--brand)' : 'transparent'), color: mode === 'email' ? 'var(--brand)' : 'var(--fg-2)', fontWeight: mode === 'email' ? 700 : 500, cursor: 'pointer', fontSize: 13 }}>邮箱登录</button>
              <button onClick={() => setMode('tel')} style={{ flex: 1, padding: '10px 0', background: 'none', border: 'none', borderBottom: '2px solid ' + (mode === 'tel' ? 'var(--brand)' : 'transparent'), color: mode === 'tel' ? 'var(--brand)' : 'var(--fg-2)', fontWeight: mode === 'tel' ? 700 : 500, cursor: 'pointer', fontSize: 13 }}>手机号登录</button>
            </div>

            <div style={{ fontSize: 11, color: 'var(--fg-3)', textAlign: 'center', padding: '4px 0' }}>
              首次登录将自动创建账号 · 沿用 apM Chat 体系
            </div>

            {mode === 'email' ? (
              <input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="邮箱" autoComplete="email" style={{ padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, fontFamily: 'inherit' }} />
            ) : (
              <div style={{ display: 'flex', gap: 6 }}>
                <select
                  value={countryIdx}
                  onChange={e => setCountryIdx(Number(e.target.value))}
                  style={{ width: 110, padding: '12px 8px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, background: '#fff', fontFamily: 'inherit', cursor: 'pointer' }}
                >
                  {(window.PHONE_COUNTRIES || []).map((c, idx) => (
                    <option key={`${c.iso}-${c.code}`} value={idx}>{c.flag} +{c.code}</option>
                  ))}
                </select>
                <input
                  type="tel" inputMode="numeric" pattern="[0-9]*"
                  value={tel}
                  onChange={e => setTel(e.target.value.replace(/\D/g, ''))}
                  onKeyDown={e => {
                    if (e.key.length === 1 && !/[0-9]/.test(e.key) && !e.ctrlKey && !e.metaKey && !e.altKey) e.preventDefault();
                  }}
                  onPaste={e => {
                    const text = e.clipboardData.getData('text');
                    const digits = text.replace(/\D/g, '');
                    if (digits !== text) {
                      e.preventDefault();
                      setTel(digits);
                    }
                  }}
                  placeholder={`手机号 (${country.iso} +${country.code})`}
                  autoComplete="tel"
                  style={{ flex: 1, padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, fontFamily: 'inherit' }}
                />
              </div>
            )}

            <div style={{ display: 'flex', gap: 6 }}>
              <input value={captcha} onChange={e => setCaptcha(e.target.value)} placeholder="验证码" autoComplete="one-time-code" style={{ flex: 1, padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, fontFamily: 'inherit' }} />
              <button onClick={sendCode} disabled={sending || countdown > 0} style={{ padding: '0 14px', background: countdown > 0 ? '#F5F5F5' : 'var(--brand-soft)', color: countdown > 0 ? 'var(--fg-3)' : 'var(--brand)', border: 'none', borderRadius: 8, fontSize: 12, fontWeight: 600, cursor: countdown > 0 ? 'not-allowed' : 'pointer', whiteSpace: 'nowrap' }}>
                {countdown > 0 ? `${countdown}s` : (sending ? '发送中…' : '发送验证码')}
              </button>
            </div>

            {err && <div style={{ background: '#FEF2F2', color: '#B91C1C', borderRadius: 8, padding: '8px 12px', fontSize: 12 }}>{err}</div>}
            {info && <div style={{ background: '#F0FDF4', color: '#15803D', borderRadius: 8, padding: '8px 12px', fontSize: 12 }}>{info}</div>}
            <button className="btn primary block lg" onClick={submit} disabled={loading}>
              {loading ? '处理中…' : '登录 / 注册'}
            </button>

            {/* OAuth 分隔条 */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, margin: '8px 0', color: 'var(--fg-3)', fontSize: 11 }}>
              <div style={{ flex: 1, height: 1, background: 'var(--border-1)' }} />
              <span>第三方快捷登录</span>
              <div style={{ flex: 1, height: 1, background: 'var(--border-1)' }} />
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
              <button onClick={() => loginWithOAuth('wechat')}
                style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '10px 14px', borderRadius: 8, border: 'none', background: '#1AAD19', color: '#fff', fontWeight: 600, fontSize: 13, cursor: 'pointer', fontFamily: 'inherit' }}>
                <svg width="18" height="18" viewBox="0 0 24 24" fill="#fff"><path d="M8.7 14.4c-.3 0-.6-.1-.8-.4l-2.8-2.8c-.1-.1-.1-.2 0-.3l.4-.5c.1-.1.2-.1.3 0l2.6 1.5L13.2 9c.3-.2.7-.1.9.2.2.3.1.7-.2.9l-4.7 3.9c-.1.1-.3.2-.5.2zm12-9C19 4 16.7 3 14.1 3 8.8 3 4.5 6.7 4.5 11.4c0 2.5 1.3 4.7 3.4 6.2L7.2 20l2.7-1.4c1.1.3 2.3.5 3.4.5h.1l-.1-.4c-.1-.5-.1-1 0-1.5-.5.1-1 .1-1.5.1-4.4 0-8-3-8-6.7s3.6-6.7 8-6.7c4 0 7.4 2.5 8 5.8.4-.1.8-.2 1.3-.2.4 0 .8 0 1.2.1-.7-1.7-2-3.2-3.6-4.2zm-9 1.7c-.6 0-1 .4-1 1s.4 1 1 1 1-.4 1-1-.5-1-1-1zm5.6 0c-.6 0-1 .4-1 1s.4 1 1 1 1-.4 1-1-.4-1-1-1z"/></svg>
                微信
              </button>
              <button onClick={() => loginWithOAuth('alipay')}
                style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '10px 14px', borderRadius: 8, border: 'none', background: '#1677FF', color: '#fff', fontWeight: 600, fontSize: 13, cursor: 'pointer', fontFamily: 'inherit' }}>
                <svg width="18" height="18" viewBox="0 0 24 24" fill="#fff"><path d="M22.5 17.6c-1.3-.4-3-1-5-1.7-1.2-.4-2.5-.9-3.6-1.4 1-1.4 1.8-3.1 2.2-4.9h-3.7v-1.5h4.5V7H11.4V4.6H9.7c-.3 0-.3.3-.3.3v1.7H4.9V8h4.5v1.5H6v1.4h7.5c-.3 1-.7 2-1.2 3-2.3-.7-4.7-1.3-6.4-1.4-3 0-3.7 1.3-3.7 2.7 0 2 2 3.5 4.7 3.5 2 0 4.2-.8 5.9-2.3 2.5 1.2 7.6 3.4 7.6 3.4l-.1.1c1 0 1.8-.8 1.8-1.8v-.5zM5.7 16.6c-1.3 0-2-.7-2-1.4 0-.4.2-1 1.6-1 1.4 0 3 .4 4.8 1.1-1.4 1-2.9 1.3-4.4 1.3z"/></svg>
                支付宝
              </button>
            </div>

            <div style={{ marginTop: 4, fontSize: 10, color: 'var(--fg-3)', textAlign: 'center', lineHeight: 1.6 }}>
              登录即代表您已阅读并同意<br />
              <span style={{ color: 'var(--fg-1)', textDecoration: 'underline' }}>《服务协议》</span> · <span style={{ color: 'var(--fg-1)', textDecoration: 'underline' }}>《隐私政策》</span>
            </div>
          </>
        )}

        {/* === 补邮箱弹窗 === */}
        {bindModal && (
          <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.55)', zIndex: 1000, display: 'grid', placeItems: 'center', padding: 16 }} onClick={() => setBindModal(null)}>
            <div onClick={e => e.stopPropagation()} style={{ background: '#fff', borderRadius: 16, padding: 20, width: '100%', maxWidth: 400, boxShadow: '0 20px 60px rgba(0,0,0,0.3)' }}>
              <div style={{ fontSize: 16, fontWeight: 700 }}>补充邮箱</div>
              <div style={{ marginTop: 6, fontSize: 12, color: 'var(--fg-3)', lineHeight: 1.6 }}>
                {bindModal.provider === 'wechat' ? '微信' : '支付宝'} 已识别身份「<b style={{ color: 'var(--fg-1)' }}>{bindModal.nickname}</b>」<br />
                请补一个邮箱用于接收订单 / 物流通知,完成后等同邮箱注册。
              </div>
              <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
                <input type="email" value={bindEmail} onChange={e => setBindEmail(e.target.value)} placeholder="邮箱" autoComplete="email" style={{ padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, fontFamily: 'inherit' }} />
                <div style={{ display: 'flex', gap: 6 }}>
                  <input value={bindCaptcha} onChange={e => setBindCaptcha(e.target.value)} placeholder="验证码" autoComplete="one-time-code" style={{ flex: 1, padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border-1)', fontSize: 13, fontFamily: 'inherit' }} />
                  <button onClick={bindSendCode} disabled={bindSending || bindCountdown > 0} style={{ padding: '0 14px', background: bindCountdown > 0 ? '#F5F5F5' : 'var(--brand-soft)', color: bindCountdown > 0 ? 'var(--fg-3)' : 'var(--brand)', border: 'none', borderRadius: 8, fontSize: 12, fontWeight: 600, cursor: bindCountdown > 0 ? 'not-allowed' : 'pointer', whiteSpace: 'nowrap' }}>
                    {bindCountdown > 0 ? `${bindCountdown}s` : (bindSending ? '发送中…' : '发送验证码')}
                  </button>
                </div>
                {bindErr && <div style={{ background: '#FEF2F2', color: '#B91C1C', borderRadius: 8, padding: '8px 12px', fontSize: 12 }}>{bindErr}</div>}
                {bindInfo && <div style={{ background: '#F0FDF4', color: '#15803D', borderRadius: 8, padding: '8px 12px', fontSize: 12 }}>{bindInfo}</div>}
                <div style={{ display: 'flex', gap: 8 }}>
                  <button className="btn ghost" style={{ flex: 1 }} onClick={() => setBindModal(null)}>取消</button>
                  <button className="btn primary" style={{ flex: 1.5 }} onClick={bindSubmit} disabled={bindLoading}>
                    {bindLoading ? '绑定中…' : '完成绑定'}
                  </button>
                </div>
              </div>
            </div>
          </div>
        )}

        <div style={{ marginTop: 'auto', paddingTop: 16, borderTop: '1px solid var(--border-1)', display: 'flex', alignItems: 'center', justifyContent: 'space-between', fontSize: 9, color: 'var(--fg-3)' }}>
          <span style={{ fontFamily: 'var(--font-mono)', letterSpacing: '0.08em' }}>© apM Korea Co.,Ltd</span>
          <span className="curpill" style={{ padding: '2px 6px', fontSize: 9 }}>
            <Icon name="globe" size={10} sw={2} /> 简体中文
          </span>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// 02 HOME — feed-style Korean retail
// ============================================================
function HomeScreen({ onCur }) {
  const { code } = useC1(CurContext);
  const cur = CURRENCIES[code];

  // 公告:从 apmchat /api/site-announcements 拉, 多条轮播
  const { lang } = useC1(window.LangContext);
  const ann = window.useApi('/site-announcements?audience=customer', {
    fallback: { items: [{ titleI18n: { zh: '今夜东大门直发 · 凌晨3点封袋', ko: '오늘밤 동대문 직발송 · 새벽 3시 마감', en: 'Tonight: Dongdaemun direct ship · seal by 3am' } }] },
  });
  const annItems = ann.data?.items || [];
  const [annIdx, setAnnIdx] = useS1(0);
  useE1(() => {
    if (annItems.length <= 1) return;
    const t = setInterval(() => setAnnIdx(i => (i + 1) % annItems.length), 4000);
    return () => clearInterval(t);
  }, [annItems.length]);
  const annText = annItems[annIdx] ? window.pickI18n(annItems[annIdx].titleI18n, lang) : '';

  // HOT 100 商品 - 跨档口按销量排序 取前 8
  const hotState = window.useDateall('listProducts', { sortBy: 'sold', limit: 8 }, []);
  const hotItems = hotState.data?.items || [];
  // 为你推荐 - 跨档口随机 6 个 (取 DK 认证或高评分)
  const forYouState = window.useDateall('listProducts', { limit: 30 }, []);
  const forYouItems = (forYouState.data?.items || []).filter(p => p.tags?.includes('DK') || p.rating >= 4.7).slice(0, 6);

  // hero carousel data
  const slides = [
    { tag: 'NEW SEASON · 26FW', title: '首尔街头女装', sub: '新天地直发 · 满399免邮', bg: 'linear-gradient(120deg, #1A1A1A, #2D1B2E)', img: 1 },
  ];

  // categories
  const cats = [
    { i: 'fire', l: '热卖榜' },
    { i: 'live', l: 'AI 直播' },
    { i: 'qr', l: 'DK 验真' },
    { i: 'coin', l: '签到返利' },
    { i: 'pin', l: '线下店' },
    { i: 'truck', l: '订单跟踪' },
    { i: 'globe', l: '多币种' },
    { i: 'star', l: '收藏夹' },
  ];

  return (
    <div className="appbody" style={{ background: '#FAFAFA' }}>
      {/* sticky header */}
      <div className="app-header" style={{ background: 'rgba(255,255,255,0.92)' }}>
        <div className="searchbar">
          <Icon name="search" size={14} stroke="var(--fg-3)" sw={2} />
          <span>搜索 “首尔短外套”</span>
        </div>
        <CurPill onClick={onCur} />
        <Icon name="bell" size={20} sw={1.5} stroke="var(--fg-1)" />
      </div>

      {/* announcement strip — 数据源: apmchat /api/site-announcements (多条轮播 4s) */}
      <div style={{ background: '#0A1634', color: '#fff', padding: '6px 14px', fontSize: 10, display: 'flex', alignItems: 'center', gap: 8, fontWeight: 500, position: 'relative' }}>
        <span className="nightband"><i />20:00–05:00 SEOUL</span>
        <span key={annIdx} style={{
          flex: 1, opacity: 0.85,
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          animation: 'ann-slide 0.4s var(--ease)',
        }}>📢 {annText}</span>
        {annItems.length > 1 && (
          <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', opacity: 0.4, letterSpacing: '0.04em' }}>
            {annIdx + 1}/{annItems.length}
          </span>
        )}
        {ann.source === 'live' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#3DDC84', opacity: 0.7, letterSpacing: '0.04em' }}>LIVE</span>}
        {ann.source === 'mock' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#FFD53D', opacity: 0.7, letterSpacing: '0.04em' }}>MOCK</span>}
      </div>

      {/* hero */}
      <div style={{ margin: '10px 14px 0', borderRadius: 14, overflow: 'hidden', position: 'relative', height: 180, background: '#1A1A1A url(assets/p2.jpg) center/cover' }}>
        <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.7) 100%)' }} />
        <div style={{ position: 'absolute', top: 12, left: 12, fontSize: 9, color: '#fff', letterSpacing: '0.12em', fontWeight: 700, fontFamily: 'var(--font-display)', padding: '3px 7px', background: 'rgba(255,255,255,0.16)', backdropFilter: 'blur(8px)', borderRadius: 2 }}>NEW SEASON · 26FW</div>
        <div style={{ position: 'absolute', bottom: 14, left: 14, right: 14, color: '#fff' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 22, lineHeight: 1.15, letterSpacing: '-0.02em' }}>首尔<br />街头女装</div>
          <div style={{ marginTop: 6, fontSize: 11, opacity: 0.85 }}>新天地直发 · 满 399 免邮</div>
        </div>
        <div style={{ position: 'absolute', bottom: 10, right: 12, display: 'flex', gap: 4 }}>
          {[0,1,2,3].map(i => (
            <span key={i} style={{ width: i === 0 ? 18 : 5, height: 3, background: i === 0 ? '#fff' : 'rgba(255,255,255,0.4)', borderRadius: 2 }} />
          ))}
        </div>
      </div>

      {/* category quicks */}
      <div style={{ padding: '14px 8px', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', rowGap: 14 }}>
        {cats.map(c => (
          <div key={c.l} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5, fontSize: 10, color: 'var(--fg-2)', fontWeight: 500 }}>
            <div style={{ width: 38, height: 38, borderRadius: 12, background: c.i === 'fire' ? 'var(--brand)' : c.i === 'live' ? '#0B0B0B' : c.i === 'qr' ? 'var(--apm-gold)' : 'var(--brand-soft)', display: 'grid', placeItems: 'center' }}>
              <Icon name={c.i} size={18} stroke={['fire','live','qr'].includes(c.i) ? '#fff' : 'var(--brand)'} sw={1.7} />
            </div>
            {c.l}
          </div>
        ))}
      </div>

      {/* offline stores strip */}
      <div style={{ margin: '4px 14px 14px', padding: 12, borderRadius: 12, background: 'linear-gradient(110deg, #FFE9D6, #FFF8F1)', display: 'flex', alignItems: 'center', gap: 10, position: 'relative', overflow: 'hidden' }}>
        <div style={{ width: 48, height: 48, borderRadius: 12, background: 'url(assets/sinsagom-1.jpg) center/cover', flexShrink: 0 }} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 12, fontWeight: 700 }}>新天地 · EKA · iAPM</div>
          <div style={{ fontSize: 10, color: 'var(--fg-3)', marginTop: 1 }}>上海 5 家 apM 实体店 · 扫码到店</div>
        </div>
        <Icon name="chev" size={14} stroke="var(--fg-2)" sw={2} />
      </div>

      {/* hot ranking pill */}
      <div style={{ padding: '0 14px', display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <Icon name="fire" size={16} stroke="var(--brand)" />
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 17, letterSpacing: '-0.01em' }}>HOT 100</span>
          <span style={{ fontSize: 10, color: 'var(--fg-3)' }}>今日热卖 · 8 大档口</span>
          {hotState.source === 'live' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#3DDC84', letterSpacing: '0.04em' }}>LIVE</span>}
          {hotState.source === 'mock' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#FFD53D', letterSpacing: '0.04em' }}>MOCK</span>}
        </div>
        <a href="#/cat" style={{ fontSize: 11, color: 'var(--fg-3)', display: 'flex', alignItems: 'center', gap: 2, textDecoration: 'none' }}>查看全部<Icon name="chev" size={11} sw={2} /></a>
      </div>

      {/* horizontal hot list — sortBy sold from real stall data */}
      <div style={{ display: 'flex', gap: 8, padding: '10px 14px', overflowX: 'auto', scrollbarWidth: 'none' }}>
        {hotItems.map((p, i) => (
          <a key={p.id} href={'#/pdp?id=' + p.id} style={{ flexShrink: 0, width: 110, background: '#fff', borderRadius: 10, overflow: 'hidden', border: '0.5px solid var(--border-1)', textDecoration: 'none', color: 'inherit', display: 'block' }}>
            <div style={{ position: 'relative' }}>
              <ProductImg idx={p.img} />
              <span style={{ position: 'absolute', top: 0, left: 0, width: 22, height: 22, background: i < 3 ? 'var(--brand)' : '#0B0B0B', color: '#fff', fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 11, display: 'grid', placeItems: 'center' }}>{i + 1}</span>
              {p.tags?.includes('DK') && <span className="chip dk" style={{ position: 'absolute', bottom: 4, right: 4, fontSize: 7, padding: '1px 3px' }}>DK</span>}
            </div>
            <div style={{ padding: '6px 8px 8px' }}>
              <div style={{ fontSize: 9, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginBottom: 2 }}>{p.stall.buildingname} {p.stall.layername}</div>
              <PriceBlock cny={p.priceCny} originCny={p.originCny} size={13} />
            </div>
          </a>
        ))}
      </div>

      {/* feed grid — 2 col, real products from multiple stalls */}
      <div style={{ padding: '6px 8px 14px' }}>
        <div style={{ padding: '0 6px 8px', fontSize: 13, fontWeight: 700, letterSpacing: '-0.01em', display: 'flex', alignItems: 'center', gap: 6 }}>
          为你推荐 <span style={{ fontSize: 10, fontWeight: 500, color: 'var(--fg-3)' }}>FOR YOU</span>
          {forYouState.source === 'live' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#3DDC84', letterSpacing: '0.04em' }}>LIVE</span>}
          {forYouState.source === 'mock' && <span style={{ fontSize: 8, fontFamily: 'var(--font-mono)', color: '#FFD53D', letterSpacing: '0.04em' }}>MOCK</span>}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
          {forYouItems.map(p => (
            <a key={p.id} href={'#/pdp?id=' + p.id} style={{ background: '#fff', borderRadius: 10, overflow: 'hidden', border: '0.5px solid var(--border-1)', textDecoration: 'none', color: 'inherit', display: 'block' }}>
              <div style={{ position: 'relative' }}>
                <ProductImg idx={p.img} />
                {p.tags?.includes('DK') && <span className="chip dk" style={{ position: 'absolute', bottom: 6, right: 6 }}>DK 认证</span>}
                {p.rating >= 4.8 && <span style={{ position: 'absolute', top: 6, left: 6, background: 'rgba(0,0,0,0.7)', color: '#fff', fontSize: 9, padding: '1px 5px', borderRadius: 2, fontWeight: 600 }}>★ {p.rating}</span>}
                <span style={{ position: 'absolute', top: 6, right: 6, width: 24, height: 24, borderRadius: '50%', background: 'rgba(255,255,255,0.85)', display: 'grid', placeItems: 'center' }}>
                  <Icon name="heart" size={13} sw={1.6} />
                </span>
              </div>
              <div style={{ padding: '8px 10px 10px' }}>
                <div style={{ fontSize: 11, lineHeight: 1.35, color: 'var(--fg-1)', height: 30, overflow: 'hidden' }}>{window.pickI18n(p.nameI18n, lang)}</div>
                <div style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 4, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>{p.stall.merchant} · {p.stall.buildingname}</div>
                <div style={{ marginTop: 6, display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
                  <PriceBlock cny={p.priceCny} originCny={p.originCny} size={14} />
                  <span style={{ fontSize: 9, color: 'var(--fg-3)' }}>已售 {p.sold.toLocaleString()}</span>
                </div>
              </div>
            </a>
          ))}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// 03 CATEGORY — left rail + right grid
// ============================================================
function CategoryScreen() {
  const cats = window.CATEGORIES;
  const { lang } = useC1(window.LangContext);
  const [activeCat, setActiveCat] = useS1('all');
  const state = window.useDateall('listProducts', { cat: activeCat, limit: 30 }, [activeCat]);
  const items = state.data?.items || [];
  const activeMeta = cats.find(c => c.id === activeCat) || cats[0];

  return (
    <div className="appbody" style={{ background: '#fff', display: 'flex', flexDirection: 'column' }}>
      <div className="app-header">
        <div className="searchbar"><Icon name="search" size={14} stroke="var(--fg-3)" sw={2} /><span>搜索分类、商家、单品</span></div>
        <Icon name="qr" size={20} sw={1.5} />
      </div>

      <div style={{ display: 'flex', flex: 1, minHeight: 0 }}>
        {/* left rail */}
        <div style={{ width: 78, background: '#F5F5F5', display: 'flex', flexDirection: 'column', overflowY: 'auto' }}>
          {cats.map(c => (
            <button key={c.id} onClick={() => setActiveCat(c.id)}
              style={{ position: 'relative', padding: '14px 6px', textAlign: 'center', background: c.id === activeCat ? '#fff' : 'transparent', borderLeft: c.id === activeCat ? '2px solid var(--brand)' : '2px solid transparent', border: 'none', cursor: 'pointer' }}>
              <div style={{ fontSize: 12, fontWeight: c.id === activeCat ? 700 : 500, color: c.id === activeCat ? 'var(--brand)' : 'var(--fg-2)' }}>{window.pickI18n(c.l, lang)}</div>
              <div style={{ fontSize: 8, color: 'var(--fg-4)', marginTop: 2, fontFamily: 'var(--font-mono)', letterSpacing: '0.06em' }}>{c.s}</div>
            </button>
          ))}
        </div>

        {/* right content */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '12px 12px 12px' }}>
          {/* sub-banner */}
          <div style={{ borderRadius: 10, padding: 12, background: 'linear-gradient(120deg, var(--brand), var(--brand-deep))', color: '#fff', marginBottom: 12, position: 'relative', overflow: 'hidden' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 16, lineHeight: 1.2 }}>{activeMeta.s}<br />SS·26 上新</div>
            <div style={{ fontSize: 10, opacity: 0.85, marginTop: 4 }}>{items.length} 款 · 来自 {new Set(items.map(p => p.stall.merchant)).size} 个档口</div>
            {state.source === 'live' && <span style={{ position: 'absolute', top: 8, right: 10, fontSize: 8, fontFamily: 'var(--font-mono)', color: '#FFD53D', letterSpacing: '0.04em' }}>LIVE</span>}
            {state.source === 'mock' && <span style={{ position: 'absolute', top: 8, right: 10, fontSize: 8, fontFamily: 'var(--font-mono)', color: '#FFE4EA', letterSpacing: '0.04em' }}>MOCK</span>}
          </div>

          {/* building filter chips - 用 BUILDINGS 常量 */}
          <div style={{ display: 'flex', gap: 6, marginBottom: 10, overflowX: 'auto', scrollbarWidth: 'none' }}>
            <span className="chip" style={{ flexShrink: 0, fontSize: 10, padding: '4px 10px' }}>全部档口</span>
            {window.BUILDINGS.map(b => (
              <span key={b.name} className="chip gray" style={{ flexShrink: 0, fontSize: 10, padding: '4px 10px' }}>{b.name}</span>
            ))}
          </div>

          <div style={{ fontSize: 12, fontWeight: 700, marginBottom: 8, display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
            <span>{window.pickI18n(activeMeta.l, lang)} · 全部档口</span>
            <span style={{ fontSize: 10, fontWeight: 500, color: 'var(--fg-3)', display: 'flex', alignItems: 'center', gap: 2 }}><Icon name="sort" size={11} sw={2} />综合</span>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 8 }}>
            {items.map(p => (
              <a key={p.id} href={'#/pdp?id=' + p.id} style={{ display: 'flex', gap: 10, background: '#fff', border: '0.5px solid var(--border-1)', borderRadius: 8, padding: 8, textDecoration: 'none', color: 'inherit' }}>
                <div style={{ width: 76, aspectRatio: 1, borderRadius: 6, background: `url(assets/p${p.img}.jpg) center/cover`, flexShrink: 0 }} />
                <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
                  <div style={{ fontSize: 12, fontWeight: 500, lineHeight: 1.3 }}>{window.pickI18n(p.nameI18n, lang)}</div>
                  <div style={{ fontSize: 9, color: 'var(--fg-3)', marginTop: 2, fontFamily: 'var(--font-mono)' }}>{p.stall.merchant} · {p.stall.buildingname} {p.stall.layername}</div>
                  <div style={{ display: 'flex', gap: 4, marginTop: 4, flexWrap: 'wrap' }}>
                    {p.tags?.includes('DK') && <span className="chip dk" style={{ fontSize: 8 }}>DK</span>}
                    {p.tags?.includes('NEW') && <span className="chip gray" style={{ fontSize: 8 }}>新品</span>}
                    {p.tags?.includes('HOT') && <span className="chip" style={{ fontSize: 8 }}>HOT</span>}
                    <span className="chip outline" style={{ fontSize: 8 }}>{p.code}</span>
                  </div>
                  <div style={{ marginTop: 'auto', display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
                    <PriceBlock cny={p.priceCny} originCny={p.originCny} size={14} />
                    <button style={{ width: 26, height: 26, borderRadius: '50%', background: 'var(--brand)', color: '#fff', border: 'none', display: 'grid', placeItems: 'center' }}><Icon name="plus" size={14} stroke="#fff" sw={2.2} /></button>
                  </div>
                </div>
              </a>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// 04 PRODUCT DETAIL
// ============================================================
function PDPScreen({ onAddCart }) {
  const { lang } = useC1(window.LangContext);
  // 从 URL hash query 读 id, 默认 p001
  const id = (() => {
    const h = window.location.hash || '';
    const m = h.match(/[?&]id=([^&]+)/);
    return m ? decodeURIComponent(m[1]) : 'p001';
  })();
  const state = window.useDateall('getProduct', id, [id]);
  const p = state.data?.item;

  if (!p) {
    return <div className="appbody" style={{ padding: 40, textAlign: 'center', color: '#999' }}>加载商品中…</div>;
  }
  const name = window.pickI18n(p.nameI18n, lang);

  return (
    <div className="appbody" style={{ background: '#FAFAFA', paddingBottom: 70, position: 'relative' }}>
      {/* image */}
      <div style={{ position: 'relative' }}>
        <ProductImg idx={p.img} ratio={1} />
        <a href="#/home" style={{ position: 'absolute', top: 32, left: 14, width: 32, height: 32, borderRadius: '50%', background: 'rgba(255,255,255,0.85)', display: 'grid', placeItems: 'center', backdropFilter: 'blur(10px)', textDecoration: 'none', color: 'inherit' }}>
          <Icon name="back" size={16} sw={2} />
        </a>
        <div style={{ position: 'absolute', top: 32, right: 80, display: 'flex', gap: 8 }}>
          <span style={{ width: 32, height: 32, borderRadius: '50%', background: 'rgba(255,255,255,0.85)', display: 'grid', placeItems: 'center' }}><Icon name="share" size={15} sw={2} /></span>
        </div>
        <div style={{ position: 'absolute', bottom: 12, right: 14, fontSize: 10, fontFamily: 'var(--font-mono)', background: 'rgba(0,0,0,0.65)', color: '#fff', padding: '3px 8px', borderRadius: 999 }}>1 / 8</div>
        <div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, height: 18, background: 'linear-gradient(180deg, transparent, #FAFAFA)' }} />
      </div>

      {/* price block */}
      <div style={{ background: '#fff', padding: '14px 14px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', gap: 8 }}>
          <PriceBlock cny={p.priceCny} originCny={p.originCny} size={26} />
          {p.originCny && <span className="chip" style={{ fontSize: 10 }}>限时 {Math.round((1 - p.priceCny / p.originCny) * 100)}% OFF</span>}
        </div>
        <div style={{ marginTop: 8, fontSize: 14, fontWeight: 600, lineHeight: 1.4 }}>{name}</div>
        <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
          {p.tags?.includes('DK') && <span className="chip dk">DK 一物一码</span>}
          <span className="chip gray">{p.stall.buildingname} 直发</span>
          <span className="chip gray">7天无理由</span>
          {p.platform === 'BOTH' && <span className="chip outline">APM + DDM 双平台</span>}
        </div>
        <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between', fontSize: 11, color: 'var(--fg-3)' }}>
          <span><b style={{ color: 'var(--fg-1)', fontWeight: 600 }}>已售 {p.sold.toLocaleString()}</b> · 商品码 {p.code}</span>
          <span style={{ display: 'flex', alignItems: 'center', gap: 3 }}><Icon name="star" size={12} stroke="#FFB800" /> <b style={{ color: 'var(--fg-1)' }}>{p.rating}</b></span>
        </div>
      </div>

      {/* fx lock notice */}
      <div style={{ margin: '8px 14px', background: '#FFF8E6', borderLeft: '3px solid var(--apm-gold)', borderRadius: 4, padding: '8px 10px', fontSize: 10, color: 'var(--fg-1)', display: 'flex', gap: 6 }}>
        <Icon name="globe" size={13} stroke="var(--apm-gold)" sw={1.8} />
        <div>下单瞬间锁定汇率 · 档口原价 ₩{p.priceKrw.toLocaleString()}</div>
      </div>

      {/* merchant - 真实档口信息 */}
      <div style={{ background: '#fff', margin: '8px 14px', borderRadius: 10, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 10 }}>
        <Sinsagom size={36} />
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 12, fontWeight: 600, display: 'flex', gap: 4, alignItems: 'center' }}>
            {p.stall.merchant} <span className="chip" style={{ fontSize: 8 }}>{p.stall.buildingname}</span>
          </div>
          <div style={{ fontSize: 9, color: 'var(--fg-3)', fontFamily: 'var(--font-mono)', marginTop: 1 }}>
            SEOUL · {p.stall.buildingname}-{p.stall.layername}-{p.stall.stallNo} · ☎ {p.stall.tel}
          </div>
        </div>
        <button className="btn ghost" style={{ padding: '6px 12px', fontSize: 11 }}>进店</button>
      </div>

      {/* spec rows */}
      <div style={{ background: '#fff', margin: '8px 14px', borderRadius: 10, padding: '4px 12px' }}>
        {[
          { k: '颜色', v: '奶杏 · 雾粉 · 烟灰', extra: '3 色' },
          { k: '尺码', v: 'S / M / L', extra: '已选 M' },
          { k: '送至', v: '上海市 浦东新区', extra: '约 4-6 天' },
          { k: '服务', v: '7天无理由 · DK 验真', extra: null },
        ].map((r, i, arr) => (
          <div key={r.k} style={{ display: 'flex', alignItems: 'center', padding: '11px 0', borderBottom: i < arr.length - 1 ? '0.5px solid var(--border-1)' : 'none' }}>
            <span style={{ fontSize: 11, color: 'var(--fg-3)', width: 40, flexShrink: 0 }}>{r.k}</span>
            <span style={{ fontSize: 12, flex: 1 }}>{r.v}</span>
            {r.extra && <span style={{ fontSize: 10, color: 'var(--fg-3)', marginRight: 4 }}>{r.extra}</span>}
            <Icon name="chev" size={12} sw={1.8} stroke="var(--fg-3)" />
          </div>
        ))}
      </div>

      {/* DK seal callout - 仅 DK 商品显示 */}
      {p.tags?.includes('DK') && (
        <div style={{ margin: '8px 14px', background: '#0B0B0B', color: '#fff', borderRadius: 10, padding: 14, display: 'flex', gap: 10 }}>
          <div style={{ width: 48, height: 48, borderRadius: '50%', border: '1.5px solid var(--apm-gold)', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
            <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 13, color: 'var(--apm-gold)' }}>DK</span>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--apm-gold)' }}>DK · 一物一码 · 已激活</div>
            <div style={{ fontSize: 10, opacity: 0.7, marginTop: 4, fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>DK-{p.code}</div>
            <div style={{ fontSize: 10, opacity: 0.7, marginTop: 2 }}>{p.stall.buildingname} {p.stall.layername} · 真品溯源</div>
          </div>
        </div>
      )}

      {/* tabs */}
      <div style={{ display: 'flex', justifyContent: 'space-around', background: '#fff', margin: '8px 14px 0', borderRadius: 10, padding: '2px 0' }}>
        {[['商品详情', true], ['尺码表', false], ['评价 1.2k', false], ['搭配', false]].map(([t, a]) => (
          <div key={t} style={{ padding: '10px 4px', fontSize: 12, fontWeight: a ? 700 : 500, color: a ? 'var(--brand)' : 'var(--fg-2)', position: 'relative' }}>
            {t}
            {a && <span style={{ position: 'absolute', bottom: 4, left: '50%', transform: 'translateX(-50%)', width: 18, height: 2, background: 'var(--brand)', borderRadius: 2 }} />}
          </div>
        ))}
      </div>

      <div style={{ background: '#fff', margin: '0 14px', borderRadius: '0 0 10px 10px', padding: 14, fontSize: 11, lineHeight: 1.7, color: 'var(--fg-2)' }}>
        <p>• 面料 65%真丝 + 35%粘胶 · 韩国进口</p>
        <p>• 模特身高 168 / 体重 50kg · 上身 M</p>
        <p>• 工厂 7,000+ 生产线之一 · 月产 53k SKU</p>
      </div>

      {/* sticky CTA */}
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: 60, background: '#fff', borderTop: '0.5px solid var(--border-1)', display: 'grid', gridTemplateColumns: '40px 40px 40px 1fr 1fr', alignItems: 'center', padding: '0 12px', gap: 8 }}>
        <a href="#/home" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 9, color: 'var(--fg-2)', textDecoration: 'none' }}><Icon name="home" size={18} sw={1.6} />首页</a>
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 9, color: 'var(--fg-2)' }}><Icon name="chat" size={18} sw={1.6} />客服</div>
        <a href="#/cart" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', fontSize: 9, color: 'var(--fg-2)', textDecoration: 'none' }}><Icon name="cart" size={18} sw={1.6} />购物车</a>
        <button className="btn" style={{ background: '#FFE4EA', color: 'var(--brand)', height: 38, fontSize: 12, fontWeight: 700 }} onClick={() => { window.cartStore.add(p.id, 1); window.location.hash = '#/cart'; }}>加入购物车</button>
        <button className="btn primary" style={{ height: 38, fontSize: 12, fontWeight: 700 }} onClick={() => { window.cartStore.add(p.id, 1); window.location.hash = '#/checkout'; }}>立即购买</button>
      </div>
    </div>
  );
}

Object.assign(window, { LoginScreen, HomeScreen, CategoryScreen, PDPScreen });
