// ─── PASSPORT view ────────────────────────────────────────────────────

const PassportView = ({
  user, stamps, activeTab, setActiveTab,
  onBack, onProfile, onActivate, onLogout, pendingRewards = []
}) => {
  const t = useT();
  const [passportZone, setPassportZone] = useState(localStorage.getItem('helixZone') || 'la-paz');
  const [showZoneSelector, setShowZoneSelector] = useState(false);
  const [passportBiz, setPassportBiz] = useState(BUSINESSES);
  const [loadingZone, setLoadingZone] = useState(false);
  const [bdayData, setBdayData] = useState({ birth_month: null, redemptions: [] });
  const [authRequest, setAuthRequest] = useState(null);
  const [authPin, setAuthPin] = useState('');

  useEffect(() => {
    if (!user?.id) return;
    fetch(`${API}/birthday/user?userId=${user.id}`)
      .then(r => r.json())
      .then(data => setBdayData({ birth_month: data.birth_month || null, redemptions: data.redemptions || [] }))
      .catch(() => {});
  }, [user?.id]);

  // Poll for pending birthday authorization requests
  useEffect(() => {
    if (!user?.id || !user?.poll_token) return;
    const checkPending = () => {
      fetch(`${API}/birthday/auth-pending?userId=${user.id}&token=${user.poll_token}&t=${Date.now()}`)
        .then(r => r.json())
        .then(data => { if (data?.requestId) setAuthRequest(data); })
        .catch(() => {});
    };
    checkPending();
    const poll = setInterval(checkPending, 3000);
    const onFocus = () => checkPending();
    const onVisible = () => { if (document.visibilityState === 'visible') checkPending(); };
    window.addEventListener('focus', onFocus);
    document.addEventListener('visibilitychange', onVisible);
    return () => {
      clearInterval(poll);
      window.removeEventListener('focus', onFocus);
      document.removeEventListener('visibilitychange', onVisible);
    };
  }, [user?.id]);

  const handleAuthRespond = async (approved) => {
    if (!authRequest) return;
    if (approved && authPin.length < 4) return;
    const req = authRequest;
    setAuthRequest(null);
    setAuthPin('');
    try {
      await fetch(`${API}/birthday/auth-respond`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ requestId: req.requestId, userId: user.id, pin: authPin, approved }),
      });
      if (approved) {
        fetch(`${API}/birthday/user?userId=${user.id}`)
          .then(r => r.json())
          .then(data => setBdayData({ birth_month: data.birth_month || null, redemptions: data.redemptions || [] }))
          .catch(() => {});
      }
    } catch(_) {}
  };

  // Guard: no user
  if (!user) {
    return (
      <div className="view-enter">
        <HeaderBar title={t('passport_title')} onBack={onBack} onProfile={()=>{}} right={<div/>}/>
        <div style={{
          padding:'60px 32px 80px',
          display:'flex', flexDirection:'column', alignItems:'center',
          textAlign:'center'
        }}>
          <div style={{
            width:96, height:96, borderRadius:'50%',
            background:'var(--fog)',
            display:'flex', alignItems:'center', justifyContent:'center',
            marginBottom:24,
            border:'1px solid rgba(15,23,42,0.08)'
          }}>
            <IconBookOpen size={40} color="var(--teal)" strokeWidth={1.6}/>
          </div>
          <div className="label-eyebrow" style={{ marginBottom:8, color:'var(--teal)' }}>{t('passport_no_user_eyebrow')}</div>
          <h2 className="h-section" style={{ fontSize:26, margin:'0 0 10px' }}>
            {t('passport_no_user_h')}<br/>{t('passport_no_user_h2')}
          </h2>
          <p className="body-soft" style={{ margin:'0 0 28px', fontSize:14, maxWidth:300 }}>
            {t('passport_no_user_sub')}
          </p>
          <button onClick={onActivate} style={{
            background:'var(--teal)', color:'white',
            border:0, borderRadius:14,
            padding:'14px 24px', fontWeight:700, fontSize:14,
            cursor:'pointer', minWidth:240,
            display:'inline-flex', alignItems:'center', justifyContent:'center', gap:8
          }}>
            {t('passport_activate')} <IconChevronRight size={16}/>
          </button>
        </div>
      </div>
    );
  }

  const handleZoneChange = async (zoneId) => {
    if (zoneId === passportZone) return;
    setLoadingZone(true);
    setPassportZone(zoneId);
    try {
      const data = await fetch(`${API}/businesses?zone=${zoneId}`).then(r => r.json());
      setPassportBiz(data.map(b => ({ ...b, rewardGoal: b.reward_goal, img: b.img_url || b.img ||
            (b.category === 'food' ? IMG?.cafe : b.category === 'adventure' ? IMG?.escape
                : b.category === 'grooming' ? IMG?.barber : IMG?.cafe) || '' })));
    } catch(_) {}
    setLoadingZone(false);
  };

  const unlockedCount = passportBiz.filter(b => stamps[b.id]?.unlocked).length;
  const initial = user.name.trim().charAt(0).toUpperCase();
  const passportId = user.public_id || String(user.id).padStart(5, '0');
  const isBdayMonth = bdayData.birth_month && bdayData.birth_month === (new Date().getMonth() + 1);

  return (
    <div className="view-enter">
      <HeaderBar title={t('passport_title')} onBack={onBack} onProfile={()=>{}} right={<div/>}/>

      {/* User card */}
      <div style={{ padding:'18px 24px 0' }}>
        <div style={{
          display:'flex', alignItems:'center', gap:14,
          padding:'16px 18px',
          background:'var(--fog)',
          borderRadius:16,
          border:'1px solid rgba(15,23,42,0.06)'
        }}>
          <div style={{
            width:48, height:48, borderRadius:'50%',
            background:'var(--teal)', color:'white',
            display:'flex', alignItems:'center', justifyContent:'center',
            fontWeight:800, fontSize:20, letterSpacing:'-0.02em'
          }}>{initial}</div>
          <div style={{ flex:1, minWidth:0 }}>
            <div style={{
              fontWeight:800, fontSize:16, letterSpacing:'-0.01em',
              whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'
            }}>{user.name}</div>
            <div style={{ fontSize:11, color:'var(--muted)', fontFamily:'monospace', fontWeight:600, marginTop:2, letterSpacing:'.06em' }}>
              {t('passport_id_label')} #{passportId}
            </div>
            <div style={{ display:'flex', alignItems:'center', gap:6, marginTop:4, minWidth:0 }}>
            <div className="label-eyebrow" style={{ margin:0, flexShrink:0 }}>{t('passport_explorer')} ·</div>
            <button onClick={() => setShowZoneSelector(true)} style={{
              background:'rgba(13,148,136,0.1)', color:'var(--teal)',
              border:'1px solid rgba(13,148,136,0.2)', borderRadius:99,
              padding:'3px 10px', fontSize:10, fontWeight:700,
              cursor:'pointer', letterSpacing:'.04em', textTransform:'uppercase',
              display:'inline-flex', alignItems:'center', gap:4,
              minWidth:0, overflow:'hidden', maxWidth:160
            }}>
              <span style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                {ZONES.find(z => z.id === passportZone)?.label || 'La Paz'}
              </span>
              <IconChevronRight size={10} style={{ flexShrink:0 }}/>
            </button>
          </div>
          </div>
          <button onClick={onLogout} style={{
            background:'transparent', border:0,
            color:'var(--muted)', fontSize:12, fontWeight:500,
            cursor:'pointer', display:'inline-flex', alignItems:'center', gap:4,
            minHeight:44, padding:'0 4px'
          }}>
            <IconLogOut size={16}/> {t('passport_logout')}
          </button>
        </div>
      </div>

      {/* Birthday banner */}
      {isBdayMonth && (
        <div style={{ padding:'14px 24px 0' }}>
          <div style={{
            background:'linear-gradient(135deg, #E91E8C, #C2185B)',
            borderRadius:16,
            padding:'16px 18px',
            color:'white'
          }}>
            <div style={{ fontWeight:800, fontSize:15, marginBottom:6, letterSpacing:'-0.01em' }}>
              {t('passport_bday_title')}
            </div>
            <div style={{ fontSize:12, lineHeight:1.5, opacity:.92 }}>
              {t('passport_bday_body', { id: passportId })}
            </div>
          </div>
        </div>
      )}

      {/* Tabs */}
      <div style={{ padding:'18px 24px 0' }}>
        <div style={{
          display:'grid', gridTemplateColumns:'1fr 1fr',
          background:'var(--fog)',
          padding:4, borderRadius:12,
          border:'1px solid rgba(15,23,42,0.06)'
        }}>
          {[
            {id:'gran-viajero', label:t('passport_tab_gv')},
            {id:'vip', label:t('passport_tab_vip')}
          ].map(tab => {
            const active = activeTab === tab.id;
            return (
              <button key={tab.id} onClick={() => setActiveTab(tab.id)} style={{
                background: active ? 'var(--canvas)' : 'transparent',
                color: active ? 'var(--ink)' : 'var(--muted)',
                border:0, borderRadius:9,
                padding:'12px 10px', minHeight:44,
                fontSize:13, fontWeight: active ? 700 : 500,
                cursor:'pointer',
                boxShadow: active ? '0 1px 3px rgba(15,23,42,0.08)' : 'none',
                transition:'all .2s'
              }}>{tab.label}</button>
            );
          })}
        </div>
      </div>

      {/* Tab content */}
      {activeTab === 'gran-viajero' ? (
        <GranViajero stamps={stamps} unlockedCount={unlockedCount} businesses={passportBiz} loading={loadingZone}/>
      ) : (
        <ClienteVIP stamps={stamps} pendingRewards={pendingRewards} userId={user?.id} businesses={passportBiz} loading={loadingZone}/>
      )}
    {authRequest && ReactDOM.createPortal(
      <div style={{ position:'fixed', inset:0, zIndex:400, background:'rgba(15,23,42,0.82)', backdropFilter:'blur(6px)', display:'flex', alignItems:'center', justifyContent:'center', padding:24, animation:'fadeIn .15s ease both' }}>
        <div style={{ background:'var(--canvas)', borderRadius:20, padding:'28px 24px', width:'100%', maxWidth:360, boxShadow:'0 20px 60px rgba(15,23,42,0.35)' }}>
          <div style={{ textAlign:'center', fontSize:40, marginBottom:12 }}>🎂</div>
          <div style={{ fontWeight:800, fontSize:18, marginBottom:8, textAlign:'center' }}>{t('passport_auth_title')}</div>
          <div style={{ fontSize:14, color:'var(--muted)', textAlign:'center', marginBottom:16, lineHeight:1.6 }}>
            {t('passport_auth_body', { biz: authRequest.business_name })}
          </div>
          <div style={{ marginBottom:16 }}>
            <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, marginBottom:5, textTransform:'uppercase', letterSpacing:'0.05em' }}>{t('passport_auth_pin_label')}</div>
            <input value={authPin} onChange={e => setAuthPin(e.target.value.replace(/\D/g,'').slice(0,6))} inputMode="numeric" maxLength={6} placeholder="••••" style={{ width:'100%', boxSizing:'border-box', background:'var(--fog)', border:'1px solid rgba(15,23,42,0.12)', borderRadius:10, padding:'12px', fontSize:20, letterSpacing:'.3em', outline:'none', fontWeight:700, textAlign:'center' }}/>
          </div>
          <div style={{ display:'flex', gap:8 }}>
            <button onClick={() => handleAuthRespond(false)} style={{ flex:1, background:'transparent', border:'1px solid rgba(15,23,42,0.15)', borderRadius:12, padding:'15px', fontSize:14, fontWeight:600, cursor:'pointer', minHeight:50 }}>
              {t('passport_auth_reject')}
            </button>
            <button onClick={() => handleAuthRespond(true)} disabled={authPin.length < 4} style={{ flex:2, background:'linear-gradient(135deg,#E91E8C,#C2185B)', color:'white', border:0, borderRadius:12, padding:'15px', fontSize:14, fontWeight:700, cursor:'pointer', opacity: authPin.length < 4 ? 0.5 : 1, minHeight:50 }}>
              {t('passport_auth_approve')}
            </button>
          </div>
        </div>
      </div>,
      document.body
    )}

    {showZoneSelector && ReactDOM.createPortal(
        <div onClick={() => setShowZoneSelector(false)} style={{ position:'fixed', inset:0, zIndex:999, background:'rgba(15,23,42,0.5)', backdropFilter:'blur(6px)', display:'flex', alignItems:'flex-end', justifyContent:'center' }}>
          <div onClick={e => e.stopPropagation()} style={{ width:'100%', maxWidth:430, background:'var(--canvas)', borderRadius:'20px 20px 0 0', padding:'20px 20px 36px' }}>
            <div className="label-eyebrow" style={{ color:'var(--teal)', marginBottom:12 }}>{t('passport_change_zone')}</div>
            <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
              {ZONES.map(z => (
                <button key={z.id} onClick={() => { handleZoneChange(z.id); setShowZoneSelector(false); }} style={{
                  background: passportZone === z.id ? 'rgba(13,148,136,0.1)' : 'var(--fog)',
                  border: passportZone === z.id ? '1px solid rgba(13,148,136,0.3)' : '1px solid rgba(15,23,42,0.06)',
                  borderRadius:12, padding:'16px',
                  display:'flex', justifyContent:'space-between', alignItems:'center',
                  cursor:'pointer', fontWeight:600, fontSize:14, minHeight:52,
                  color: passportZone === z.id ? 'var(--teal)' : 'var(--ink)'
                }}>
                  {z.label}
                  {passportZone === z.id && <IconCheck size={16} color="var(--teal)"/>}
                </button>
              ))}
            </div>
          </div>
        </div>,
        document.body
      )}
    </div>
  );
};

