// profile.jsx — 个人中心 (/profile)

const _profileToken = () => window.YiteAPI?.getToken?.() || localStorage.getItem('token') || '';
const _profileHeaders = () => ({ Authorization: 'Bearer ' + _profileToken() });
const _pAPI = {
  profile: () => fetch('/api/v1/user/profile', { headers: _profileHeaders() }).then(r => r.json()),
  updateProfile: body => fetch('/api/v1/user/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', ..._profileHeaders() }, body: JSON.stringify(body) }).then(r => r.json()),
  uploadAvatar: file => { const fd = new FormData(); fd.append('file', file); return fetch('/api/v1/user/avatar', { method: 'POST', headers: _profileHeaders(), body: fd }).then(r => r.json()); },
  changePassword: body => fetch('/api/v1/user/password', { method: 'PUT', headers: { 'Content-Type': 'application/json', ..._profileHeaders() }, body: JSON.stringify(body) }).then(r => r.json()),
  storage: () => fetch('/api/v1/user/storage', { headers: _profileHeaders() }).then(r => r.json()),
  stats: () => fetch('/api/v1/user/stats', { headers: _profileHeaders() }).then(r => r.json()),
  signin: () => fetch('/api/v1/user/signin/history', { headers: _profileHeaders() }).then(r => r.json()),
  invite: () => fetch('/api/v1/user/invite', { headers: _profileHeaders() }).then(r => r.json()),
  myTemplates: () => fetch('/api/v1/templates/mine', { headers: _profileHeaders() }).then(r => r.json()),
  coinRecords: (page = 1) => fetch('/api/v1/user/coin-records?page=' + page + '&page_size=20', { headers: _profileHeaders() }).then(r => r.json()),
  orderRecords: (page = 1) => fetch('/api/v1/user/order-records?page=' + page + '&page_size=20', { headers: _profileHeaders() }).then(r => r.json()),
  subscriptions: () => fetch('/api/v1/subscriptions/mine', { headers: _profileHeaders() }).then(r => r.json()),
  followers: id => fetch('/api/v1/users/' + id + '/profile').then(r => r.json()),
};

const _fmtBytes = b => { if (b >= 1073741824) return (b / 1073741824).toFixed(1) + ' GB'; if (b >= 1048576) return (b / 1048576).toFixed(0) + ' MB'; return (b / 1024).toFixed(0) + ' KB'; };
const _fmtCoins = n => n >= 10000 ? (n / 1000).toFixed(1) + 'K' : String(n || 0);
const _relTime = iso => { if (!iso) return ''; const d = (Date.now() - new Date(iso).getTime()) / 1000; if (d < 60) return window.t('time.justNow'); if (d < 3600) return Math.floor(d / 60) + window.t('time.minutesAgo'); if (d < 86400) return Math.floor(d / 3600) + window.t('time.hoursAgo'); return new Date(iso).toLocaleDateString(); };

// ─── Storage card ────────────────────────────────────────────────────────────
const StorageCard = ({ data, isMobile }) => {
  const { t } = useI18n();
  if (!data) return null;
  const pct = Math.min(100, Math.round(data.used_bytes / Math.max(data.total_limit, 1) * 100));
  return (
    <div style={{ flex: isMobile ? 'none' : 1, width: isMobile ? '100%' : undefined, minWidth: isMobile ? 0 : 200, padding: '20px 22px', borderRadius: 12, border: '1px solid var(--line)', background: 'rgba(255,255,255,0.02)' }}>
      <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 10 }}>{t('profile.storage')}</div>
      <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--fg-0)', marginBottom: 8 }}>{_fmtBytes(data.used_bytes)} <span style={{ fontSize: 13, fontWeight: 400, color: 'var(--fg-3)' }}>/ {_fmtBytes(data.total_limit)}</span></div>
      <div style={{ height: 6, borderRadius: 3, background: 'var(--bg-3)', overflow: 'hidden', marginBottom: 10 }}>
        <div style={{ height: '100%', width: pct + '%', borderRadius: 3, background: pct > 80 ? '#f87171' : 'var(--accent)', transition: 'width .3s' }} />
      </div>
      <div style={{ display: 'flex', gap: 16, fontSize: 12, color: 'var(--fg-3)' }}>
        <span>{t('profile.images')} {_fmtBytes(data.image_bytes || 0)}</span>
        <span>{t('profile.videos')} {_fmtBytes(data.video_bytes || 0)}</span>
        <span>{data.file_count || 0} {t('profile.files')}</span>
      </div>
    </div>
  );
};

// ─── Stats card ──────────────────────────────────────────────────────────────
const StatsCard = ({ data, isMobile }) => {
  const { t } = useI18n();
  if (!data) return null;
  return (
    <div style={{ flex: isMobile ? 'none' : 1, width: isMobile ? '100%' : undefined, minWidth: isMobile ? 0 : 200, padding: '20px 22px', borderRadius: 12, border: '1px solid var(--line)', background: 'rgba(255,255,255,0.02)' }}>
      <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 10 }}>{t('profile.stats')}</div>
      <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--fg-0)', marginBottom: 8 }}>{data.total_count || 0} <span style={{ fontSize: 13, fontWeight: 400, color: 'var(--fg-3)' }}>{t('profile.totalCreations')}</span></div>
      <div style={{ display: 'flex', gap: 16, fontSize: 12, color: 'var(--fg-3)' }}>
        <span>{t('profile.images')} {data.image_count || 0}</span>
        <span>{t('profile.videos')} {data.video_count || 0}</span>
        <span>{t('profile.templates')} {data.template_count || 0}</span>
      </div>
    </div>
  );
};