// ── Customs-ink palette + stamp geometries (deterministic per business id) ──
// Rich immigration-office inks: passport blue, dark teal, burgundy, indigo.
const PASSPORT_INKS = [
  { base:'#1B3A6B', deep:'rgba(10,22,48,0.55)' },   // deep passport blue
  { base:'#0B473F', deep:'rgba(4,42,36,0.55)' },    // dark teal
  { base:'#7A1F2B', deep:'rgba(58,12,18,0.58)' },   // burgundy / crimson
  { base:'#382663', deep:'rgba(26,16,52,0.55)' },   // dark indigo
];
// Mixed shapes so the page reads like a chaotic real passport
const PASSPORT_SHAPES = ['square','circle','hexagon','oval','octagon'];

const GranViajero = ({ stamps, unlockedCount, businesses = BUSINESSES, loading }) => {
  const t = useT();
  const total = businesses.length;
  const pct = total > 0 ? (unlockedCount / total) * 100 : 0;

  return (
  <div style={{ padding:'24px 24px 80px' }}>
    {loading && <div style={{ textAlign:'center', color:'var(--muted)', fontSize:13, padding:'20px 0' }}>{t('passport_loading')}</div>}

    {/* Header row */}
    <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:12 }}>
      <SectionLabel style={{ padding:0 }}>
        {t('passport_stamps_label')} · {t('passport_destinations')}
      </SectionLabel>
      <span style={{ fontVariantNumeric:'tabular-nums', fontSize:14, fontWeight:800, color:'var(--teal)', letterSpacing:'-0.01em' }}>
        {unlockedCount}<span style={{ color:'var(--muted)', fontWeight:500, fontSize:13 }}>/{total}</span>
      </span>
    </div>

    {/* Flight-path progress bar */}
    <div style={{ position:'relative', height:22, marginBottom:26 }}>
      {/* Dashed air-route background */}
      <div style={{
        position:'absolute', left:8, right:8, top:'50%',
        height:1.5, transform:'translateY(-50%)',
        backgroundImage:'repeating-linear-gradient(90deg, rgba(15,23,42,0.18) 0, rgba(15,23,42,0.18) 4px, transparent 4px, transparent 10px)',
      }}/>
      {/* Filled route */}
      {unlockedCount > 0 && (
        <div style={{
          position:'absolute', left:8, top:'50%',
          height:3, transform:'translateY(-50%)',
          background:'linear-gradient(90deg, var(--teal) 0%, #06B6D4 100%)',
          width:`calc(${pct}% - 16px + ${pct * 0.16}px)`,
          borderRadius:99, transition:'width .6s cubic-bezier(.4,0,.2,1)',
        }}/>
      )}
      {/* Origin pin */}
      <div style={{
        position:'absolute', left:4, top:'50%', transform:'translateY(-50%)',
        width:9, height:9, borderRadius:'50%',
        background: unlockedCount > 0 ? 'var(--teal)' : 'rgba(15,23,42,0.2)',
        border:'2px solid var(--canvas)', zIndex:2, transition:'background .4s',
      }}/>
      {/* Destination pin */}
      <div style={{
        position:'absolute', right:4, top:'50%', transform:'translateY(-50%)',
        width:9, height:9, borderRadius:'50%',
        background: unlockedCount === total && total > 0 ? 'var(--teal)' : 'rgba(15,23,42,0.2)',
        border:'2px solid var(--canvas)', zIndex:2, transition:'background .4s',
      }}/>
      {/* Plane marker at progress front */}
      {unlockedCount > 0 && unlockedCount < total && (
        <div style={{
          position:'absolute',
          left:`calc(${pct}% - 8px)`,
          top:'50%', transform:'translateY(-50%)',
          fontSize:14, lineHeight:1, zIndex:3,
          filter:'drop-shadow(0 1px 3px rgba(15,23,42,0.28))',
          transition:'left .6s cubic-bezier(.4,0,.2,1)',
        }}>✈</div>
      )}
      {/* Star at destination when complete */}
      {unlockedCount === total && total > 0 && (
        <div style={{
          position:'absolute', right:0, top:'50%',
          transform:'translate(2px,-50%)', fontSize:14, lineHeight:1, zIndex:3,
        }}>⭐</div>
      )}
    </div>

    {/* Passport page container – bone security paper */}
    <div className="passport-page" style={{
      borderRadius:4, padding:'20px 16px',
      marginBottom:20,
    }}>
      <div style={{
        display:'grid', gridTemplateColumns:'repeat(3, 1fr)',
        gap:12,
      }}>
        {[...businesses].sort((a, b) => {
          const ua = stamps[a.id]?.unlocked ? 1 : 0;
          const ub = stamps[b.id]?.unlocked ? 1 : 0;
          return ub - ua;
        }).map(b => {
          const unlocked = stamps[b.id]?.unlocked;
          const ROTS = [-3.8, 3.2, -4.5, 4.0, -3.2, 4.5, -4.0, 3.5, -3.5, 4.2];
          // Number derived from id (ids can be strings) so % never yields NaN
          const idNum = typeof b.id === 'number' ? b.id
            : (typeof stampHash === 'function' ? stampHash(b.id)
            : String(b.id).split('').reduce((a, c) => (a * 31 + c.charCodeAt(0)) >>> 0, 0));
          const rot = ROTS[idNum % ROTS.length];
          // Deterministic ink + geometry locked to the business id
          const inkIdx = idNum % PASSPORT_INKS.length;
          const ink = PASSPORT_INKS[inkIdx];
          const shape = PASSPORT_SHAPES[idNum % PASSPORT_SHAPES.length];
          const clipCls = shape === 'octagon' ? 'clip-octagon' : shape === 'hexagon' ? 'clip-hexagon' : '';
          const bodyCls = shape === 'square' ? 'stamp-perf' : clipCls;
          const innerRadius = shape === 'circle' ? '50%' : shape === 'oval' ? '50% / 38%' : 3;
          const bodyPad = shape === 'square' ? 7 : shape === 'octagon' || shape === 'hexagon' ? 9 : 6;
          return (
            <div key={b.id} style={{ textAlign:'center', minWidth:0, paddingBottom: unlocked ? 44 : 28 }}>

              {/* ── UNLOCKED: rich customs-ink rubber stamp ── */}
              {unlocked ? (
                <div style={{
                  position:'relative', display:'inline-block', width:'100%',
                  '--stamp-rot': `${rot}deg`,
                  transform:`rotate(${rot}deg)`, transformOrigin:'center',
                  animation:'stampIn .6s cubic-bezier(.2,.8,.2,1) both',
                }}>
                  {/* Transparent custom-stamp PNG inked straight onto the passport paper —
                      no card, no border, no shadow, no perforation. Filter tints it per city;
                      multiply makes any white background melt into the paper. */}
                  <div style={{ position:'relative', aspectRatio:'1/1' }}>
                    <SmartImg src={b.img_seal || b.img} alt={b.name} style={{
                      position:'absolute', inset:0,
                      objectFit:'contain',
                      filter:`url(#ink-${inkIdx})`,
                      mixBlendMode:'multiply',
                      background:'transparent',
                    }}/>
                  </div>
                  {/* VISITADO — hand-stamp in this venue's ink */}
                  <div style={{ position:'absolute', left:'50%', bottom:'4%', transform:'translateX(-50%)', zIndex:5, color:ink.base }}>
                    <span className="visitado-badge">Visitado</span>
                  </div>
                </div>
              ) : (
                /* ── LOCKED: blank passport box (shape-aware) ── */
                <div className={'stamp-empty-bg '+clipCls} style={{
                  position:'relative', aspectRatio:'1/1',
                  borderRadius: shape === 'circle' ? '50%' : shape === 'oval' ? '50% / 38%' : 3,
                  overflow:'hidden',
                  border: clipCls ? 'none' : '1px solid rgba(74,56,24,0.16)',
                }}>
                  <div style={{
                    position:'absolute', inset:0,
                    display:'flex', alignItems:'center', justifyContent:'center',
                    flexDirection:'column', gap:6,
                  }}>
                    <div style={{ opacity:0.3, display:'flex' }}>
                      <IconLock size={20} color="rgba(15,23,42,0.5)" strokeWidth={1.6}/>
                    </div>
                    <div style={{
                      fontSize:6.5, fontWeight:700,
                      color:'rgba(15,23,42,0.28)',
                      letterSpacing:'0.16em', textTransform:'uppercase',
                      fontFamily:'ui-monospace, monospace',
                    }}>SIN SELLO</div>
                  </div>
                </div>
              )}

              {/* Business name */}
              <div style={{
                marginTop: unlocked ? 20 : 12, fontSize:11, fontWeight:600,
                color: unlocked ? 'var(--ink)' : 'var(--muted)',
                opacity: unlocked ? 0.78 : 0.75,
                whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis',
                letterSpacing:'-0.01em',
              }}>{b.name}</div>
              {unlocked && (
                <div style={{
                  fontSize:9.5, color:'var(--teal)', fontWeight:700, marginTop:1,
                  letterSpacing:'.04em', fontVariantNumeric:'tabular-nums',
                }}>
                  {stamps[b.id].visits} {stamps[b.id].visits === 1 ? t('passport_visit_s') : t('passport_visit_p')}
                </div>
              )}
            </div>
          );
        })}
      </div>
      {/* Micro-text page footer */}
      <div className="passport-micro">· PAGE 01 ·</div>
    </div>

    {/* Footer summary */}
    <div style={{
      background:'var(--fog)', borderRadius:14,
      padding:'14px 16px', border:'1px solid rgba(15,23,42,0.06)',
      fontSize:13, color:'var(--ink)', lineHeight:1.45
    }}>
      {t('passport_visited_pre')} <b>{unlockedCount}</b> {unlockedCount === 1 ? t('home_spots_singular') : t('home_spots_plural')} {unlockedCount === 1 ? t('passport_visited_post_s') : t('passport_visited_post_p')}
      {' '}
      <span style={{ color:'var(--muted)' }}>
        {unlockedCount === businesses.length
          ? t('passport_all_done')
          : `${t('passport_visit_more_pre')} ${businesses.length - unlockedCount} ${businesses.length - unlockedCount === 1 ? t('home_spots_singular') : t('home_spots_plural')} ${t('passport_visit_more_post')}`}
      </span>
    </div>
  </div>
  );
};

const ClienteVIP = ({ stamps, pendingRewards = [], userId, businesses = BUSINESSES, loading }) => {
  const t = useT();
  const plt = usePLT();
  const [drawerBiz, setDrawerBiz] = useState(null);
  const [localRewards, setLocalRewards] = useState(pendingRewards);
  useEffect(() => { setLocalRewards(pendingRewards); }, [pendingRewards]);
  const handleRedeemed = (rewardId) => {
    setLocalRewards(prev => {
      const updated = prev.filter(r => r.id !== rewardId);
      if (drawerBiz && !updated.some(r => r.business_id === drawerBiz.id)) {
        setDrawerBiz(null);
      }
      return updated;
    });
  };

  return (
    <div style={{ padding:'24px 24px 80px' }}>
      <SectionLabel style={{ padding:0 }}>{t('passport_progress')}</SectionLabel>
      <div style={{ display:'flex', flexDirection:'column', gap:14 }}>
        {loading
          ? <div style={{ textAlign:'center', color:'var(--muted)', fontSize:13, padding:'20px 0' }}>{t('passport_loading')}</div>
          : null}
        {[...businesses].sort((a, b) => (stamps[b.id]?.unlocked ? 1 : 0) - (stamps[a.id]?.unlocked ? 1 : 0)).map(b => {
          const visits = stamps[b.id]?.visits || 0;
          const goal = b.rewardGoal || 0;
          const cycleVisits = goal > 0 ? visits % goal : 0;
          const pct = goal > 0 ? (cycleVisits / goal) * 100 : 0;
          const count = localRewards.filter(r => r.business_id === b.id).length;
          return (
            <div key={b.id} style={{
              background:'var(--fog)',
              border:'1px solid rgba(15,23,42,0.06)',
              borderRadius:14,
              padding:'14px 16px',
            }}>
              <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:8, gap:10 }}>
                <div>
                  <div className="label-eyebrow">{b.tag}</div>
                  <div style={{ fontWeight:700, fontSize:15, letterSpacing:'-0.01em' }}>{b.name}</div>
                </div>
                <div style={{ fontVariantNumeric:'tabular-nums', fontSize:13, fontWeight:700, color:'var(--ink)' }}>
                  {cycleVisits}<span style={{ color:'var(--muted)', fontWeight:500 }}>/{b.rewardGoal}</span>
                </div>
              </div>
              <div style={{ height:8, borderRadius:99, background:'rgba(13,148,136,0.15)', overflow:'hidden', marginBottom:10 }}>
                <div style={{ width:`${pct}%`, height:'100%', background:'var(--teal)', transition:'width .6s ease' }}/>
              </div>
              <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', gap:10 }}>
                <div style={{ fontSize:12, color:'var(--muted)', lineHeight:1.4, display:'inline-flex', alignItems:'center', gap:6 }}>
                  <IconGift size={13}/> {plt(b.reward)}
                </div>
                {count > 0 && (
                  <button onClick={() => setDrawerBiz(b)} style={{
                    background:'rgba(13,148,136,0.12)', color:'var(--teal)',
                    border:0, borderRadius:99, padding:'9px 13px',
                    fontSize:11, fontWeight:700, letterSpacing:'.06em',
                    textTransform:'uppercase', whiteSpace:'nowrap', cursor:'pointer',
                    minHeight:36
                  }} className="pulse-soft">
                    {count === 1 ? `${t('passport_reward_pending_1')} 🎁` : `${count} ${t('passport_reward_pending_n')} 🎁`}
                  </button>
                )}
              </div>
            </div>
          );
        })}
      </div>

      {drawerBiz && ReactDOM.createPortal(
        <RewardsDrawer
          businessName={drawerBiz.name}
          rewards={localRewards.filter(r => r.business_id === drawerBiz.id)}
          onClose={() => setDrawerBiz(null)}
          userId={userId}
          onRedeemed={handleRedeemed}
        />,
        document.body
      )}
    </div>
  );
};

Object.assign(window, { PassportView });