// ─── Sign-in card ────────────────────────────────────────────────────────────
const SignInCard = ({ data, onSignIn, isMobile }) => {
  const { t } = useI18n();
  if (!data) return null;
  const config = data.gift_coins_config || [20, 25, 30, 35, 40, 45, 50];
  const signed = new Set(data.signed_dates || []);
  const today = data.today_date || new Date().toISOString().slice(0, 10);
  const cycleStart = data.cycle_start_date || today;
  const days = [];
  for (let i = 0; i < 7; i++) {
    const d = new Date(cycleStart); d.setDate(d.getDate() + i);
    const ds = d.toISOString().slice(0, 10);
    days.push({ date: ds, signed: signed.has(ds), isToday: ds === today, coins: config[i] || 20 });
  }
  return (
    <div style={{ flex: isMobile ? 'none' : 1, width: isMobile ? '100%' : undefined, minWidth: isMobile ? 0 : 200, padding: '20px 22px', borderRadius: 12, border: '1px solid var(--line)', background: 'rgba(255,255,255,0.02)' }}>
      <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 10 }}>{t('profile.dailySignIn')}</div>
      <div style={{ display: 'flex', gap: 6, marginBottom: 10 }}>
        {days.map((d, i) => (
          <div key={i} onClick={() => d.isToday && !d.signed && onSignIn()} style={{
            flex: 1, textAlign: 'center', padding: '8px 0', borderRadius: 6, cursor: d.isToday && !d.signed ? 'pointer' : 'default',
            background: d.signed ? 'rgba(74,222,128,0.12)' : d.isToday ? 'rgba(34,211,238,0.08)' : 'transparent',
            border: d.isToday ? '1px solid rgba(34,211,238,0.3)' : '1px solid transparent',
          }}>
            <div style={{ fontSize: 11, color: d.signed ? '#4ade80' : 'var(--fg-3)', fontWeight: 600 }}>D{i + 1}</div>
            <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>+{d.coins}</div>
            {d.signed && <div style={{ fontSize: 11, color: '#4ade80' }}>✓</div>}
          </div>
        ))}
      </div>
      {!data.today_signed && <div style={{ fontSize: 11, color: 'var(--accent)', textAlign: 'center' }}>{t('profile.clickSignIn')}</div>}
      {data.today_signed && <div style={{ fontSize: 11, color: '#4ade80', textAlign: 'center' }}>{t('profile.alreadySignedIn')}</div>}
    </div>
  );
};

// ─── Invite card ─────────────────────────────────────────────────────────────
const InviteCard = ({ data, isMobile }) => {
  const { t } = useI18n();
  if (!data) return null;
  const [copied, setCopied] = React.useState(false);
  const link = window.location.origin + data.invite_link;
  const handleCopy = () => { navigator.clipboard.writeText(link).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); };
  return (
    <div style={{ flex: isMobile ? 'none' : 1, width: isMobile ? '100%' : undefined, minWidth: isMobile ? 0 : 200, padding: '20px 22px', borderRadius: 12, border: '1px solid var(--line)', background: 'rgba(255,255,255,0.02)' }}>
      <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 10 }}>{t('profile.inviteFriends')}</div>
      <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--fg-0)', marginBottom: 8 }}>{data.invited_count || 0} <span style={{ fontSize: 13, fontWeight: 400, color: 'var(--fg-3)' }}>{t('profile.invited')}</span></div>
      <button onClick={handleCopy} style={{ width: '100%', height: 36, borderRadius: 8, border: '1px solid var(--line-2)', background: copied ? 'rgba(74,222,128,0.08)' : 'rgba(255,255,255,0.04)', color: copied ? '#4ade80' : 'var(--fg-1)', fontSize: 13, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
        <Icon name={copied ? 'check' : 'link'} size={13} color={copied ? '#4ade80' : 'var(--fg-3)'} />
        {copied ? t('common.copied') : t('profile.copyInviteLink')}
      </button>
    </div>
  );
};

// ─── Account settings modal ──────────────────────────────────────────────────
// Password input with show/hide toggle (touch target ≥44px)
const PasswordInput = ({ value, onChange, show, onToggleShow, placeholder, autoComplete }) => (
  <div style={{ position: 'relative' }}>
    <input type={show ? 'text' : 'password'} value={value} onChange={e => onChange(e.target.value)}
      placeholder={placeholder} autoComplete={autoComplete}
      className="yt-input"
      style={{ width: '100%', height: 44, fontSize: 14, paddingRight: 48 }} />
    <button type="button" onClick={onToggleShow} aria-label={show ? '隐藏密码' : '显示密码'}
      style={{
        position: 'absolute', right: 4, top: 4, width: 36, height: 36, borderRadius: 8,
        border: 'none', background: 'transparent', color: 'var(--fg-3)', cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
      {show ? (
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/>
          <line x1="1" y1="1" x2="23" y2="23"/>
        </svg>
      ) : (
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
          <circle cx="12" cy="12" r="3"/>
        </svg>
      )}
    </button>
  </div>
);

const AccountSettingsModal = ({ profile, onClose, onSaved, isMobile }) => {
  const { t } = useI18n();
  const [nickname, setNickname] = React.useState(profile?.nickname || '');
  const [bio, setBio] = React.useState(profile?.bio || '');
  const [oldPwd, setOldPwd] = React.useState('');
  const [newPwd, setNewPwd] = React.useState('');
  const [showOldPwd, setShowOldPwd] = React.useState(false);
  const [showNewPwd, setShowNewPwd] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  const [msgOk, setMsgOk] = React.useState(false);
  const avatarRef = React.useRef(null);

  // ESC key dismiss + body scroll lock
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      window.removeEventListener('keydown', onKey);
      document.body.style.overflow = prevOverflow;
    };
  }, [onClose]);

  const handleSaveProfile = async () => {
    setSaving(true); setMsg('');
    const res = await _pAPI.updateProfile({ nickname: nickname.trim(), bio: bio.trim() });
    if (res.error) { setMsg(res.error); setMsgOk(false); } else { setMsg(t('profile.savingSuccess')); setMsgOk(true); onSaved(res); }
    setSaving(false);
  };
  const handleUploadAvatar = async e => {
    const f = e.target.files?.[0]; e.target.value = '';
    if (!f) return;
    setSaving(true);
    const res = await _pAPI.uploadAvatar(f);
    if (res.error) { setMsg(res.error); setMsgOk(false); } else { setMsg(t('profile.avatarUpdated')); setMsgOk(true); onSaved(res); }
    setSaving(false);
  };
  const handleChangePwd = async () => {
    if (!oldPwd || !newPwd) { setMsg(t('profile.passwordRequired')); setMsgOk(false); return; }
    setSaving(true); setMsg('');
    const res = await _pAPI.changePassword({ old_password: oldPwd, new_password: newPwd });
    if (res.error) { setMsg(res.error); setMsgOk(false); } else { setMsg(t('profile.passwordChanged')); setMsgOk(true); setOldPwd(''); setNewPwd(''); }
    setSaving(false);
  };

  return ReactDOM.createPortal(
    <div style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 200,
      padding: isMobile ? '12px' : '24px',
      boxSizing: 'border-box',
    }}
      onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{
        width: '100%', maxWidth: isMobile ? '100%' : 500,
        maxHeight: '100%',
        background: '#141722', border: '1px solid var(--line-2)', borderRadius: 16,
        display: 'flex', flexDirection: 'column', boxShadow: '0 24px 80px rgba(0,0,0,0.7)',
        overflow: 'hidden',
      }}>
        <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexShrink: 0 }}>
          <span style={{ fontSize: 17, fontWeight: 700, color: 'var(--fg-0)' }}>{t('profile.settings')}</span>
          <button onClick={onClose} aria-label="关闭设置" style={{
            width: 36, height: 36, borderRadius: 8, border: 'none',
            background: 'rgba(255,255,255,0.04)', color: 'var(--fg-2)', cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            transition: 'background .12s',
          }}
            onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.08)'}
            onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.04)'}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '20px 22px', display: 'flex', flexDirection: 'column', gap: 20 }}>
          {/* Avatar */}
          <div>
            <div style={{ fontSize: 14, color: 'var(--fg-0)', fontWeight: 500, marginBottom: 10 }}>{t('profile.avatar')}</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
              <div style={{ width: 64, height: 64, borderRadius: '50%', overflow: 'hidden', background: 'linear-gradient(135deg,#a78bfa,#22d3ee)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 22, fontWeight: 700, color: '#0a0e1a' }}>
                {(profile?.avatar || profile?.avatar_oss_key) ? <img src={profile.avatar || profile.avatar_oss_key} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : (profile?.nickname || '?')[0].toUpperCase()}
              </div>
              <button onClick={() => avatarRef.current?.click()} style={{ height: 40, padding: '0 18px', borderRadius: 8, border: '1px solid var(--line-2)', background: 'rgba(255,255,255,0.04)', color: 'var(--fg-1)', fontSize: 13, cursor: 'pointer' }}>{t('profile.changeAvatar')}</button>
              <input ref={avatarRef} type="file" accept="image/*" style={{ display: 'none' }} onChange={handleUploadAvatar} />
            </div>
          </div>
          {/* Nickname */}
          <div>
            <label style={{ display: 'block', fontSize: 14, color: 'var(--fg-0)', fontWeight: 500, marginBottom: 8 }}>
              {t('profile.nickname')} <span style={{ color: '#f87171' }} aria-hidden="true">*</span>
            </label>
            <input value={nickname} onChange={e => setNickname(e.target.value)}
              autoComplete="nickname" required
              className="yt-input" style={{ width: '100%', height: 44, fontSize: 14 }} />
          </div>
          {/* Bio */}
          <div>
            <div style={{ fontSize: 14, color: 'var(--fg-0)', fontWeight: 500, marginBottom: 8 }}>{t('profile.bio')}</div>
            <textarea value={bio} onChange={e => setBio(e.target.value)} placeholder={t('profile.bioPlaceholder')} style={{ width: '100%', minHeight: 80, resize: 'vertical', background: 'rgba(255,255,255,0.03)', border: '1px solid var(--line-2)', borderRadius: 10, padding: 12, fontSize: 14, color: 'var(--fg-0)', fontFamily: 'var(--font-sans)', outline: 'none' }} />
          </div>
          <button onClick={handleSaveProfile} disabled={saving} style={{
            height: 44, borderRadius: 10, border: 'none',
            background: 'var(--accent)', color: '#052028',
            fontSize: 14, fontWeight: 600, cursor: 'pointer',
            opacity: saving ? 0.6 : 1,
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
            transition: 'opacity .12s, background .12s',
          }}>
            {saving && <span className="yt-spinner" style={{ width: 14, height: 14, borderWidth: 2 }} />}
            {t('profile.saveProfile')}
          </button>
          {/* Password */}
          {profile?.auth_type === 'email' && (
            <div style={{ borderTop: '1px solid var(--line)', paddingTop: 20 }}>
              <div style={{ fontSize: 14, color: 'var(--fg-0)', fontWeight: 500, marginBottom: 10 }}>{t('profile.changePassword')}</div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                <PasswordInput value={oldPwd} onChange={setOldPwd} show={showOldPwd} onToggleShow={() => setShowOldPwd(v => !v)}
                  placeholder={t('profile.currentPassword')} autoComplete="current-password" />
                <PasswordInput value={newPwd} onChange={setNewPwd} show={showNewPwd} onToggleShow={() => setShowNewPwd(v => !v)}
                  placeholder={t('profile.newPassword')} autoComplete="new-password" />
                <button onClick={handleChangePwd} disabled={saving} style={{ height: 44, borderRadius: 10, border: '1px solid var(--line-2)', background: 'rgba(255,255,255,0.04)', color: 'var(--fg-1)', fontSize: 13, cursor: 'pointer', opacity: saving ? 0.5 : 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{t('profile.changePassword')}</button>
              </div>
            </div>
          )}
          {/* Account info */}
          <div style={{ borderTop: '1px solid var(--line)', paddingTop: 16 }}>
            <div style={{ fontSize: 13, color: 'var(--fg-3)', display: 'flex', alignItems: 'center', gap: 8 }}>
              <Icon name={profile?.auth_type === 'gmail' ? 'google' : 'mail'} size={14} />
              {profile?.email} · {profile?.auth_type === 'gmail' ? t('profile.googleLogin') : t('profile.emailRegistration')}
            </div>
          </div>
          {msg && <div style={{ padding: '8px 12px', borderRadius: 8, background: msgOk ? 'rgba(74,222,128,0.1)' : 'rgba(248,113,113,0.1)', fontSize: 13, color: msgOk ? '#4ade80' : '#fca5a5' }}>{msg}</div>}

          {/* ── 偏好设置 ── */}
          <div style={{ borderTop: '1px solid var(--line)', paddingTop: 18 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-2)', marginBottom: 12, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{t('profile.preferences')}</div>
            {/* Language switcher */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 0' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="var(--fg-2)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                  <circle cx="12" cy="12" r="9"/>
                  <path d="M3.6 9h16.8M3.6 15h16.8M12 3c-2.5 3-4 5.8-4 9s1.5 6 4 9M12 3c2.5 3 4 5.8 4 9s-1.5 6-4 9"/>
                </svg>
                <span style={{ fontSize: 14, color: 'var(--fg-0)' }}>{t('profile.language')}</span>
              </div>
              <div role="radiogroup" aria-label="语言选择" style={{ display: 'flex', gap: 4, padding: 4, background: 'rgba(255,255,255,0.04)', border: '1px solid var(--line-2)', borderRadius: 10 }}>
                {[{id: 'en', label: 'EN'}, {id: 'zh', label: '中文'}].map(opt => {
                  const active = (window.i18n?.getLang?.() || 'en') === opt.id;
                  return (
                    <button key={opt.id} onClick={() => window.i18n?.setLang?.(opt.id)}
                      role="radio" aria-checked={active} aria-label={opt.label === 'EN' ? 'English' : '中文'}
                      style={{
                        height: 36, padding: '0 16px', minWidth: 56, borderRadius: 7, border: 'none', cursor: 'pointer',
                        background: active ? 'var(--accent)' : 'transparent',
                        color: active ? '#052028' : 'var(--fg-2)',
                        fontSize: 13, fontWeight: 600, transition: 'all .12s',
                      }}>{opt.label}</button>
                  );
                })}
              </div>
            </div>
          </div>

          {/* ── 法律条款 ── */}
          <div style={{ borderTop: '1px solid var(--line)', paddingTop: 18 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--fg-2)', marginBottom: 12, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{t('profile.legal')}</div>
            {[
              { href: '/privacy', label: t('profile.privacy'), sub: t('profile.privacySub') },
              { href: '/terms',   label: t('profile.terms'),   sub: t('profile.termsSub') },
              { href: '/refund',  label: t('profile.refund'),  sub: t('profile.refundSub') },
            ].map((p, i) => (
              <a key={p.href} href={p.href} target="_blank" rel="noopener noreferrer" style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                padding: '12px 0',
                borderBottom: i < 2 ? '1px solid var(--line)' : 'none',
                textDecoration: 'none', color: 'inherit',
              }}>
                <div>
                  <div style={{ fontSize: 14, color: 'var(--fg-0)' }}>{p.label}</div>
                  <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 2 }}>{p.sub}</div>
                </div>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--fg-3)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                  <polyline points="15 3 21 3 21 9"/>
                  <line x1="10" y1="14" x2="21" y2="3"/>
                </svg>
              </a>
            ))}
          </div>

          {/* ── 退出（销毁性操作 — 二次确认）── */}
          <button onClick={async () => {
            const ok = await ytConfirm(t('profile.logoutConfirm'), { danger: true });
            if (!ok) return;
            if (window.YiteAPI) { window.YiteAPI.setToken(''); window.YiteAPI.setUser(null); }
            localStorage.removeItem('token');
            window.location.href = '/login';
          }} style={{
            width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
            padding: '12px 14px', marginTop: 4, borderRadius: 10, border: 'none',
            background: 'rgba(248,113,113,0.08)', color: '#f87171',
            fontSize: 14, fontWeight: 500, cursor: 'pointer',
          }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
              <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
              <polyline points="16 17 21 12 16 7"/>
              <line x1="21" y1="12" x2="9" y2="12"/>
            </svg>
            {t('shared.menu.logout') || '退出登录'}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
};

// ─── Status badge helpers ────────────────────────────────────────────────────
const ORDER_STATUS_STYLE = {
  pending:  { bg: 'rgba(251,191,36,0.12)', color: '#fbbf24', labelKey: 'billing.status.pending' },
  success:  { bg: 'rgba(74,222,128,0.12)', color: '#4ade80', labelKey: 'billing.status.success' },
  failed:   { bg: 'rgba(248,113,113,0.12)', color: '#f87171', labelKey: 'billing.status.failed' },
  abnormal: { bg: 'rgba(167,139,250,0.12)', color: '#a78bfa', labelKey: 'billing.status.abnormal' },
};
const SUB_STATUS_STYLE = {
  active:    { bg: 'rgba(74,222,128,0.12)', color: '#4ade80', labelKey: 'billing.status.active' },
  expired:   { bg: 'rgba(180,180,180,0.12)', color: 'var(--fg-3)', labelKey: 'billing.status.expired' },
  cancelled: { bg: 'rgba(248,113,113,0.12)', color: '#f87171', labelKey: 'billing.status.cancelled' },
};
const StatusBadge = ({ st, label }) => {
  const { t } = useI18n();
  if (!st) return null;
  return <span style={{ fontSize: 10, fontWeight: 600, padding: '2px 7px', borderRadius: 4, background: st.bg, color: st.color, whiteSpace: 'nowrap' }}>{label || t(st.labelKey)}</span>;
};
const _fmtDate = iso => { if (!iso) return ''; const d = new Date(iso); return d.toLocaleString('en-CA', { hour12: false }).slice(0, 16).replace('T', ' '); };
const _fmtMoney = (a, c) => '$' + Number(a || 0).toFixed(2) + (c && c !== 'USD' ? ' ' + c : '');

// ─── Order record row（订单 / 一次性充值 / 订阅创建订单）────────────────────
const OrderRecordRow = ({ r }) => {
  const { t } = useI18n();
  const st = ORDER_STATUS_STYLE[r.status] || { bg: 'rgba(180,180,180,0.1)', color: 'var(--fg-3)', labelKey: null };
  const kindLabel = r.order_kind === 'coin_recharge' ? t('billing.coinRecharge') : (r.plan_name || t('billing.subOrder'));
  const isCoin = r.order_kind === 'coin_recharge';
  return (
    <div style={{ display: 'flex', alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--line)', gap: 14 }}>
      <div style={{ width: 32, height: 32, borderRadius: 6, background: isCoin ? 'rgba(34,211,238,0.1)' : 'rgba(167,139,250,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <Icon name={isCoin ? 'coin' : 'star'} size={14} color={isCoin ? 'var(--accent)' : 'var(--accent-2)'} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 3 }}>
          <span style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{kindLabel}</span>
          <StatusBadge st={st} />
        </div>
        <div style={{ fontSize: 11, color: 'var(--fg-3)', display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <span>{_fmtDate(r.created_at)}</span>
          {r.provider_type && <span>· {r.provider_type}</span>}
          {r.coins_granted > 0 && <span>· +{r.coins_granted} {t('profile.coins')}</span>}
        </div>
      </div>
      <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--fg-0)', fontVariantNumeric: 'tabular-nums', flexShrink: 0 }}>
        {_fmtMoney(r.amount, r.currency)}
      </div>
    </div>
  );
};

// ─── Subscription row ────────────────────────────────────────────────────────
const SubscriptionRow = ({ s }) => {
  const { t } = useI18n();
  const st = SUB_STATUS_STYLE[s.status] || { bg: 'rgba(180,180,180,0.1)', color: 'var(--fg-3)', labelKey: null };
  return (
    <div style={{ display: 'flex', alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--line)', gap: 14 }}>
      <div style={{ width: 32, height: 32, borderRadius: 6, background: 'rgba(34,211,238,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <Icon name="check" size={14} color="var(--accent)" />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 3 }}>
          <span style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500 }}>{s.plan_type ? s.plan_type.toUpperCase() : t('dashboard.plan')} · {s.duration_days}d</span>
          <StatusBadge st={st} />
        </div>
        <div style={{ fontSize: 11, color: 'var(--fg-3)', display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <span>{t('billing.purchased')} {_fmtDate(s.purchased_at)}</span>
          <span>· {t('billing.expires')} {_fmtDate(s.expires_at)}</span>
          {s.payment_provider && <span>· {s.payment_provider}</span>}
        </div>
      </div>
      <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--fg-0)', fontVariantNumeric: 'tabular-nums', flexShrink: 0 }}>
        {_fmtMoney(s.paid_amount, s.currency)}
      </div>
    </div>
  );
};

// ─── Coin record row ─────────────────────────────────────────────────────────
const COIN_SCENE_LABEL = {
  recharge: 'billing.scene.recharge',
  subscription_bonus: 'billing.scene.subscription_bonus',
  unlock_script: 'billing.scene.unlock_script',
  admin_grant: 'billing.scene.admin_grant',
  custom: 'billing.scene.custom',
};
const CoinRecordRow = ({ r }) => {
  const { t } = useI18n();
  const isPositive = r.amount > 0;
  const label = r.remark || (COIN_SCENE_LABEL[r.scene] ? t(COIN_SCENE_LABEL[r.scene]) : null) || r.scene || t('profile.coinChange');
  return (
    <div style={{ display: 'flex', alignItems: 'center', padding: '12px 0', borderBottom: '1px solid var(--line)', gap: 14 }}>
      <div style={{ width: 32, height: 32, borderRadius: 6, background: isPositive ? 'rgba(74,222,128,0.1)' : 'rgba(248,113,113,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <Icon name="coin" size={14} color={isPositive ? '#4ade80' : '#f87171'} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, color: 'var(--fg-0)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{label}</div>
        <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{_fmtDate(r.created_at)} {r.balance_after != null ? '· ' + t('billing.balance') + ' ' + r.balance_after : ''}</div>
      </div>
      <div style={{ fontSize: 14, fontWeight: 600, color: isPositive ? '#4ade80' : '#f87171', fontVariantNumeric: 'tabular-nums', flexShrink: 0 }}>
        {isPositive ? '+' : ''}{r.amount}
      </div>
    </div>
  );
};

// ─── Main screen ──────────────────────────────────────────────────────────────
const DashboardScreen = () => {
  const { t } = useI18n();
  const { isMobile } = useBreakpoint();
  const [profile, setProfile] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [storage, setStorage] = React.useState(null);
  const [stats, setStats] = React.useState(null);
  const [signin, setSignin] = React.useState(null);
  const [invite, setInvite] = React.useState(null);
  const [tab, setTab] = React.useState('works');
  const [recordTab, setRecordTab] = React.useState('subscriptions'); // subscriptions | orders | coins
  const [works, setWorks] = React.useState([]);
  const [coinRecords, setCoinRecords] = React.useState([]);
  const [orderRecords, setOrderRecords] = React.useState([]);
  const [subscriptions, setSubscriptions] = React.useState([]);
  const [recordsLoaded, setRecordsLoaded] = React.useState({ subscriptions: false, orders: false, coins: false });
  const [showSettings, setShowSettings] = React.useState(false);
  const [followers, setFollowers] = React.useState(0);
  const [following, setFollowing] = React.useState(0);
  const [editingNick, setEditingNick] = React.useState(false);
  const [nickInput, setNickInput] = React.useState('');

  React.useEffect(() => {
    if (!window.YiteAPI?.isLoggedIn?.()) { showLoginModal().then(ok => { if (ok) window.location.reload(); }); return; }
    // Check for #settings hash
    if (window.location.hash === '#settings') setShowSettings(true);
    // Load all data
    _pAPI.profile().then(d => { setProfile(d); setLoading(false); window.YiteAPI?.setUser?.(d); }).catch(() => setLoading(false));
    _pAPI.storage().then(d => setStorage(d)).catch(() => {});
    _pAPI.stats().then(d => setStats(d)).catch(() => {});
    _pAPI.signin().then(d => setSignin(d)).catch(() => {});
    _pAPI.invite().then(d => setInvite(d)).catch(() => {});
    _pAPI.myTemplates().then(d => setWorks(d.items || [])).catch(() => {});
  }, []);

  // Lazy-load records when records tab is opened
  React.useEffect(() => {
    if (tab !== 'records') return;
    if (recordTab === 'subscriptions' && !recordsLoaded.subscriptions) {
      _pAPI.subscriptions().then(d => setSubscriptions(d.items || [])).catch(() => {});
      setRecordsLoaded(s => ({ ...s, subscriptions: true }));
    } else if (recordTab === 'orders' && !recordsLoaded.orders) {
      _pAPI.orderRecords().then(d => setOrderRecords(d.items || [])).catch(() => {});
      setRecordsLoaded(s => ({ ...s, orders: true }));
    } else if (recordTab === 'coins' && !recordsLoaded.coins) {
      _pAPI.coinRecords().then(d => setCoinRecords(d.items || [])).catch(() => {});
      setRecordsLoaded(s => ({ ...s, coins: true }));
    }
  }, [tab, recordTab]);

  // Load follower counts
  React.useEffect(() => {
    if (!profile?.id) return;
    _pAPI.followers(profile.id).then(d => { setFollowers(d.followers || 0); setFollowing(d.following || 0); }).catch(() => {});
  }, [profile?.id]);

  const handleSignIn = async () => {
    const d = await _pAPI.signin();
    setSignin(d);
    _pAPI.profile().then(p => { setProfile(p); window.YiteAPI?.setUser?.(p); });
  };

  const handleNickSave = async () => {
    if (!nickInput.trim()) return;
    const res = await _pAPI.updateProfile({ nickname: nickInput.trim() });
    if (!res.error) { setProfile(res); window.YiteAPI?.setUser?.(res); }
    setEditingNick(false);
  };

  const handleSettingsSaved = p => { setProfile(p); window.YiteAPI?.setUser?.(p); };

  const avatarUrl = profile?.avatar || (profile?.avatar_oss_key ? (window.YiteAPI?.cdnDomain || '') + '/' + profile.avatar_oss_key : '');
  const planName = profile?.subscription?.plan_name || 'Free';

  if (loading) return <AppShell active="profile"><div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}><div className="yt-spinner" style={{ width: 32, height: 32 }} /></div></AppShell>;

  return (
    <AppShell active="profile">
      <div style={{ overflowY: 'auto', height: '100%' }}>
        <div style={{ padding: isMobile ? '20px 16px 80px' : '32px 36px 60px' }}>

          {/* ── Header ── */}
          <div className="yt-fadein" style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', alignItems: isMobile ? 'center' : 'flex-start', gap: isMobile ? 12 : 24, marginBottom: 36, textAlign: isMobile ? 'center' : undefined }}>
            {/* Avatar */}
            <div style={{ position: 'relative', width: 80, height: 80, borderRadius: '50%', overflow: 'hidden', background: 'linear-gradient(135deg, var(--accent), var(--accent-2))', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 28, fontWeight: 700, color: '#06080f', flexShrink: 0, cursor: 'pointer' }}
              onClick={() => setShowSettings(true)}>
              {avatarUrl ? <img src={avatarUrl} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : (profile?.nickname || '?')[0].toUpperCase()}
              <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: 0, transition: 'opacity .15s' }}
                onMouseEnter={e => e.currentTarget.style.opacity = 1} onMouseLeave={e => e.currentTarget.style.opacity = 0}>
                <Icon name="image" size={20} color="#fff" />
              </div>
            </div>
            {/* Info */}
            <div style={{ flex: 1, minWidth: 0, width: isMobile ? '100%' : undefined }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6, justifyContent: isMobile ? 'center' : undefined }}>
                {editingNick ? (
                  <div style={{ display: 'flex', gap: 6 }}>
                    <input autoFocus value={nickInput} onChange={e => setNickInput(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') handleNickSave(); if (e.key === 'Escape') setEditingNick(false); }}
                      className="yt-input" style={{ height: 36, width: 200, fontSize: 16 }} />
                    <button onClick={handleNickSave} style={{ height: 36, padding: '0 12px', borderRadius: 8, border: 'none', background: 'var(--accent)', color: '#052028', fontSize: 13, fontWeight: 600, cursor: 'pointer' }}>{t('common.save')}</button>
                  </div>
                ) : (
                  <>
                    <span style={{ fontSize: 24, fontWeight: 700, color: 'var(--fg-0)' }}>{profile?.nickname || 'User'}</span>
                    <button onClick={() => { setNickInput(profile?.nickname || ''); setEditingNick(true); }} aria-label="编辑昵称" style={{ background: 'none', border: 'none', color: 'var(--fg-3)', cursor: 'pointer', width: 32, height: 32, borderRadius: 6, display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon name="pen" size={14} /></button>
                  </>
                )}
              </div>
              <div style={{ fontSize: 13, color: 'var(--fg-3)', marginBottom: 10 }}>{profile?.bio || t('profile.bioPrompt')}</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 16, fontSize: 13, color: 'var(--fg-2)', flexWrap: 'wrap', justifyContent: isMobile ? 'center' : undefined }}>
                <span><strong style={{ color: 'var(--fg-0)' }}>{followers}</strong> {t('profile.followers')}</span>
                <span><strong style={{ color: 'var(--fg-0)' }}>{following}</strong> {t('profile.following')}</span>
                <span style={{ display: 'flex', alignItems: 'center', gap: 4 }}><Icon name="coin" size={13} color="var(--accent)" /><strong style={{ color: 'var(--accent)' }}>{_fmtCoins(profile?.coins || 0)}</strong> {t('profile.coins')}</span>
                <span style={{ padding: '2px 8px', borderRadius: 4, background: 'rgba(34,211,238,0.1)', color: 'var(--accent)', fontSize: 12, fontWeight: 600 }}>{planName}</span>
              </div>
            </div>
            {/* Actions — 桌面端：settings + payment records + share */}
            {!isMobile && (
              <div style={{ display: 'flex', gap: 8, flexShrink: 0, flexWrap: 'wrap' }}>
                <button onClick={() => setShowSettings(true)} style={{ height: 34, padding: '0 14px', borderRadius: 8, border: '1px solid var(--line-2)', background: 'rgba(255,255,255,0.04)', color: 'var(--fg-1)', fontSize: 13, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6 }}>
                  <Icon name="settings" size={13} /> {t('profile.settings')}
                </button>
                <a href="/orders" style={{ height: 34, padding: '0 14px', borderRadius: 8, border: '1px solid var(--line-2)', background: 'rgba(255,255,255,0.04)', color: 'var(--fg-1)', fontSize: 13, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, textDecoration: 'none' }}>
                  <Icon name="coin" size={13} /> {t('shared.menu.orders')}
                </a>
                <button onClick={() => { navigator.clipboard.writeText(window.location.origin + '/u/' + profile?.id); ytAlert(t('profile.linkCopied')); }} style={{ height: 34, padding: '0 14px', borderRadius: 8, border: '1px solid var(--line-2)', background: 'rgba(255,255,255,0.04)', color: 'var(--fg-1)', fontSize: 13, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6 }}>
                  <Icon name="link" size={13} /> {t('profile.shareHome')}
                </button>
              </div>
            )}
          </div>

          {/* ── Data cards ── */}
          <div style={{ display: 'flex', flexDirection: isMobile ? 'column' : 'row', gap: 16, marginBottom: isMobile ? 20 : 36, flexWrap: isMobile ? undefined : 'wrap' }}>
            <StorageCard data={storage} isMobile={isMobile} />
            <StatsCard data={stats} isMobile={isMobile} />
            <SignInCard data={signin} onSignIn={handleSignIn} isMobile={isMobile} />
            <InviteCard data={invite} isMobile={isMobile} />
          </div>

          {/* ── Mobile-only quick links（紧凑列表样式，副标显示真实数据）── */}
          {isMobile && (
            <div style={{ marginBottom: 28 }}>
              <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--fg-3)', letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 10 }}>{t('profile.quickLinks')}</div>
              <div style={{ borderRadius: 12, background: 'rgba(255,255,255,0.02)', border: '1px solid var(--line-2)', overflow: 'hidden' }}>
                {(() => {
                  const assetSub = storage
                    ? t('profile.assetsSubFiles').replace('{size}', _fmtBytes(storage.used_bytes || 0)).replace('{n}', String(storage.file_count || 0))
                    : t('profile.assetsView');
                  const items = [
                    { icon: 'folder',   label: t('profile.assets'),         sub: assetSub, href: '/assets' },
                    { icon: 'star',     label: t('profile.subscription'),   sub: profile?.subscription?.plan_name || 'Free', href: '/pricing' },
                    { icon: 'coin',     label: t('profile.payments'),       sub: t('profile.paymentsSub'), href: '/orders' },
                    { icon: 'bell',     label: t('profile.notifications'),  sub: t('profile.notificationsSub'), href: '#' },
                    { icon: 'settings', label: t('profile.settingsCard'),   sub: t('profile.settingsSub'), href: '#', onClick: () => setShowSettings(true) },
                  ];
                  return items.map((q, i) => {
                    const isLast = i === items.length - 1;
                    const Wrapper = q.onClick ? 'button' : 'a';
                    return (
                      <Wrapper
                        key={i}
                        href={q.onClick ? undefined : q.href}
                        onClick={q.onClick ? (e) => { e.preventDefault(); q.onClick(); } : undefined}
                        style={{
                          display: 'flex', alignItems: 'center', gap: 14,
                          padding: '14px 16px',
                          borderBottom: isLast ? 'none' : '1px solid var(--line)',
                          background: 'transparent', border: 'none', borderRadius: 0,
                          textDecoration: 'none', color: 'inherit', fontFamily: 'inherit',
                          cursor: 'pointer', textAlign: 'left', width: '100%',
                        }}
                      >
                        <div style={{ width: 32, height: 32, borderRadius: 8, background: 'rgba(34,211,238,0.1)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                          <Icon name={q.icon} size={15} color="var(--accent)" />
                        </div>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 14, fontWeight: 500, color: 'var(--fg-0)' }}>{q.label}</div>
                          <div style={{ fontSize: 11, color: 'var(--fg-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginTop: 2 }}>{q.sub}</div>
                        </div>
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--fg-3)" strokeWidth="2" strokeLinecap="round" style={{ flexShrink: 0 }}>
                          <polyline points="9 18 15 12 9 6"/>
                        </svg>
                      </Wrapper>
                    );
                  });
                })()}
              </div>
            </div>
          )}

          {/* ── Tabs ── */}
          <div style={{ display: 'flex', gap: 6, marginBottom: 24, borderBottom: '1px solid var(--line)', paddingBottom: 8, overflowX: isMobile ? 'auto' : undefined }}>
            {[
              { id: 'works', label: t('profile.myWorks'), count: works.length },
              { id: 'liked', label: t('profile.liked'), locked: true },
              { id: 'saved', label: t('profile.saved'), locked: true },
              // records tab 已迁移至独立页 /orders（profile 快捷入口可点击进入）
            ].map(tb => (
              <button key={tb.id} onClick={() => !tb.locked && setTab(tb.id)} style={{
                padding: '10px 20px', borderRadius: 8, border: 'none', cursor: tb.locked ? 'default' : 'pointer',
                background: tab === tb.id ? 'rgba(34,211,238,0.08)' : 'transparent',
                color: tab === tb.id ? 'var(--fg-0)' : 'var(--fg-3)',
                fontSize: 14, fontWeight: tab === tb.id ? 600 : 400,
                display: 'flex', alignItems: 'center', gap: 6,
                whiteSpace: 'nowrap', flexShrink: 0,
              }}>
                {tb.label} {tb.count != null && <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>({tb.count})</span>}
                {tb.locked && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--fg-3)" strokeWidth="2" strokeLinecap="round"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 018 0v4"/></svg>}
              </button>
            ))}
          </div>

          {/* ── Tab content ── */}
          <div style={{ width: '100%', padding: isMobile ? 0 : undefined }}>
            {tab === 'works' && (
              works.length > 0 ? (
                <div className="yt-masonry" style={{ columnCount: isMobile ? 2 : Math.min(4, Math.max(2, works.length)) }}>
                  {works.map(t => <InspirationCard key={t.id} tpl={t} />)}
                </div>
              ) : <EmptyState icon="image" title={t('profile.noWorks')} subtitle={t('profile.publishWorks')} />
            )}

            {tab === 'liked' && <EmptyState icon="star" title={t('profile.liked')} subtitle={t('profile.comingSoon')} />}
            {tab === 'saved' && <EmptyState icon="star" title={t('profile.saved')} subtitle={t('profile.comingSoon')} />}

            {tab === 'records' && (
              <div>
                {/* Sub-tabs: 订阅 / 订单 / 额度流水 */}
                <div style={{ display: 'flex', gap: 4, marginBottom: 16, padding: 3, background: 'rgba(255,255,255,0.03)', border: '1px solid var(--line-2)', borderRadius: 9, width: 'fit-content' }}>
                  {[
                    { id: 'subscriptions', label: t('billing.tab.subscriptions') },
                    { id: 'orders',        label: t('billing.tab.orders') },
                    { id: 'coins',         label: t('billing.tab.coins') },
                  ].map(rt => (
                    <button key={rt.id} onClick={() => setRecordTab(rt.id)} style={{
                      padding: '6px 14px', borderRadius: 6, border: 'none', cursor: 'pointer',
                      background: recordTab === rt.id ? 'var(--accent)' : 'transparent',
                      color: recordTab === rt.id ? '#052028' : 'var(--fg-2)',
                      fontSize: 12, fontWeight: 600,
                      transition: 'all .12s',
                    }}>{rt.label}</button>
                  ))}
                </div>

                {/* 订阅 */}
                {recordTab === 'subscriptions' && (
                  subscriptions.length > 0 ? (
                    <div>{subscriptions.map(s => <SubscriptionRow key={s.id} s={s} />)}</div>
                  ) : <EmptyState icon="star" title={t('billing.empty.subs')} subtitle={t('billing.emptySub.subs')} />
                )}

                {/* 订单 */}
                {recordTab === 'orders' && (
                  orderRecords.length > 0 ? (
                    <div>{orderRecords.map(r => <OrderRecordRow key={r.id || r.order_id} r={r} />)}</div>
                  ) : <EmptyState icon="coin" title={t('billing.empty.orders')} subtitle={t('billing.emptySub.orders')} />
                )}

                {/* 额度流水 */}
                {recordTab === 'coins' && (
                  coinRecords.length > 0 ? (
                    <div>{coinRecords.map((r, i) => <CoinRecordRow key={r.id || i} r={r} />)}</div>
                  ) : <EmptyState icon="coin" title={t('billing.empty.coins')} subtitle={t('billing.emptySub.coins')} />
                )}
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Settings modal */}
      {showSettings && <AccountSettingsModal profile={profile} onClose={() => setShowSettings(false)} onSaved={handleSettingsSaved} isMobile={isMobile} />}
    </AppShell>
  );
};

Object.assign(window, { DashboardScreen });
