// screens/wc.jsx — World Cup 2026 Predictor
// Three tabs: Fixtures (predict + see results), Leaderboard, My Points.
// Anonymous users get a display name prompt on first prediction.
// After 3+ predictions, signed-out users see a soft sign-up nudge.
// Admin entry point: /wc-admin (password-gated score entry).

(function () {
  const T = () => window.T;

  // ── Helpers ────────────────────────────────────────────────────────────────

  // Persistent anon identity — UUID stored in localStorage.
  const ANON_KEY = 'futball_wc_anon';
  function getAnonId() {
    // If a recovery link was used (?recover=UUID), restore that anon_id
    const params = new URLSearchParams(window.location.search);
    const recover = params.get('recover');
    if (recover && recover.match(/^[0-9a-f-]{36}$/i)) {
      localStorage.setItem(ANON_KEY, recover);
      // Clean the URL without reloading
      window.history.replaceState({}, '', window.location.pathname);
    }
    let id = localStorage.getItem(ANON_KEY);
    if (!id) { id = crypto.randomUUID(); localStorage.setItem(ANON_KEY, id); }
    return id;
  }

  const NAME_KEY = 'futball_wc_name';
  function getSavedName() { return localStorage.getItem(NAME_KEY) || ''; }
  function saveName(n) { localStorage.setItem(NAME_KEY, n); }

  function fmtKickoff(dateStr) {
    const d = new Date(dateStr);
    return d.toLocaleDateString('en-US', { weekday:'short', month:'short', day:'numeric' })
      + ' · ' + d.toLocaleTimeString('en-US', { hour:'numeric', minute:'2-digit' });
  }

  function fmtGroup(g) { return `Group ${g}`; }

  function outcomeLabel(match) {
    if (match.status !== 'completed') return null;
    const h = match.home_score, a = match.away_score;
    if (h > a) return { text: match.home_team, draw: false };
    if (h < a) return { text: match.away_team, draw: false };
    return { text: 'Draw', draw: true };
  }

  function isPredictionLocked(match) {
    if (match.status !== 'upcoming') return true;
    return Date.now() >= new Date(match.match_date).getTime();
  }

  // Group matches by date for section headers
  function groupByDate(matches) {
    const sections = [];
    let lastDate = null;
    for (const m of matches) {
      const d = new Date(m.match_date).toLocaleDateString('en-US',
        { weekday:'long', month:'long', day:'numeric' });
      if (d !== lastDate) { sections.push({ type:'header', date: d }); lastDate = d; }
      sections.push({ type:'match', match: m });
    }
    return sections;
  }

  // ── Flag mapping: team name → ISO 3166-1 alpha-2 code ────────────────────
  const FLAG_CODES = {
    'Mexico': 'mx', 'South Africa': 'za', 'Korea Republic': 'kr', 'Czechia': 'cz',
    'Canada': 'ca', 'Bosnia & Herzegovina': 'ba', 'Qatar': 'qa', 'Switzerland': 'ch',
    'Brazil': 'br', 'Morocco': 'ma', 'Haiti': 'ht', 'Scotland': 'gb-sct',
    'USA': 'us', 'Paraguay': 'py', 'Australia': 'au', 'Türkiye': 'tr',
    'Germany': 'de', 'Curaçao': 'cw', 'Ivory Coast': 'ci', 'Ecuador': 'ec',
    'Netherlands': 'nl', 'Japan': 'jp', 'Tunisia': 'tn', 'Sweden': 'se',
    'Belgium': 'be', 'Egypt': 'eg', 'Iran': 'ir', 'New Zealand': 'nz',
    'Spain': 'es', 'Cabo Verde': 'cv', 'Saudi Arabia': 'sa', 'Uruguay': 'uy',
    'France': 'fr', 'Senegal': 'sn', 'Iraq': 'iq', 'Norway': 'no',
    'Argentina': 'ar', 'Algeria': 'dz', 'Austria': 'at', 'Jordan': 'jo',
    'Portugal': 'pt', 'DR Congo': 'cd', 'Uzbekistan': 'uz', 'Colombia': 'co',
    'England': 'gb-eng', 'Croatia': 'hr', 'Ghana': 'gh', 'Panama': 'pa',
  };

  // Uses flagcdn.com's fixed-size format. Base is 64x48, 2x is 160x120 (max available).
  // Displayed at 56x42px for sharpness on retina screens.
  function TeamFlag({ team }) {
    const code = FLAG_CODES[team];
    if (!code) return (
      <div style={{
        width: 56, height: 42, borderRadius: 4,
        background: '#1F2A40', display: 'flex', alignItems: 'center',
        justifyContent: 'center', fontSize: 12, color: '#5A6A82',
      }}>?</div>
    );
    const base = `https://flagcdn.com`;
    return (
      <picture>
        <source
          type="image/webp"
          srcSet={`${base}/64x48/${code}.webp, ${base}/160x120/${code}.webp 2x`}
        />
        <source
          type="image/png"
          srcSet={`${base}/64x48/${code}.png, ${base}/160x120/${code}.png 2x`}
        />
        <img
          src={`${base}/64x48/${code}.png`}
          width="56" height="42"
          alt={team}
          loading="eager"
          decoding="sync"
          style={{ display: 'block', borderRadius: 4, boxShadow: '0 1px 4px rgba(0,0,0,0.3)' }}
        />
      </picture>
    );
  }

  // ── Score input — vertical layout: + on top, digit in middle, − on bottom ──
  function ScoreInput({ val, onChange }) {
    const TT = T();
    return (
      <div style={{ display:'flex', alignItems:'center', gap:3 }}>
        <button
          onClick={() => onChange(Math.max(0, val - 1))}
          style={{
            width:24, height:24, borderRadius:5, border:'none',
            background:'#1F2A40', color: TT.textSec,
            fontSize:14, lineHeight:1, cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center',
            flexShrink:0,
          }}>−</button>
        <div style={{
          width:34, height:34,
          display:'flex', alignItems:'center', justifyContent:'center',
          fontFamily:'Bakbak One,sans-serif', fontSize:22,
          color: TT.text, background:'#253145', borderRadius:7,
          flexShrink:0,
        }}>{val}</div>
        <button
          onClick={() => onChange(Math.min(20, val + 1))}
          style={{
            width:24, height:24, borderRadius:5, border:'none',
            background:'#1F2A40', color: TT.textSec,
            fontSize:14, lineHeight:1, cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center',
            flexShrink:0,
          }}>+</button>
      </div>
    );
  }

  // ── Match card — matches the screenshot design ──────────────────────────────
  // States:
  //   idle (no prediction, upcoming): show pickers + PREDICT button
  //   predicted (has prediction, upcoming): show saved score + UPDATE button (no pickers)
  //   editing (has prediction, clicked UPDATE): show pickers + SAVE button
  //   locked (kicked off, no prediction): show scores or dashes, no picker
  //   completed: show actual result
  function MatchCard({ match, prediction, onPredict }) {
    const TT = T();
    const locked = isPredictionLocked(match);
    const completed = match.status === 'completed';
    const hasPred = prediction != null;

    // 'idle' | 'predicted' | 'editing'
    const initialMode = hasPred ? 'predicted' : 'idle';
    const [mode, setMode] = React.useState(initialMode);
    const [ph, setPh] = React.useState(hasPred ? prediction.predicted_home : 0);
    const [pa, setPa] = React.useState(hasPred ? prediction.predicted_away : 0);
    const [saving, setSaving] = React.useState(false);

    React.useEffect(() => {
      if (hasPred) {
        setPh(prediction.predicted_home);
        setPa(prediction.predicted_away);
        setMode('predicted');
      }
    }, [prediction]);

    const handleSave = async () => {
      setSaving(true);
      try {
        await onPredict(match.id, ph, pa);
        setMode('predicted');
      } catch(e) { console.error(e); }
      finally { setSaving(false); }
    };

    // Derive outcome state for styling
    const isExact = hasPred && prediction.points_earned === 5;
    const isCorrectDraw = hasPred && prediction.points_earned === 3;
    const isCorrectOutcome = hasPred && prediction.points_earned === 2;
    const isOneRight = hasPred && prediction.points_earned === 1;
    const isWrong = hasPred && prediction.points_earned === 0;
    const isScored = hasPred && prediction.points_earned != null;

    // Card border: green for any points, red for wrong, neutral otherwise
    const cardBorder = completed && isScored
      ? isExact || isCorrectDraw || isCorrectOutcome || isOneRight
        ? '1.5px solid #2D8C4E'
        : '1.5px solid #C0392B'
      : `1px solid ${TT.border}`;

    // Per-score coloring: exact gets green, wrong prediction gets red
    const homeScoreColor = completed && isScored
      ? (prediction.predicted_home === match.home_score ? '#2ECC71' : TT.text)
      : TT.text;
    const awayScoreColor = completed && isScored
      ? (prediction.predicted_away === match.away_score ? '#2ECC71' : TT.text)
      : TT.text;

    const isEditing = (mode === 'idle' || mode === 'editing') && !locked;
    const [showDetail, setShowDetail] = React.useState(false);

    return (
      <div
        onClick={() => setShowDetail(true)}
        style={{
          background: TT.card,
          border: cardBorder,
          borderRadius: 14,
          marginBottom: 8,
          overflow: 'hidden',
          cursor: 'pointer',
        }}>
        {/* Top meta row — shows "CORRECT SCORE" when exact */}
        <div style={{
          display:'flex', justifyContent:'space-between',
          padding:'7px 12px 4px',
          fontSize:10, fontFamily:'DM Sans',
          letterSpacing:'0.06em',
        }}>
          <span style={{ color: TT.textMute }}>Group {match.group_label}</span>
          {isExact ? (
            <span style={{
              color: '#2ECC71', fontWeight: 800, letterSpacing: '0.1em',
              fontSize: 10,
            }}>CORRECT SCORE</span>
          ) : isCorrectDraw ? (
            <span style={{ color: '#2ECC71', fontWeight: 700, fontSize: 10 }}>
              CORRECT DRAW
            </span>
          ) : isCorrectOutcome ? (
            <span style={{ color: '#5BC0EB', fontWeight: 700, fontSize: 10 }}>
              CORRECT WINNER
            </span>
          ) : isOneRight ? (
            <span style={{ color: '#8BC34A', fontWeight: 700, fontSize: 10 }}>
              ONE SCORE RIGHT
            </span>
          ) : null}
          <span style={{ color: TT.textMute }}>{match.venue}</span>
        </div>


        {/* Main score row */}
        <div style={{
          display:'flex', alignItems:'center',
          padding: '4px 12px 10px',
          gap: 6,
        }}>
          {/* Home side */}
          <div style={{
            flex:1, display:'flex', flexDirection:'column',
            alignItems:'center', gap:5,
          }}>
            <TeamFlag team={match.home_team}/>
            <div style={{
              fontFamily:'DM Sans', fontSize:11, fontWeight:700,
              color: TT.text, textAlign:'center', lineHeight:1.2,
            }}>{match.home_team}</div>
          </div>

          {/* Score / picker center — stop propagation so taps here don't open detail modal */}
          <div
            onClick={e => e.stopPropagation()}
            style={{
              display:'flex', flexDirection:'column',
              alignItems:'center', gap:6, minWidth:120,
            }}>
            {completed ? (
              /* Actual result with per-digit coloring */
              <div style={{
                display:'flex', alignItems:'center', gap:8,
                fontFamily:'Bakbak One,sans-serif', fontSize:26,
                letterSpacing:'0.04em',
              }}>
                <span style={{ color: homeScoreColor }}>{match.home_score}</span>
                <span style={{ color: TT.textMute, fontSize:18 }}>–</span>
                <span style={{ color: awayScoreColor }}>{match.away_score}</span>
              </div>
            ) : isEditing ? (
              /* Score pickers — visible in idle + editing modes */
              <div style={{ display:'flex', alignItems:'center', gap:6 }}>
                <ScoreInput val={ph} onChange={setPh}/>
                <span style={{ color: TT.textMute, fontFamily:'DM Sans', fontSize:14,
                  marginTop:2 }}>–</span>
                <ScoreInput val={pa} onChange={setPa}/>
              </div>
            ) : (
              /* Locked display — show saved prediction or dashes */
              <div style={{
                display:'flex', alignItems:'center', gap:8,
                fontFamily:'Bakbak One,sans-serif', fontSize:26,
                color: TT.textSec, letterSpacing:'0.04em',
              }}>
                <span>{hasPred ? ph : '–'}</span>
                <span style={{ color: TT.textMute, fontSize:18 }}>–</span>
                <span>{hasPred ? pa : '–'}</span>
              </div>
            )}

            {/* Action button */}
            {!completed && !locked && (
              <button
                onClick={isEditing ? handleSave : () => setMode('editing')}
                disabled={saving}
                style={{
                  padding:'5px 16px', borderRadius:6, border:'none', cursor:'pointer',
                  background: isEditing
                    ? 'linear-gradient(180deg,#F5C842,#C99A10)'
                    : '#253145',
                  color: isEditing ? '#1A1200' : TT.textSec,
                  fontSize:10, fontFamily:'DM Sans', fontWeight:800,
                  letterSpacing:'0.1em', minWidth:70,
                }}>
                {saving ? '…' : isEditing ? (hasPred ? 'SAVE' : 'PREDICT') : 'UPDATE'}
              </button>
            )}
            {!completed && locked && !hasPred && (
              <div style={{ fontSize:9, color:'#E87A7A', fontFamily:'DM Sans',
                letterSpacing:'0.08em', marginTop:2 }}>LOCKED</div>
            )}
          </div>

          {/* Away side */}
          <div style={{
            flex:1, display:'flex', flexDirection:'column',
            alignItems:'center', gap:5,
          }}>
            <TeamFlag team={match.away_team}/>
            <div style={{
              fontFamily:'DM Sans', fontSize:11, fontWeight:700,
              color: TT.text, textAlign:'center', lineHeight:1.2,
            }}>{match.away_team}</div>
          </div>
        </div>

        {/* Your pick row */}
        {hasPred && (
          <div style={{
            borderTop:`1px solid ${TT.border}`,
            padding:'5px 12px',
            display:'flex', justifyContent:'space-between', alignItems:'center',
          }}>
            <div style={{ fontSize:10, color: TT.textSec, fontFamily:'DM Sans' }}>
              Your pick: <b style={{ color: TT.text }}>
                {prediction.predicted_home} – {prediction.predicted_away}
              </b>
            </div>
            {isScored && (
              <div style={{
                fontFamily:'DM Sans', fontWeight:800,
                fontSize: 11, letterSpacing:'0.06em',
                color: isExact ? TT.gold400
                  : isCorrectDraw ? '#2ECC71'
                  : isCorrectOutcome ? '#5BC0EB'
                  : isOneRight ? '#8BC34A'
                  : '#E87A7A',
              }}>
                {isExact ? '+5 POINTS'
                  : isCorrectDraw ? '+3 POINTS'
                  : isCorrectOutcome ? '+2 POINTS'
                  : isOneRight ? '+1 POINT'
                  : '+0 POINTS'}
              </div>
            )}
          </div>
        )}

        {showDetail && (
          <MatchDetailModal match={match} onClose={() => setShowDetail(false)} />
        )}
      </div>
    );
  }

  // ── Match detail modal ───────────────────────────────────────────────────────
  // Shows every user's pick (and points, if scored) for a single match.
  function MatchDetailModal({ match, onClose }) {
    const TT = T();
    const [rows, setRows] = React.useState(null);
    const [error, setError] = React.useState(null);
    const [visible, setVisible] = React.useState(false);
    const [dragY, setDragY] = React.useState(0);
    const dragStart = React.useRef(null);

    React.useEffect(() => {
      requestAnimationFrame(() => setVisible(true));
      window.fetchMatchPredictions(match.id)
        .then(setRows)
        .catch(e => setError(e.message));
    }, [match.id]);

    const dismiss = () => {
      setVisible(false);
      setTimeout(onClose, 280);
    };

    const onTouchStart = e => { dragStart.current = e.touches[0].clientY; setDragY(0); };
    const onTouchMove = e => {
      const delta = e.touches[0].clientY - dragStart.current;
      if (delta > 0) setDragY(delta);
    };
    const onTouchEnd = () => {
      if (dragY > 80) dismiss();
      else setDragY(0);
      dragStart.current = null;
    };

    const completed = match.status === 'completed';
    const scoredRows = rows ? rows.filter(r => r.points_earned != null) : [];

    const ptColor = (pts) => pts === 5 ? TT.gold400
      : pts === 3 || pts === 2 ? '#5BC0EB'
      : pts === 1 ? '#8BC34A'
      : '#E87A7A';
    const ptLabel = (pts) => pts === 5 ? '+5'
      : pts === 3 ? '+3' : pts === 2 ? '+2'
      : pts === 1 ? '+1' : '+0';

    return (
      <div
        onClick={e => { e.stopPropagation(); if (e.target === e.currentTarget) dismiss(); }}
        style={{
          position:'fixed', inset:0, zIndex:300,
          background: visible ? 'rgba(0,0,0,0.7)' : 'rgba(0,0,0,0)',
          display:'flex', alignItems:'flex-end',
          transition:'background 0.28s ease',
        }}>
        <div
          onClick={e => e.stopPropagation()}
          onTouchStart={onTouchStart}
          onTouchMove={onTouchMove}
          onTouchEnd={onTouchEnd}
          style={{
            width:'100%', maxHeight:'85vh',
            background: TT.navy950,
            borderTop:`1px solid ${TT.border}`,
            borderRadius:'20px 20px 0 0',
            display:'flex', flexDirection:'column',
            overflow:'hidden',
            transform: visible ? `translateY(${dragY}px)` : 'translateY(100%)',
            transition: dragY > 0 ? 'none' : 'transform 0.28s cubic-bezier(0.32, 0.72, 0, 1)',
          }}>
          {/* Handle */}
          <div onClick={dismiss} style={{
            display:'flex', justifyContent:'center', padding:'12px 0 6px',
            flexShrink:0, cursor:'pointer',
          }}>
            <div style={{ width:36, height:4, borderRadius:2, background:'rgba(255,255,255,0.25)' }}/>
          </div>

          {/* Header — match summary */}
          <div style={{ padding:'4px 16px 14px', borderBottom:`1px solid ${TT.border}`, flexShrink:0 }}>
            <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
              letterSpacing:'0.1em', textAlign:'center', marginBottom:8 }}>
              Group {match.group_label} · {match.venue}
            </div>
            <div style={{ display:'flex', alignItems:'center', justifyContent:'center', gap:16 }}>
              <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4, flex:1 }}>
                <TeamFlag team={match.home_team}/>
                <div style={{ fontFamily:'DM Sans', fontSize:11, fontWeight:700,
                  color: TT.text, textAlign:'center' }}>{match.home_team}</div>
              </div>
              <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:24, color: TT.text,
                display:'flex', alignItems:'center', gap:6 }}>
                {completed ? (
                  <>{match.home_score}<span style={{ color: TT.textMute, fontSize:16 }}>–</span>{match.away_score}</>
                ) : (
                  <span style={{ fontSize:12, color: TT.textMute, fontFamily:'DM Sans',
                    letterSpacing:'0.08em' }}>UPCOMING</span>
                )}
              </div>
              <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4, flex:1 }}>
                <TeamFlag team={match.away_team}/>
                <div style={{ fontFamily:'DM Sans', fontSize:11, fontWeight:700,
                  color: TT.text, textAlign:'center' }}>{match.away_team}</div>
              </div>
            </div>
          </div>

          {/* Predictions list */}
          <div className="no-scrollbar" style={{ flex:1, overflowY:'auto', padding:'12px 14px' }}>
            {!rows && !error && (
              <div style={{ textAlign:'center', padding:24, color: TT.textSec, fontSize:12 }}>Loading…</div>
            )}
            {error && (
              <div style={{ color:'#E87A7A', fontSize:12, padding:16 }}>{error}</div>
            )}
            {rows && rows.length === 0 && (
              <div style={{ textAlign:'center', padding:32, color: TT.textSec, fontSize:12 }}>
                No predictions yet for this match.
              </div>
            )}

            {/* Completed: show points, sorted highest first (from RPC) */}
            {completed && scoredRows.length > 0 && (
              <>
                <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                  letterSpacing:'0.12em', textTransform:'uppercase', marginBottom:8 }}>
                  Picks & Points ({scoredRows.length})
                </div>
                {scoredRows.map((r, i) => (
                  <div key={i} style={{
                    display:'flex', justifyContent:'space-between', alignItems:'center',
                    padding:'8px 10px', borderRadius:8,
                    background: TT.card, border:`1px solid ${TT.border}`,
                    marginBottom:6,
                  }}>
                    <div style={{ fontFamily:'DM Sans', fontSize:13, fontWeight:700, color: TT.text }}>
                      {r.display_name}
                    </div>
                    <div style={{ display:'flex', alignItems:'center', gap:10 }}>
                      <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:14, color: TT.textSec }}>
                        {r.predicted_home}–{r.predicted_away}
                      </div>
                      <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:14,
                        color: ptColor(r.points_earned), minWidth:28, textAlign:'right' }}>
                        {ptLabel(r.points_earned)}
                      </div>
                    </div>
                  </div>
                ))}
              </>
            )}

            {/* Upcoming: just show everyone's picks, no points, alphabetical */}
            {!completed && rows && rows.length > 0 && (
              <>
                <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                  letterSpacing:'0.12em', textTransform:'uppercase', marginBottom:8 }}>
                  Predictions ({rows.length})
                </div>
                {rows
                  .slice()
                  .sort((a,b) => a.display_name.localeCompare(b.display_name))
                  .map((r, i) => (
                  <div key={i} style={{
                    display:'flex', justifyContent:'space-between', alignItems:'center',
                    padding:'8px 10px', borderRadius:8,
                    background: TT.card, border:`1px solid ${TT.border}`,
                    marginBottom:6,
                  }}>
                    <div style={{ fontFamily:'DM Sans', fontSize:13, fontWeight:700, color: TT.text }}>
                      {r.display_name}
                    </div>
                    <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:14, color: TT.textSec }}>
                      {r.predicted_home}–{r.predicted_away}
                    </div>
                  </div>
                ))}
              </>
            )}

            <div style={{ height:16 }}/>
          </div>
        </div>
      </div>
    );
  }

  // ── Profile modal ─────────────────────────────────────────────────────────────
  // Shown when the person icon is tapped in standalone mode.
  // Anon: shows display name editor + sign-in prompt
  // Signed in: shows name + email, with edit + sign-out
  function WcProfileModal({ user, anonId, displayName, onNameChange, onSignedIn, onSignOut, onClose }) {
    const TT = T();
    const [view, setView] = React.useState('profile'); // profile | signin
    const [editingName, setEditingName] = React.useState(false);
    const [name, setName] = React.useState(displayName || getSavedName() || '');
    const [nameSaved, setNameSaved] = React.useState(false);

    const handleSaveName = () => {
      if (!name.trim()) return;
      onNameChange(name.trim());
      setEditingName(false);
      setNameSaved(true);
      setTimeout(() => setNameSaved(false), 2000);
    };

    return (
      <div style={{
        position:'fixed', inset:0, zIndex:200,
        background:'rgba(0,0,0,0.75)',
        display:'flex', alignItems:'center', justifyContent:'center',
        padding:'24px',
      }} onClick={e => e.target === e.currentTarget && onClose()}>
        <div style={{
          background:'#0F1C2E',
          border:`1px solid ${TT.border}`,
          borderRadius:20, padding:'24px',
          width:'100%', maxWidth:320,
          boxShadow:'0 24px 64px rgba(0,0,0,0.5)',
        }}>
          {view === 'profile' ? (
            <>
              {/* Header */}
              <div style={{ display:'flex', justifyContent:'space-between',
                alignItems:'center', marginBottom:20 }}>
                <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:16,
                  color: TT.text, letterSpacing:'0.04em' }}>
                  {user ? 'Your Account' : 'Your Profile'}
                </div>
                <button onClick={onClose} style={{
                  background:'transparent', border:'none',
                  color: TT.textMute, fontSize:22, lineHeight:1, cursor:'pointer',
                }}>×</button>
              </div>

              {/* Display name row */}
              <div style={{ marginBottom:12 }}>
                <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                  letterSpacing:'0.1em', textTransform:'uppercase', marginBottom:6 }}>
                  Display name
                </div>
                {editingName ? (
                  <div style={{ display:'flex', gap:8 }}>
                    <input
                      value={name}
                      onChange={e => setName(e.target.value)}
                      maxLength={24}
                      autoFocus
                      onKeyDown={e => e.key === 'Enter' && handleSaveName()}
                      style={{
                        flex:1, padding:'9px 12px', borderRadius:8,
                        border:`1px solid ${TT.border}`,
                        background:'#0A1828', color: TT.text,
                        fontFamily:'DM Sans', fontSize:14, outline:'none',
                      }}
                    />
                    <button onClick={handleSaveName} style={{
                      padding:'9px 14px', background: TT.gold500,
                      color:'#1A1200', border:'none', borderRadius:8,
                      cursor:'pointer', fontFamily:'DM Sans', fontWeight:700, fontSize:12,
                    }}>Save</button>
                  </div>
                ) : (
                  <div style={{ display:'flex', justifyContent:'space-between',
                    alignItems:'center',
                    padding:'10px 12px', borderRadius:8,
                    background:'#0A1828', border:`1px solid ${TT.border}`,
                  }}>
                    <span style={{ fontFamily:'DM Sans', fontSize:14, color: TT.text }}>
                      {displayName || '—'}
                    </span>
                    <button onClick={() => setEditingName(true)} style={{
                      background:'transparent', border:'none',
                      color: TT.textMute, fontSize:11, cursor:'pointer',
                      fontFamily:'DM Sans', textDecoration:'underline',
                    }}>Edit</button>
                  </div>
                )}
                {nameSaved && (
                  <div style={{ fontSize:11, color:'#2ECC71', fontFamily:'DM Sans',
                    marginTop:4 }}>✓ Name updated</div>
                )}
              </div>

              {/* Email row — signed in */}
              {user && (
                <div style={{ marginBottom:20 }}>
                  <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                    letterSpacing:'0.1em', textTransform:'uppercase', marginBottom:6 }}>
                    Email
                  </div>
                  <div style={{
                    padding:'10px 12px', borderRadius:8,
                    background:'#0A1828', border:`1px solid ${TT.border}`,
                    display:'flex', alignItems:'center', gap:8,
                  }}>
                    <div style={{ fontSize:10, width:8, height:8, borderRadius:'50%',
                      background:'#2ECC71', flexShrink:0 }}/>
                    <span style={{ fontFamily:'DM Sans', fontSize:13, color: TT.text }}>
                      {user.email}
                    </span>
                  </div>
                </div>
              )}

              {/* Anon — prompt to add email */}
              {!user && (
                <div style={{ marginBottom:16 }}>
                  <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                    letterSpacing:'0.1em', textTransform:'uppercase', marginBottom:6 }}>
                    Email
                  </div>
                  <div style={{
                    padding:'10px 12px', borderRadius:8,
                    background:'rgba(245,200,66,0.06)',
                    border:'1px solid rgba(245,200,66,0.2)',
                  }}>
                    <div style={{ fontSize:12, color: TT.textSec, fontFamily:'DM Sans',
                      lineHeight:1.5, marginBottom:8 }}>
                      Add an email to access your picks from any device.
                    </div>
                    <button onClick={() => setView('signin')} style={{
                      width:'100%', padding:'9px',
                      background:'linear-gradient(180deg,#F5C842,#C99A10)',
                      color:'#1A1200', border:'none', borderRadius:8, cursor:'pointer',
                      fontFamily:'Bakbak One,sans-serif', fontSize:12,
                      letterSpacing:'0.06em',
                    }}>ADD EMAIL / SIGN IN</button>
                  </div>
                </div>
              )}

              {/* Sign out */}
              {user && (
                <button onClick={onSignOut} style={{
                  width:'100%', padding:'10px',
                  background:'transparent',
                  border:`1px solid ${TT.border}`,
                  borderRadius:10, cursor:'pointer',
                  color: TT.textMute, fontFamily:'DM Sans', fontSize:12,
                }}>Sign out</button>
              )}
            </>
          ) : (
            <>
              {/* Back to profile */}
              <button onClick={() => setView('profile')} style={{
                background:'transparent', border:'none',
                color: TT.textSec, fontSize:12, fontFamily:'DM Sans',
                cursor:'pointer', marginBottom:16, padding:0,
              }}>‹ Back</button>
              <WcAuthModal
                embedded
                onSuccess={async () => {
                  await onSignedIn();
                }}
                onClose={() => setView('profile')}
              />
            </>
          )}
        </div>
      </div>
    );
  }

  // ── Auth modal ────────────────────────────────────────────────────────────────
  // Full-screen overlay modal for sign-in/sign-up from the standalone /wc route.
  // Works for both new and existing accounts — OTP flow treats them the same.
  function WcAuthModal({ onSuccess, onClose, embedded }) {
    const TT = T();
    const [email, setEmail] = React.useState('');
    const [step, setStep] = React.useState('email'); // email | code | success
    const [code, setCode] = React.useState('');
    const [sending, setSending] = React.useState(false);
    const [error, setError] = React.useState('');

    const sendCode = async () => {
      if (!email.trim()) return;
      setSending(true); setError('');
      try {
        await window.signInWithEmail(email.trim());
        setStep('code');
      } catch(e) {
        setError(e.message || 'Failed to send code');
      } finally { setSending(false); }
    };

    const verify = async () => {
      if (code.length < 6) return;
      setSending(true); setError('');
      try {
        const { error: err } = await window.sb.auth.verifyOtp({
          email: email.trim(), token: code, type: 'email',
        });
        if (err) throw err;

        // Migrate any anon predictions to this account
        const anonId = localStorage.getItem('futball_wc_anon');
        const { data: { user } } = await window.sb.auth.getUser();
        if (anonId && user?.id) {
          await window.migrateAnonToUser(anonId, user.id);
        }

        setStep('success');
        setTimeout(onSuccess, 1200);
      } catch(e) {
        setError('Invalid or expired code — try again');
      } finally { setSending(false); }
    };

    return embedded ? (
      <div>
        {/* Embedded inside WcProfileModal — no backdrop or card wrapper */}
        {step === 'email' && (
          <>
            <div style={{ fontSize:12, color: TT.textSec, marginBottom:12,
              fontFamily:'DM Sans', lineHeight:1.5 }}>
              Works for new and existing accounts. No password needed.
            </div>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)}
              onKeyDown={e => e.key === 'Enter' && sendCode()}
              placeholder="your@email.com" autoFocus
              style={{ width:'100%', padding:'11px 12px', borderRadius:8,
                border:`1px solid ${error ? '#E87A7A' : TT.border}`,
                background:'#0A1828', color: TT.text, fontFamily:'DM Sans',
                fontSize:14, outline:'none', marginBottom:8, boxSizing:'border-box' }}
            />
            <button onClick={sendCode} disabled={sending || !email.trim()} style={{
              width:'100%', padding:'11px',
              background: email.trim() ? 'linear-gradient(180deg,#F5C842,#C99A10)' : '#1F2A40',
              color: email.trim() ? '#1A1200' : TT.textMute,
              border:'none', borderRadius:8, cursor: email.trim() ? 'pointer' : 'default',
              fontFamily:'Bakbak One,sans-serif', fontSize:13, letterSpacing:'0.06em',
            }}>{sending ? 'Sending…' : 'Send Code'}</button>
            {error && <div style={{ color:'#E87A7A', fontSize:11, marginTop:6,
              fontFamily:'DM Sans' }}>{error}</div>}
          </>
        )}
        {step === 'code' && (
          <>
            <div style={{ fontSize:12, color: TT.textSec, marginBottom:12,
              fontFamily:'DM Sans' }}>Code sent to {email}</div>
            <input type="text" inputMode="numeric" value={code}
              onChange={e => setCode(e.target.value.replace(/\D/g,'').slice(0,8))}
              onKeyDown={e => e.key === 'Enter' && verify()}
              placeholder="Enter code" autoFocus autoComplete="one-time-code"
              style={{ width:'100%', padding:'11px 12px', borderRadius:8,
                border:`1px solid ${error ? '#E87A7A' : TT.border}`,
                background:'#0A1828', color: TT.text, fontFamily:'DM Sans',
                fontSize:22, fontWeight:700, letterSpacing:'0.2em', textAlign:'center',
                outline:'none', marginBottom:8, boxSizing:'border-box' }}
            />
            <button onClick={verify} disabled={sending || code.length < 6} style={{
              width:'100%', padding:'11px',
              background: code.length >= 6 ? 'linear-gradient(180deg,#F5C842,#C99A10)' : '#1F2A40',
              color: code.length >= 6 ? '#1A1200' : TT.textMute,
              border:'none', borderRadius:8, cursor: code.length >= 6 ? 'pointer' : 'default',
              fontFamily:'Bakbak One,sans-serif', fontSize:13, letterSpacing:'0.06em',
            }}>{sending ? 'Verifying…' : 'Verify'}</button>
            {error && <div style={{ color:'#E87A7A', fontSize:11, marginTop:6,
              fontFamily:'DM Sans' }}>{error}</div>}
            <button onClick={() => { setStep('email'); setCode(''); setError(''); }}
              style={{ display:'block', margin:'10px auto 0', background:'transparent',
                border:'none', color: TT.textMute, fontSize:11,
                fontFamily:'DM Sans', cursor:'pointer', textDecoration:'underline' }}>
              Use a different email
            </button>
          </>
        )}
        {step === 'success' && (
          <div style={{ textAlign:'center', padding:'8px 0' }}>
            <div style={{ fontSize:32, marginBottom:6 }}>⭐</div>
            <div style={{ fontFamily:'DM Sans', fontSize:12, color:'#2ECC71' }}>
              ✓ Signed in — picks saved to your account
            </div>
          </div>
        )}
      </div>
    ) : (
      <div style={{
        position:'fixed', inset:0, zIndex:200,
        background:'rgba(0,0,0,0.75)',
        display:'flex', alignItems:'center', justifyContent:'center',
        padding:'24px',
      }} onClick={e => e.target === e.currentTarget && onClose()}>
        <div style={{
          background:'#0F1C2E',
          border:`1px solid ${TT.border}`,
          borderRadius:20, padding:'28px 24px',
          width:'100%', maxWidth:340,
          boxShadow:'0 24px 64px rgba(0,0,0,0.5)',
        }}>
          {/* Header */}
          <div style={{ display:'flex', justifyContent:'space-between',
            alignItems:'flex-start', marginBottom:20 }}>
            <div>
              <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:18,
                color: TT.text, letterSpacing:'0.04em', marginBottom:4 }}>
                {step === 'success' ? '✓ Signed in' : 'Sign in'}
              </div>
              <div style={{ fontSize:12, color: TT.textSec, fontFamily:'DM Sans',
                lineHeight:1.5 }}>
                {step === 'email' && 'Save your predictions & appear on the leaderboard.'}
                {step === 'code' && `Enter the code sent to ${email}`}
                {step === 'success' && 'Your predictions are saved to your account — accessible from any device.'}
              </div>
            </div>
            <button onClick={onClose} style={{
              background:'transparent', border:'none',
              color: TT.textMute, fontSize:22, lineHeight:1,
              cursor:'pointer', padding:'0 0 0 12px', flexShrink:0,
            }}>×</button>
          </div>

          {step === 'email' && (
            <>
              <input
                type="email"
                value={email}
                onChange={e => setEmail(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && sendCode()}
                placeholder="your@email.com"
                autoFocus
                style={{
                  width:'100%', padding:'13px 14px', borderRadius:10,
                  background:'#0A1828',
                  border:`1px solid ${error ? '#E87A7A' : TT.border}`,
                  color: TT.text, fontFamily:'DM Sans', fontSize:15,
                  outline:'none', marginBottom:10, boxSizing:'border-box',
                }}
              />
              <button onClick={sendCode} disabled={sending || !email.trim()} style={{
                width:'100%', padding:'13px',
                background: email.trim()
                  ? 'linear-gradient(180deg,#F5C842,#C99A10)' : '#1F2A40',
                color: email.trim() ? '#1A1200' : TT.textMute,
                border:'none', borderRadius:10,
                cursor: email.trim() ? 'pointer' : 'default',
                fontFamily:'Bakbak One,sans-serif', fontSize:14,
                letterSpacing:'0.06em',
              }}>{sending ? 'Sending…' : 'Send Code'}</button>
              {error && <div style={{ color:'#E87A7A', fontSize:12,
                marginTop:8, fontFamily:'DM Sans' }}>{error}</div>}
              <div style={{ fontSize:11, color: TT.textMute, marginTop:12,
                fontFamily:'DM Sans', lineHeight:1.5, textAlign:'center' }}>
                Works for new and existing accounts.<br/>No password needed.
              </div>
            </>
          )}

          {step === 'code' && (
            <>
              <input
                type="text"
                inputMode="numeric"
                value={code}
                onChange={e => setCode(e.target.value.replace(/\D/g,'').slice(0,8))}
                onKeyDown={e => e.key === 'Enter' && verify()}
                placeholder="Enter code"
                autoFocus
                autoComplete="one-time-code"
                style={{
                  width:'100%', padding:'13px 14px', borderRadius:10,
                  background:'#0A1828',
                  border:`1px solid ${error ? '#E87A7A' : TT.border}`,
                  color: TT.text, fontFamily:'DM Sans', fontSize:24,
                  fontWeight:700, letterSpacing:'0.2em', textAlign:'center',
                  outline:'none', marginBottom:10, boxSizing:'border-box',
                }}
              />
              <button onClick={verify} disabled={sending || code.length < 6} style={{
                width:'100%', padding:'13px',
                background: code.length >= 6
                  ? 'linear-gradient(180deg,#F5C842,#C99A10)' : '#1F2A40',
                color: code.length >= 6 ? '#1A1200' : TT.textMute,
                border:'none', borderRadius:10,
                cursor: code.length >= 6 ? 'pointer' : 'default',
                fontFamily:'Bakbak One,sans-serif', fontSize:14,
                letterSpacing:'0.06em',
              }}>{sending ? 'Verifying…' : 'Verify'}</button>
              {error && <div style={{ color:'#E87A7A', fontSize:12,
                marginTop:8, fontFamily:'DM Sans' }}>{error}</div>}
              <button onClick={() => { setStep('email'); setCode(''); setError(''); }}
                style={{
                  display:'block', margin:'12px auto 0', background:'transparent',
                  border:'none', color: TT.textMute, fontSize:12,
                  fontFamily:'DM Sans', cursor:'pointer', textDecoration:'underline',
                }}>Use a different email</button>
            </>
          )}

          {step === 'success' && (
            <div style={{ textAlign:'center', padding:'8px 0' }}>
              <div style={{ fontSize:40, marginBottom:8 }}>⭐</div>
              <div style={{ fontFamily:'DM Sans', fontSize:13, color: TT.textSec }}>
                Redirecting…
              </div>
            </div>
          )}
        </div>
      </div>
    ); // end non-embedded
  }

  // ── Name picker modal ─────────────────────────────────────────────────────────
  function NameModal({ onConfirm }) {
    const TT = T();
    const [name, setName] = React.useState(getSavedName());
    return (
      <div style={{
        position:'fixed', inset:0, background:'rgba(0,0,0,0.7)',
        display:'flex', alignItems:'center', justifyContent:'center',
        zIndex: 100, padding:24,
      }}>
        <div style={{
          background: TT.navy800 || '#131F30',
          borderRadius:16, padding:24, width:'100%', maxWidth:320,
        }}>
          <div style={{
            fontFamily:'Bakbak One,sans-serif', fontSize:17,
            color: TT.text, marginBottom:6, letterSpacing:'0.04em',
          }}>Pick a name</div>
          <div style={{ fontSize:12, color: TT.textSec, marginBottom:16, lineHeight:1.5 }}>
            This shows on the global leaderboard. You can change it later.
          </div>
          <input
            value={name}
            onChange={e => setName(e.target.value)}
            placeholder="e.g. FootballFan99"
            maxLength={24}
            style={{
              width:'100%', padding:'10px 12px',
              borderRadius:8, border:`1px solid ${TT.border}`,
              background:'#0D1828', color: TT.text,
              fontSize:14, fontFamily:'DM Sans',
              boxSizing:'border-box', outline:'none',
              marginBottom:12,
            }}
            onKeyDown={e => e.key === 'Enter' && name.trim() && onConfirm(name.trim())}
            autoFocus
          />
          <button
            onClick={() => name.trim() && onConfirm(name.trim())}
            disabled={!name.trim()}
            style={{
              width:'100%', padding:'12px',
              background: name.trim()
                ? 'linear-gradient(180deg,#F5C842,#C99A10)' : '#1F2A40',
              color: name.trim() ? '#1A1200' : TT.textMute,
              border:'none', borderRadius:10, cursor: name.trim() ? 'pointer':'default',
              fontFamily:'Bakbak One,sans-serif', fontSize:14, letterSpacing:'0.06em',
            }}>
            LET'S GO
          </button>
        </div>
      </div>
    );
  }

  // ── Sign-up nudge banner ────────────────────────────────────────────────────
  function SignUpNudge({ onSignIn, onDismiss }) {
    const TT = T();
    return (
      <div style={{
        margin:'8px 0', padding:'10px 14px',
        background:'rgba(245,200,66,0.08)',
        border:'1px solid rgba(245,200,66,0.25)',
        borderRadius:10,
        display:'flex', alignItems:'center', gap:10,
      }}>
        <div style={{ flex:1 }}>
          <div style={{ fontSize:12, fontWeight:700, color: TT.gold400,
            fontFamily:'DM Sans', marginBottom:2 }}>
            Save your position
          </div>
          <div style={{ fontSize:11, color: TT.textSec, lineHeight:1.4 }}>
            Create an account to appear on the global leaderboard with your name.
          </div>
        </div>
        <div style={{ display:'flex', flexDirection:'column', gap:4 }}>
          <button onClick={onSignIn} style={{
            padding:'5px 10px', borderRadius:6, border:'none', cursor:'pointer',
            background: TT.gold500, color:'#1A1200',
            fontSize:10, fontFamily:'DM Sans', fontWeight:700,
            letterSpacing:'0.06em',
          }}>SIGN UP</button>
          <button onClick={onDismiss} style={{
            background:'transparent', border:'none', color: TT.textMute,
            fontSize:10, cursor:'pointer', fontFamily:'DM Sans',
          }}>Not now</button>
        </div>
      </div>
    );
  }

  // ── User picks modal ──────────────────────────────────────────────────────────
  // Shows another user's predictions in a slide-up modal.
  function UserPicksModal({ row, matches, onClose }) {
    const TT = T();
    const [preds, setPreds] = React.useState(null);
    const [error, setError] = React.useState(null);
    const [filter, setFilter] = React.useState('all');

    React.useEffect(() => {
      window.fetchUserWcPredictions(row.user_id, row.anon_id)
        .then(ps => {
          const map = {};
          ps.forEach(p => { map[p.match_id] = p; });
          setPreds(map);
        })
        .catch(e => setError(e.message));
    }, [row]);

    const filteredMatches = (matches || []).filter(m => {
      if (!preds?.[m.id]) return false;
      if (filter === 'results') return m.status === 'completed';
      if (filter === 'upcoming') return m.status !== 'completed';
      return true;
    }).sort((a, b) => new Date(a.match_date) - new Date(b.match_date));

    // Group by date like the fixtures tab
    const sections = [];
    let lastDate = null;
    for (const m of filteredMatches) {
      const d = new Date(m.match_date).toLocaleDateString('en-US',
        { weekday:'long', month:'long', day:'numeric' });
      if (d !== lastDate) { sections.push({ type:'header', date: d }); lastDate = d; }
      sections.push({ type:'match', match: m });
    }

    // Slide-up animation + swipe-to-dismiss
    const [visible, setVisible] = React.useState(false);
    const [dragY, setDragY] = React.useState(0);
    const dragStart = React.useRef(null);

    React.useEffect(() => {
      // Trigger slide-up on next frame
      requestAnimationFrame(() => setVisible(true));
    }, []);

    const dismiss = () => {
      setVisible(false);
      setTimeout(onClose, 280);
    };

    const onTouchStart = e => {
      dragStart.current = e.touches[0].clientY;
      setDragY(0);
    };
    const onTouchMove = e => {
      const delta = e.touches[0].clientY - dragStart.current;
      if (delta > 0) setDragY(delta);
    };
    const onTouchEnd = () => {
      if (dragY > 80) { dismiss(); }
      else setDragY(0);
      dragStart.current = null;
    };

    return (
      <div style={{
        position:'fixed', inset:0, zIndex:300,
        background: visible ? 'rgba(0,0,0,0.7)' : 'rgba(0,0,0,0)',
        display:'flex', alignItems:'flex-end',
        transition:'background 0.28s ease',
      }} onClick={e => e.target === e.currentTarget && dismiss()}>
        <div
          onTouchStart={onTouchStart}
          onTouchMove={onTouchMove}
          onTouchEnd={onTouchEnd}
          style={{
            width:'100%', maxHeight:'88vh',
            background: TT.navy950,
            borderTop:`1px solid ${TT.border}`,
            borderRadius:'20px 20px 0 0',
            display:'flex', flexDirection:'column',
            overflow:'hidden',
            transform: visible
              ? `translateY(${dragY}px)`
              : 'translateY(100%)',
            transition: dragY > 0
              ? 'none'
              : 'transform 0.28s cubic-bezier(0.32, 0.72, 0, 1)',
          }}>
          {/* Handle — tap to dismiss */}
          <div
            onClick={dismiss}
            style={{
              display:'flex', justifyContent:'center',
              padding:'12px 0 6px', flexShrink:0, cursor:'pointer',
            }}>
            <div style={{
              width:36, height:4, borderRadius:2,
              background:'rgba(255,255,255,0.25)',
            }}/>
          </div>

          {/* Header */}
          <div style={{ padding:'4px 16px 12px', borderBottom:`1px solid ${TT.border}`, flexShrink:0 }}>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start' }}>
              <div>
                <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:16,
                  color: TT.text, letterSpacing:'0.03em' }}>{row.display_name}</div>
                <div style={{ fontSize:10, color: TT.textMute, fontFamily:'DM Sans', marginTop:1 }}>
                  #{row.rank} · {row.total_predictions} predictions
                </div>
              </div>
              <div style={{ textAlign:'right' }}>
                <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:20,
                  color: TT.gold400 }}>{row.total_points} pts</div>
                <div style={{ fontSize:10, color: TT.textSec, fontFamily:'DM Sans' }}>
                  {row.exact_scores} ⭐ · {row.correct_outcomes} ✓
                </div>
              </div>
            </div>

            {/* Filter pills */}
            <div style={{ display:'flex', gap:6, marginTop:10 }}>
              {[['all','All'],['results','Results'],['upcoming','Upcoming']].map(([key,label]) => (
                <button key={key} onClick={() => setFilter(key)} style={{
                  padding:'3px 10px', borderRadius:20, border:'none', cursor:'pointer',
                  background: filter === key ? TT.gold500 : '#1F2A40',
                  color: filter === key ? '#1A1200' : TT.textSec,
                  fontSize:11, fontFamily:'DM Sans', fontWeight:600,
                }}>{label}</button>
              ))}
            </div>
          </div>

          {/* Match cards */}
          <div className="no-scrollbar" style={{ flex:1, overflowY:'auto', padding:'10px 12px' }}>
            {!preds && !error && (
              <div style={{ textAlign:'center', padding:24, color: TT.textSec, fontSize:12 }}>Loading…</div>
            )}
            {error && (
              <div style={{ color:'#E87A7A', fontSize:12, padding:16 }}>{error}</div>
            )}

            {preds && sections.map((s, i) => {
              if (s.type === 'header') return (
                <div key={`h-${i}-${s.date}`} style={{
                  fontSize:10, color: TT.textMute, fontFamily:'DM Sans',
                  letterSpacing:'0.12em', textTransform:'uppercase',
                  marginTop: i === 0 ? 0 : 10, marginBottom:6,
                }}>{s.date}</div>
              );

              const m = s.match;
              const p = preds[m.id];
              const completed = m.status === 'completed';
              const isExact = p.points_earned === 5;
              const isDraw = p.points_earned === 3;
              const isCorrect = p.points_earned === 2;
              const isOne = p.points_earned === 1;
              const isScored = p.points_earned != null;

              const cardBorder = completed && isScored
                ? (isExact || isDraw || isCorrect || isOne)
                  ? '1.5px solid #2D8C4E' : '1.5px solid #C0392B'
                : `1px solid ${TT.border}`;

              const ptColor = isExact ? TT.gold400 : isDraw || isCorrect ? '#5BC0EB'
                : isOne ? '#8BC34A' : '#E87A7A';
              const ptLabel = isExact ? '+5 POINTS' : isDraw ? '+3 POINTS'
                : isCorrect ? '+2 POINTS' : isOne ? '+1 POINT' : '+0 POINTS';

              const homeScoreColor = completed && isScored
                ? p.predicted_home === m.home_score ? '#2ECC71' : TT.text : TT.text;
              const awayScoreColor = completed && isScored
                ? p.predicted_away === m.away_score ? '#2ECC71' : TT.text : TT.text;

              return (
                <div key={`m-${m.id}`} style={{
                  background: TT.card, border: cardBorder,
                  borderRadius:14, marginBottom:8, overflow:'hidden',
                }}>
                  {/* Top meta */}
                  <div style={{
                    display:'flex', justifyContent:'space-between',
                    padding:'7px 12px 4px',
                    fontSize:10, fontFamily:'DM Sans',
                  }}>
                    <span style={{ color: TT.textMute }}>Group {m.group_label}</span>
                    {isExact ? (
                      <span style={{ color:'#2ECC71', fontWeight:800, letterSpacing:'0.1em', fontSize:9 }}>CORRECT SCORE</span>
                    ) : isDraw ? (
                      <span style={{ color:'#2ECC71', fontWeight:700, fontSize:9 }}>CORRECT DRAW</span>
                    ) : isCorrect ? (
                      <span style={{ color:'#5BC0EB', fontWeight:700, fontSize:9 }}>CORRECT WINNER</span>
                    ) : isOne ? (
                      <span style={{ color:'#8BC34A', fontWeight:700, fontSize:9 }}>ONE SCORE RIGHT</span>
                    ) : null}
                    <span style={{ color: TT.textMute }}>{m.venue}</span>
                  </div>

                  {/* Flags + score row */}
                  <div style={{ display:'flex', alignItems:'center', padding:'4px 12px 10px', gap:6 }}>
                    {/* Home */}
                    <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:4 }}>
                      <TeamFlag team={m.home_team}/>
                      <div style={{ fontFamily:'DM Sans', fontSize:11, fontWeight:700,
                        color: TT.text, textAlign:'center', lineHeight:1.2 }}>{m.home_team}</div>
                    </div>

                    {/* Score */}
                    <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:4, minWidth:80 }}>
                      {completed ? (
                        <div style={{ display:'flex', alignItems:'center', gap:8,
                          fontFamily:'Bakbak One,sans-serif', fontSize:26, letterSpacing:'0.04em' }}>
                          <span style={{ color: homeScoreColor }}>{m.home_score}</span>
                          <span style={{ color: TT.textMute, fontSize:18 }}>–</span>
                          <span style={{ color: awayScoreColor }}>{m.away_score}</span>
                        </div>
                      ) : (
                        <div style={{ display:'flex', alignItems:'center', gap:8,
                          fontFamily:'Bakbak One,sans-serif', fontSize:26,
                          color: TT.textSec, letterSpacing:'0.04em' }}>
                          <span>{p.predicted_home}</span>
                          <span style={{ color: TT.textMute, fontSize:18 }}>–</span>
                          <span>{p.predicted_away}</span>
                        </div>
                      )}
                    </div>

                    {/* Away */}
                    <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:4 }}>
                      <TeamFlag team={m.away_team}/>
                      <div style={{ fontFamily:'DM Sans', fontSize:11, fontWeight:700,
                        color: TT.text, textAlign:'center', lineHeight:1.2 }}>{m.away_team}</div>
                    </div>
                  </div>

                  {/* Their pick row */}
                  {completed && (
                    <div style={{
                      borderTop:`1px solid ${TT.border}`, padding:'5px 12px',
                      display:'flex', justifyContent:'space-between', alignItems:'center',
                    }}>
                      <div style={{ fontSize:10, color: TT.textSec, fontFamily:'DM Sans' }}>
                        Their pick: <b style={{ color: TT.text }}>{p.predicted_home} – {p.predicted_away}</b>
                      </div>
                      {isScored && (
                        <div style={{ fontSize:10, fontFamily:'DM Sans', fontWeight:800,
                          color: ptColor, letterSpacing:'0.06em' }}>{ptLabel}</div>
                      )}
                    </div>
                  )}
                </div>
              );
            })}

            {preds && sections.length === 0 && (
              <div style={{ textAlign:'center', padding:32, color: TT.textSec, fontSize:12 }}>
                No predictions in this filter.
              </div>
            )}
            <div style={{ height:16 }}/>
          </div>
        </div>
      </div>
    );
  }

  // ── Leaderboard tab ─────────────────────────────────────────────────────────
  function LeaderboardTab({ myAnonId, matches }) {
    const TT = T();
    const [rows, setRows] = React.useState(null);
    const [myRank, setMyRank] = React.useState(null);
    const [error, setError] = React.useState(null);
    const [viewing, setViewing] = React.useState(null); // leaderboard row being peeked

    React.useEffect(() => {
      let cancelled = false;
      (async () => {
        try {
          const [lb, me] = await Promise.all([
            window.getWcLeaderboard(50),
            window.getMyWcRank(myAnonId),
          ]);
          if (!cancelled) { setRows(lb); setMyRank(me); }
        } catch(e) {
          if (!cancelled) setError(e.message);
        }
      })();
      return () => { cancelled = true; };
    }, [myAnonId]);

    if (error) return (
      <div style={{ padding:24, textAlign:'center', color:'#E87A7A', fontSize:12 }}>
        {error}
      </div>
    );

    if (!rows) return (
      <div style={{ padding:24, textAlign:'center', color: TT.textSec, fontSize:12 }}>
        Loading…
      </div>
    );

    if (rows.length === 0) return (
      <div style={{ padding:32, textAlign:'center' }}>
        <div style={{ fontSize:28, marginBottom:8 }}>🏆</div>
        <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:15,
          color: TT.text, marginBottom:4 }}>No predictions yet</div>
        <div style={{ fontSize:12, color: TT.textSec }}>
          Be the first on the board — predict a match!
        </div>
      </div>
    );

    const isMe = (row) => (row.anon_id && row.anon_id === myAnonId);

    return (
      <div>
        {myRank && (
          <div style={{
            margin:'0 0 12px', padding:'10px 14px',
            background:'rgba(245,200,66,0.08)',
            border:'1px solid rgba(245,200,66,0.25)',
            borderRadius:10,
            display:'flex', justifyContent:'space-between', alignItems:'center',
          }}>
            <div>
              <div style={{ fontSize:10, color: TT.gold400, letterSpacing:'0.1em',
                fontFamily:'DM Sans', textTransform:'uppercase' }}>Your rank</div>
              <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:22,
                color: TT.text }}>#{myRank.rank}</div>
            </div>
            <div style={{ textAlign:'right' }}>
              <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:22,
                color: TT.gold400 }}>{myRank.total_points} pts</div>
              <div style={{ fontSize:10, color: TT.textSec }}>
                {myRank.exact_scores} exact · {myRank.correct_outcomes} outcomes
              </div>
            </div>
          </div>
        )}

        {rows.map((row, i) => (
          <div key={i}
            onClick={() => setViewing(row)}
            style={{
              display:'flex', alignItems:'center', gap:10,
              padding:'10px 6px',
              borderBottom:`1px solid ${TT.border}`,
              background: isMe(row) ? 'rgba(245,200,66,0.05)' : 'transparent',
              borderRadius: isMe(row) ? 6 : 0,
              cursor:'pointer',
            }}>
            <div style={{
              width:28, textAlign:'center',
              fontFamily:'Bakbak One,sans-serif', fontSize:14,
              color: i < 3 ? TT.gold400 : TT.textMute,
            }}>#{row.rank}</div>
            <div style={{ flex:1 }}>
              <div style={{ fontFamily:'DM Sans', fontSize:13, fontWeight:700,
                color: isMe(row) ? TT.gold400 : TT.text }}>
                {row.display_name}{isMe(row) ? ' (you)' : ''}
              </div>
              <div style={{ fontSize:10, color: TT.textSec }}>
                {row.exact_scores} exact · {row.correct_outcomes} correct outcomes
              </div>
            </div>
            <div style={{ display:'flex', alignItems:'center', gap:8 }}>
              <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:16,
                color: TT.text }}>{row.total_points}</div>
              <div style={{ color: TT.textMute, fontSize:12 }}>›</div>
            </div>
          </div>
        ))}

        {/* User picks modal */}
        {viewing && (
          <UserPicksModal
            row={viewing}
            matches={matches}
            onClose={() => setViewing(null)}
          />
        )}
      </div>
    );
  }

  // ── Push notification nudge ───────────────────────────────────────────────
  // Shown after first prediction. Asks for permission to send result alerts.
  function PushNudge({ anonId, onDismiss }) {
    const TT = T();
    const [state, setState] = React.useState('idle'); // idle | requesting | granted | denied

    const handleEnable = async () => {
      setState('requesting');
      const result = await window.subscribeToPush(anonId);
      if (result.ok) {
        setState('granted');
        setTimeout(onDismiss, 2000);
      } else if (result.reason === 'denied') {
        setState('denied');
      } else {
        onDismiss();
      }
    };

    if (state === 'granted') return (
      <div style={{
        margin:'0 0 10px', padding:'10px 14px',
        background:'rgba(46,204,113,0.1)', border:'1px solid rgba(46,204,113,0.3)',
        borderRadius:10, fontSize:12, color:'#2ECC71', fontFamily:'DM Sans',
        textAlign:'center',
      }}>
        ✓ You'll get notified when results come in
      </div>
    );

    if (state === 'denied') return (
      <div style={{
        margin:'0 0 10px', padding:'10px 14px',
        background:'rgba(232,122,122,0.08)', border:'1px solid rgba(232,122,122,0.2)',
        borderRadius:10, fontSize:11, color: TT.textSec, fontFamily:'DM Sans',
      }}>
        Notifications blocked — enable them in your device settings to get result alerts.
        <button onClick={onDismiss} style={{
          float:'right', background:'transparent', border:'none',
          color: TT.textMute, cursor:'pointer', fontSize:14, lineHeight:1,
        }}>×</button>
      </div>
    );

    return (
      <div style={{
        margin:'0 0 10px', padding:'10px 14px',
        background:'rgba(245,200,66,0.06)',
        border:'1px solid rgba(245,200,66,0.2)',
        borderRadius:10,
        display:'flex', alignItems:'center', gap:10,
      }}>
        <div style={{ flex:1 }}>
          <div style={{ fontSize:12, fontWeight:700, color: TT.text,
            fontFamily:'DM Sans', marginBottom:2 }}>
            Get result alerts
          </div>
          <div style={{ fontSize:11, color: TT.textSec, lineHeight:1.4 }}>
            We'll notify you when match scores are in.
          </div>
        </div>
        <div style={{ display:'flex', flexDirection:'column', gap:4 }}>
          <button onClick={handleEnable} disabled={state === 'requesting'} style={{
            padding:'5px 10px', borderRadius:6, border:'none', cursor:'pointer',
            background: TT.gold500, color:'#1A1200',
            fontSize:10, fontFamily:'DM Sans', fontWeight:700,
            letterSpacing:'0.06em', opacity: state === 'requesting' ? 0.6 : 1,
          }}>{state === 'requesting' ? '…' : 'ENABLE'}</button>
          <button onClick={onDismiss} style={{
            background:'transparent', border:'none', color: TT.textMute,
            fontSize:10, cursor:'pointer', fontFamily:'DM Sans',
          }}>Not now</button>
        </div>
      </div>
    );
  }

  // ── Rules tab ────────────────────────────────────────────────────────────────
  function RulesTab() {
    const TT = T();

    const Rule = ({ pts, color, icon, title, desc }) => (
      <div style={{
        display:'flex', gap:14, alignItems:'flex-start',
        padding:'14px 0',
        borderBottom:`1px solid ${TT.border}`,
      }}>
        <div style={{
          minWidth:48, height:48, borderRadius:12,
          background: color + '18',
          border:`1px solid ${color}40`,
          display:'flex', flexDirection:'column',
          alignItems:'center', justifyContent:'center',
        }}>
          <div style={{ fontSize:16 }}>{icon}</div>
          <div style={{
            fontFamily:'Bakbak One,sans-serif', fontSize:13,
            color, letterSpacing:'0.02em', lineHeight:1,
          }}>{pts}</div>
        </div>
        <div>
          <div style={{ fontFamily:'DM Sans', fontSize:13, fontWeight:700,
            color: TT.text, marginBottom:3 }}>{title}</div>
          <div style={{ fontFamily:'DM Sans', fontSize:12, color: TT.textSec,
            lineHeight:1.5 }}>{desc}</div>
        </div>
      </div>
    );

    return (
      <div>
        {/* Hero */}
        <div style={{
          background:'linear-gradient(135deg,#0A3060,#05193A)',
          border:`1px solid rgba(245,200,66,0.2)`,
          borderRadius:14, padding:'16px 14px', marginBottom:16,
          textAlign:'center',
        }}>
          <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:18,
            color: TT.text, letterSpacing:'0.04em', marginBottom:4 }}>
            HOW TO PLAY
          </div>
          <div style={{ fontFamily:'DM Sans', fontSize:12, color: TT.textSec,
            lineHeight:1.6 }}>
            Predict the score for every group stage match.
            Points for getting the outcome right, bonus for nailing the exact score.
            Top the global leaderboard by the end of the group stage.
          </div>
        </div>

        {/* Scoring */}
        <div style={{ fontFamily:'DM Sans', fontSize:10, color: TT.textMute,
          letterSpacing:'0.12em', textTransform:'uppercase', marginBottom:4 }}>
          Scoring
        </div>

        <Rule
          pts="+5" color={TT.gold400} icon="⭐"
          title="Exact Score"
          desc="You called both goals perfectly — the ultimate prediction."
        />
        <Rule
          pts="+3" color="#2ECC71" icon="="
          title="Correct Draw"
          desc="You predicted a draw and it ended level. Draws are hard to call — rewarded."
        />
        <Rule
          pts="+2" color="#5BC0EB" icon="✓"
          title="Correct Winner"
          desc="You got the winning team right but the scoreline was different."
        />
        <Rule
          pts="+1" color="#8BC34A" icon="½"
          title="One Score Right"
          desc="You got one team's exact goal tally but missed the result."
        />
        <Rule
          pts="+0" color="#E87A7A" icon="✗"
          title="Wrong Result"
          desc="The game went the other way. Better luck next match."
        />

        {/* Locking */}
        <div style={{ fontFamily:'DM Sans', fontSize:10, color: TT.textMute,
          letterSpacing:'0.12em', textTransform:'uppercase',
          marginTop:20, marginBottom:4 }}>
          Deadlines
        </div>
        <div style={{
          background: TT.card, border:`1px solid ${TT.border}`,
          borderRadius:12, padding:'12px 14px',
        }}>
          {[
            ['🔒', 'Predictions lock', 'at kick-off — no changes after the whistle.'],
            ['📅', '72 matches', 'across all 12 groups, June 11–27.'],
            ['🏆', 'Leaderboard', 'updates live as results come in.'],
            ['👤', 'No account needed', 'to predict — just pick a display name.'],
          ].map(([icon, bold, rest], i) => (
            <div key={i} style={{
              display:'flex', gap:10, alignItems:'flex-start',
              paddingBottom: i < 3 ? 10 : 0,
              marginBottom: i < 3 ? 10 : 0,
              borderBottom: i < 3 ? `1px solid ${TT.border}` : 'none',
            }}>
              <span style={{ fontSize:16, lineHeight:1.3 }}>{icon}</span>
              <div style={{ fontFamily:'DM Sans', fontSize:12,
                color: TT.textSec, lineHeight:1.5 }}>
                <b style={{ color: TT.text }}>{bold}</b> {rest}
              </div>
            </div>
          ))}
        </div>

        {/* Tips */}
        <div style={{ fontFamily:'DM Sans', fontSize:10, color: TT.textMute,
          letterSpacing:'0.12em', textTransform:'uppercase',
          marginTop:20, marginBottom:4 }}>
          Tips
        </div>
        <div style={{
          background: TT.card, border:`1px solid ${TT.border}`,
          borderRadius:12, padding:'12px 14px',
          fontFamily:'DM Sans', fontSize:12, color: TT.textSec, lineHeight:1.7,
        }}>
          Exact scores in international football are typically low — 1-0, 1-1, and 2-1
          are the most common results. Big group stage upsets do happen.
          Don't sleep on the draws.
        </div>

        <div style={{ height:24 }}/>
      </div>
    );
  }

  // ── Main predictor screen ───────────────────────────────────────────────────
  function WcPredictor({ onExit, standalone }) {
    const TT = T();
    const anonId = React.useMemo(() => getAnonId(), []);

    const [tab, setTab] = React.useState('fixtures');
    const [matches, setMatches] = React.useState(null);
    const [preds, setPreds] = React.useState({});
    const [loadErr, setLoadErr] = React.useState(null);
    const [user, setUser] = React.useState(undefined);
    const [displayName, setDisplayName] = React.useState(getSavedName());
    const [showNameModal, setShowNameModal] = React.useState(false);
    const [showNudge, setShowNudge] = React.useState(false);
    const [nudgeDismissed, setNudgeDismissed] = React.useState(false);
    const [showPushNudge, setShowPushNudge] = React.useState(false);
    const [pendingPredict, setPendingPredict] = React.useState(null);
    const [filter, setFilter] = React.useState('all');
    const [pushState, setPushState] = React.useState('default'); // default|subscribed|denied|unsupported
    const [showProfile, setShowProfile] = React.useState(false);

    // Load user
    React.useEffect(() => {
      window.getCurrentUser().then(u => setUser(u || null));
      // Check push state for header icon
      window.getPushState && window.getPushState().then(s => setPushState(s));
    }, []);

    // Load matches + my predictions
    React.useEffect(() => {
      let cancelled = false;
      (async () => {
        try {
          const [ms, ps] = await Promise.all([
            window.fetchWcMatches(),
            window.fetchMyWcPredictions(anonId),
          ]);
          if (cancelled) return;
          setMatches(ms);
          const predMap = {};
          ps.forEach(p => { predMap[p.match_id] = p; });
          setPreds(predMap);
          if (!user && ps.length >= 3 && !nudgeDismissed) setShowNudge(true);

          // Sync display name from DB — ground truth over localStorage
          if (ps.length > 0 && ps[0].display_name) {
            setDisplayName(ps[0].display_name);
            saveName(ps[0].display_name);
          }

          // Preload flags for the first 10 matches so they're ready on first paint
          const base = 'https://flagcdn.com';
          const teams = new Set();
          ms.slice(0, 10).forEach(m => { teams.add(m.home_team); teams.add(m.away_team); });
          teams.forEach(team => {
            const code = FLAG_CODES[team];
            if (!code) return;
            const img = new Image();
            img.src = `${base}/64x48/${code}.png`;
          });
        } catch(e) {
          if (!cancelled) setLoadErr(e.message);
        }
      })();
      return () => { cancelled = true; };
    }, [anonId, user]);

    const handlePredict = async (matchId, ph, pa) => {
      // Need a display name first
      if (!displayName) {
        setPendingPredict({ matchId, ph, pa });
        setShowNameModal(true);
        return;
      }
      const result = await window.upsertWcPrediction({
        matchId, predictedHome: ph, predictedAway: pa,
        displayName, anonId,
      });
      if (!result?.ok) throw new Error(result?.error || 'Failed to save');
      // Optimistic update
      setPreds(prev => ({
        ...prev,
        [matchId]: { ...prev[matchId], match_id: matchId,
          predicted_home: ph, predicted_away: pa, display_name: displayName },
      }));
      // Check nudge thresholds
      const newPredCount = Object.keys(preds).length + (preds[matchId] ? 0 : 1);
      if (!user && newPredCount >= 3 && !nudgeDismissed) setShowNudge(true);
      // Push nudge: show after very first prediction if not already subscribed
      if (newPredCount === 1) {
        window.getPushState && window.getPushState().then(state => {
          if (state === 'default') setShowPushNudge(true);
        });
      }
    };

    const handleNameConfirm = async (name) => {
      saveName(name);
      setDisplayName(name);
      setShowNameModal(false);
      if (pendingPredict) {
        const { matchId, ph, pa } = pendingPredict;
        setPendingPredict(null);
        await handlePredict(matchId, ph, pa);
      }
    };

    const filteredMatches = (matches || [])
      .filter(m =>
        filter === 'all' ? true
        : filter === 'upcoming' ? m.status === 'upcoming'
        : m.status === 'completed'
      )
      .sort((a, b) => new Date(a.match_date) - new Date(b.match_date));

    const sections = groupByDate(filteredMatches);

    const totalPoints = Object.values(preds)
      .reduce((s, p) => s + (p.points_earned || 0), 0);
    const predCount = Object.keys(preds).length;

    return (
      <div style={{ position:'absolute', inset:0, background: TT.navy950,
        display:'flex', flexDirection:'column' }}>

        {/* Header */}
        <div style={{
          padding:'12px 16px 10px',
          display:'flex', justifyContent:'space-between', alignItems:'center',
          borderBottom:`1px solid ${TT.border}`,
        }}>
          {/* Left — back button or spacer */}
          {standalone ? (
            <div style={{ width:24 }}/>
          ) : (
            <button onClick={onExit} style={{
              background:'transparent', border:'none', color: TT.text,
              fontSize:22, lineHeight:1, cursor:'pointer',
            }}>‹</button>
          )}

          {/* Centre — title + pts */}
          <div style={{ flex:1, textAlign:'center' }}>
            <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:15,
              color: TT.text, letterSpacing:'0.04em' }}>
              WC 2026 PREDICTOR
            </div>
            {predCount > 0 && (
              <div style={{ fontSize:10, color: TT.gold400,
                fontFamily:'DM Sans', letterSpacing:'0.1em' }}>
                {totalPoints} PTS · {predCount} PREDICTIONS
              </div>
            )}
          </div>

          {/* Right — standalone action icons */}
          {standalone ? (
            <div style={{ display:'flex', gap:8, alignItems:'center' }}>
              {/* Person icon — opens profile modal */}
              <button
                onClick={() => setShowProfile(s => !s)}
                title={user ? `Account: ${user.email}` : 'Profile & sign in'}
                style={{
                  width:32, height:32, borderRadius:8,
                  background: user ? 'rgba(245,200,66,0.12)' : '#1F2A40',
                  border: user ? '1px solid rgba(245,200,66,0.3)' : `1px solid ${TT.border}`,
                  cursor:'pointer', display:'flex',
                  alignItems:'center', justifyContent:'center',
                }}>
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <circle cx="8" cy="5.5" r="2.5"
                    stroke={user ? '#F5C842' : TT.textSec} strokeWidth="1.5"/>
                  <path d="M2.5 13.5c0-3.038 2.462-5.5 5.5-5.5s5.5 2.462 5.5 5.5"
                    stroke={user ? '#F5C842' : TT.textSec}
                    strokeWidth="1.5" strokeLinecap="round" fill="none"/>
                </svg>
              </button>

              {/* Notification bell icon */}
              <button
                onClick={async () => {
                  if (pushState === 'subscribed') {
                    if (window.confirm('Turn off match notifications?')) {
                      await window.unsubscribeFromPush();
                      setPushState('default');
                    }
                    return;
                  }
                  if (pushState === 'denied' || pushState === 'unsupported') return;
                  setPushState('requesting');
                  const result = await window.subscribeToPush(anonId);
                  if (result.ok) setPushState('subscribed');
                  else if (result.reason === 'denied') setPushState('denied');
                  else setPushState('default');
                }}
                title={
                  pushState === 'subscribed' ? 'Notifications on — tap to turn off'
                  : pushState === 'denied' ? 'Notifications blocked in settings'
                  : pushState === 'unsupported' ? 'Notifications not supported'
                  : pushState === 'requesting' ? 'Enabling…'
                  : 'Enable match notifications'
                }
                disabled={pushState === 'denied' || pushState === 'unsupported' || pushState === 'requesting'}
                style={{
                  width:32, height:32, borderRadius:8,
                  background: pushState === 'subscribed' ? 'rgba(46,204,113,0.12)'
                    : pushState === 'requesting' ? 'rgba(245,200,66,0.08)'
                    : '#1F2A40',
                  border: pushState === 'subscribed' ? '1px solid rgba(46,204,113,0.3)'
                    : pushState === 'denied' ? '1px solid rgba(232,122,122,0.3)'
                    : pushState === 'requesting' ? '1px solid rgba(245,200,66,0.3)'
                    : `1px solid ${TT.border}`,
                  cursor: (pushState === 'denied' || pushState === 'unsupported' || pushState === 'requesting') ? 'default' : 'pointer',
                  display:'flex', alignItems:'center', justifyContent:'center',
                  opacity: pushState === 'unsupported' ? 0.4 : 1,
                }}>
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                  <path d="M8 1.5A4.5 4.5 0 003.5 6v3.5L2 11h12l-1.5-1.5V6A4.5 4.5 0 008 1.5z"
                    stroke={
                      pushState === 'subscribed' ? '#2ECC71'
                      : pushState === 'denied' ? '#E87A7A'
                      : pushState === 'requesting' ? '#F5C842'
                      : TT.textSec
                    }
                    strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                  <path d="M6.5 11.5a1.5 1.5 0 003 0"
                    stroke={
                      pushState === 'subscribed' ? '#2ECC71'
                      : pushState === 'denied' ? '#E87A7A'
                      : pushState === 'requesting' ? '#F5C842'
                      : TT.textSec
                    }
                    strokeWidth="1.5" strokeLinecap="round"/>
                  {pushState === 'subscribed' && (
                    <circle cx="12" cy="3.5" r="2" fill="#2ECC71"/>
                  )}
                  {pushState === 'requesting' && (
                    <circle cx="12" cy="3.5" r="2" fill="#F5C842" opacity="0.6"/>
                  )}
                </svg>
              </button>
            </div>
          ) : (
            <div style={{ width:24 }}/>
          )}
        </div>

        {/* Sign-in modal */}
        {standalone && showProfile && (
          <WcProfileModal
            user={user}
            anonId={anonId}
            displayName={displayName}
            onNameChange={async name => {
              saveName(name);
              setDisplayName(name);
              // Update display_name on all existing predictions in DB
              try {
                const u = await window.getCurrentUser();
                if (u) {
                  await window.sb.from('wc_predictions')
                    .update({ display_name: name })
                    .eq('user_id', u.id);
                } else {
                  await window.sb.from('wc_predictions')
                    .update({ display_name: name })
                    .eq('anon_id', anonId);
                }
              } catch(e) { console.warn('Name update failed:', e.message); }
            }}
            onSignedIn={async () => {
              const u = await window.getCurrentUser();
              setUser(u || null);
              setShowProfile(false);
            }}
            onSignOut={async () => {
              await window.signOut();
              setUser(null);
              setShowProfile(false);
            }}
            onClose={() => setShowProfile(false)}
          />
        )}

        {/* Tabs */}
        <div style={{
          display:'flex', borderBottom:`1px solid ${TT.border}`,
          background: TT.navy950,
        }}>
          {[['fixtures','Fixtures'],['leaderboard','Leaderboard'],['rules','Rules']].map(([key,label]) => (
            <button key={key} onClick={() => setTab(key)} style={{
              flex:1, padding:'10px 0',
              background:'transparent', border:'none',
              color: tab === key ? TT.gold400 : TT.textSec,
              fontFamily:'DM Sans', fontSize:12, fontWeight:700,
              letterSpacing:'0.06em', cursor:'pointer',
              borderBottom: tab === key ? `2px solid ${TT.gold400}` : '2px solid transparent',
              marginBottom:-1,
            }}>{label}</button>
          ))}
        </div>

        {/* Content */}
        <div className="no-scrollbar" style={{ flex:1, overflowY:'auto', padding:'12px 14px' }}>

          {tab === 'fixtures' && (
            <>
              {showNudge && !nudgeDismissed && !user && (
                <SignUpNudge
                  onSignIn={() => onExit && onExit('signin')}
                  onDismiss={() => { setShowNudge(false); setNudgeDismissed(true); }}
                />
              )}

              {/* Push notification nudge — shown after first prediction */}
              {showPushNudge && (
                <PushNudge
                  anonId={anonId}
                  onDismiss={() => setShowPushNudge(false)}
                />
              )}

              {/* Filter pills */}
              <div style={{ display:'flex', gap:6, marginBottom:10 }}>
                {[['all','All'],['upcoming','Upcoming'],['completed','Results']].map(([key,label]) => (
                  <button key={key} onClick={() => setFilter(key)} style={{
                    padding:'4px 10px', borderRadius:20, border:'none', cursor:'pointer',
                    background: filter === key ? TT.gold500 : '#1F2A40',
                    color: filter === key ? '#1A1200' : TT.textSec,
                    fontSize:11, fontFamily:'DM Sans', fontWeight:600,
                  }}>{label}</button>
                ))}
              </div>

              {loadErr && (
                <div style={{ color:'#E87A7A', fontSize:12, marginBottom:10 }}>{loadErr}</div>
              )}

              {!matches && !loadErr && (
                <div style={{ color: TT.textSec, fontSize:12, textAlign:'center',
                  padding:24 }}>Loading matches…</div>
              )}

              {sections.map((s, i) => s.type === 'header' ? (
                <div key={`h-${i}-${s.date}`} style={{
                  fontSize:10, color: TT.textMute,
                  fontFamily:'DM Sans', letterSpacing:'0.12em',
                  textTransform:'uppercase', marginTop: i === 0 ? 0 : 12, marginBottom:6,
                }}>{s.date}</div>
              ) : (
                <MatchCard
                  key={`m-${s.match.id}`}
                  match={s.match}
                  prediction={preds[s.match.id] || null}
                  onPredict={handlePredict}
                />
              ))}

              {sections.length === 0 && matches && (
                <div style={{ textAlign:'center', padding:32, color: TT.textSec,
                  fontSize:12 }}>No matches in this filter.</div>
              )}
            </>
          )}

          {tab === 'leaderboard' && (
            <LeaderboardTab myAnonId={anonId} matches={matches} />
          )}

          {tab === 'rules' && (
            <RulesTab />
          )}
        </div>

        {showNameModal && (
          <NameModal onConfirm={handleNameConfirm} />
        )}
      </div>
    );
  }

  window.WcPredictor = WcPredictor;

  // ── Admin panel ─────────────────────────────────────────────────────────────
  // Password-gated score entry. Mounted separately at the /wc-admin route.
  const ADMIN_PASS = 'vogie';

  function WcAdmin({ onExit }) {
    const TT = T();
    const [authed, setAuthed] = React.useState(
      sessionStorage.getItem('wc_admin_authed') === '1'
    );
    const [pass, setPass] = React.useState('');
    const [matches, setMatches] = React.useState(null);
    const [saving, setSaving] = React.useState(null);
    const [msg, setMsg] = React.useState(null);

    React.useEffect(() => {
      if (!authed) return;
      window.fetchWcMatches().then(setMatches).catch(console.error);
    }, [authed]);

    if (!authed) return (
      <div style={{ position:'absolute', inset:0, background: TT.navy950,
        display:'flex', flexDirection:'column', alignItems:'center',
        justifyContent:'center', padding:32 }}>
        <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:16,
          color: TT.text, marginBottom:16 }}>ADMIN — WC RESULTS</div>
        <input
          type="password" value={pass} onChange={e => setPass(e.target.value)}
          placeholder="Admin password"
          style={{ width:'100%', maxWidth:280, padding:'10px 12px',
            borderRadius:8, border:`1px solid ${TT.border}`,
            background:'#0D1828', color: TT.text, fontSize:14,
            fontFamily:'DM Sans', outline:'none', marginBottom:10 }}
          onKeyDown={e => {
            if (e.key === 'Enter' && pass === ADMIN_PASS) {
              sessionStorage.setItem('wc_admin_authed','1'); setAuthed(true);
            }
          }}
        />
        <button
          onClick={() => {
            if (pass === ADMIN_PASS) {
              sessionStorage.setItem('wc_admin_authed','1'); setAuthed(true);
            }
          }}
          style={{ padding:'10px 24px', background: TT.gold500, color:'#1A1200',
            border:'none', borderRadius:10, cursor:'pointer',
            fontFamily:'Bakbak One,sans-serif', fontSize:13 }}>
          ENTER
        </button>
      </div>
    );

    const upcoming = (matches || []).filter(m => m.status === 'upcoming');
    const completed = (matches || []).filter(m => m.status === 'completed');

    return (
      <div style={{ position:'absolute', inset:0, background: TT.navy950,
        display:'flex', flexDirection:'column' }}>
        <div style={{ padding:'14px 16px', display:'flex',
          justifyContent:'space-between', alignItems:'center',
          borderBottom:`1px solid ${TT.border}` }}>
          <button onClick={onExit} style={{ background:'transparent', border:'none',
            color: TT.text, fontSize:22, cursor:'pointer' }}>‹</button>
          <div style={{ fontFamily:'Bakbak One,sans-serif', fontSize:14,
            color: TT.gold400 }}>ADMIN · ENTER RESULTS</div>
          <div style={{ width:24 }}/>
        </div>

        {msg && (
          <div style={{ padding:'8px 16px', background:'rgba(91,192,235,0.1)',
            fontSize:12, color:'#5BC0EB', textAlign:'center' }}>{msg}</div>
        )}

        <div className="no-scrollbar" style={{ flex:1, overflowY:'auto', padding:14 }}>
          <div style={{ fontSize:11, color: TT.textSec, marginBottom:8,
            fontFamily:'DM Sans' }}>
            Upcoming ({upcoming.length}) — tap to enter result
          </div>
          {upcoming.map(m => (
            <AdminMatchRow key={m.id} match={m}
              onSave={async (home, away) => {
                setSaving(m.id);
                try {
                  await window.scoreWcMatch(m.id, home, away);
                  setMsg(`✓ ${m.home_team} ${home}–${away} ${m.away_team}`);
                  setMatches(prev => prev.map(x => x.id === m.id
                    ? { ...x, home_score:home, away_score:away, status:'completed' } : x));
                  setTimeout(() => setMsg(null), 3000);
                  // Fire notifications — non-blocking
                  window.notifyWcResult(m.id);
                } catch(e) { setMsg('Error: ' + e.message); }
                finally { setSaving(null); }
              }}
              saving={saving === m.id}
            />
          ))}

          {completed.length > 0 && (
            <>
              <div style={{ fontSize:11, color: TT.textSec, margin:'16px 0 8px',
                fontFamily:'DM Sans' }}>
                Completed ({completed.length}) — tap to correct or clear
              </div>
              {completed.map(m => (
                <AdminMatchRow key={m.id} match={m} isCompleted
                  onSave={async (home, away) => {
                    setSaving(m.id);
                    try {
                      await window.scoreWcMatch(m.id, home, away);
                      setMsg(`✓ Updated: ${m.home_team} ${home}–${away} ${m.away_team}`);
                      setMatches(prev => prev.map(x => x.id === m.id
                        ? { ...x, home_score:home, away_score:away } : x));
                      setTimeout(() => setMsg(null), 3000);
                      // Re-notify with corrected result
                      window.notifyWcResult(m.id);
                    } catch(e) { setMsg('Error: ' + e.message); }
                    finally { setSaving(null); }
                  }}
                  onClear={async () => {
                    setSaving('clear_' + m.id);
                    try {
                      await window.clearWcMatch(m.id);
                      setMsg(`↩ Cleared: ${m.home_team} vs ${m.away_team}`);
                      setMatches(prev => prev.map(x => x.id === m.id
                        ? { ...x, home_score:null, away_score:null, status:'upcoming' } : x));
                      setTimeout(() => setMsg(null), 3000);
                    } catch(e) { setMsg('Error: ' + e.message); }
                    finally { setSaving(null); }
                  }}
                  saving={saving === m.id}
                />
              ))}
            </>
          )}
        </div>
      </div>
    );
  }

  function AdminMatchRow({ match, onSave, onClear, saving, isCompleted }) {
    const TT = T();
    const [h, setH] = React.useState(isCompleted ? (match.home_score ?? 0) : 0);
    const [a, setA] = React.useState(isCompleted ? (match.away_score ?? 0) : 0);
    const [open, setOpen] = React.useState(false);
    const [confirmClear, setConfirmClear] = React.useState(false);

    return (
      <div style={{ marginBottom:6, borderRadius:8,
        border:`1px solid ${isCompleted ? 'rgba(91,192,235,0.3)' : TT.border}`,
        overflow:'hidden' }}>
        <button onClick={() => setOpen(o => !o)} style={{
          width:'100%', padding:'10px 12px',
          background: isCompleted ? 'rgba(91,192,235,0.06)' : TT.card,
          border:'none', cursor:'pointer',
          display:'flex', justifyContent:'space-between', alignItems:'center',
        }}>
          <span style={{ fontSize:13, color: TT.text, fontFamily:'DM Sans', fontWeight:700 }}>
            {match.home_team}
            {isCompleted && (
              <span style={{ color:'#5BC0EB', margin:'0 4px' }}>
                {match.home_score}–{match.away_score}
              </span>
            )}
            {isCompleted ? '' : ' vs '}{match.away_team}
          </span>
          <span style={{ fontSize:10, color: TT.textMute }}>
            {new Date(match.match_date).toLocaleDateString()} {open ? '▲' : '▼'}
          </span>
        </button>
        {open && (
          <div style={{ padding:'12px', background:'#0D1828', display:'flex',
            flexDirection:'column', gap:8 }}>
            {/* Score entry row */}
            <div style={{ display:'flex', gap:10, alignItems:'center', flexWrap:'wrap' }}>
              <ScoreInput val={h} onChange={setH}/>
              <span style={{ color:'rgba(255,255,255,0.4)', fontSize:16 }}>–</span>
              <ScoreInput val={a} onChange={setA}/>
              <button
                onClick={() => onSave(h, a)}
                disabled={saving}
                style={{ padding:'6px 16px',
                  background: isCompleted ? '#5BC0EB' : TT.gold500,
                  color:'#0D1828', border:'none', borderRadius:8, cursor:'pointer',
                  fontFamily:'Bakbak One,sans-serif', fontSize:12 }}>
                {saving ? '…' : isCompleted ? 'UPDATE' : 'SAVE'}
              </button>
              {isCompleted && (
                <span style={{ fontSize:10, color:'#E87A7A', fontFamily:'DM Sans' }}>
                  ⚠ Rescores all predictions
                </span>
              )}
            </div>
            {/* Clear button — completed matches only */}
            {isCompleted && onClear && (
              <div style={{ display:'flex', alignItems:'center', gap:8,
                paddingTop:6, borderTop:'1px solid rgba(255,255,255,0.07)' }}>
                {confirmClear ? (
                  <>
                    <span style={{ fontSize:10, color:'#E87A7A', fontFamily:'DM Sans', flex:1 }}>
                      Resets to upcoming + clears all prediction points. Sure?
                    </span>
                    <button
                      onClick={() => { setConfirmClear(false); onClear(); }}
                      style={{ padding:'4px 12px', background:'#C0392B',
                        color:'#fff', border:'none', borderRadius:6, cursor:'pointer',
                        fontFamily:'DM Sans', fontWeight:700, fontSize:11 }}>
                      Yes, clear
                    </button>
                    <button
                      onClick={() => setConfirmClear(false)}
                      style={{ padding:'4px 10px', background:'transparent',
                        color: TT.textMute, border:`1px solid ${TT.border}`,
                        borderRadius:6, cursor:'pointer', fontFamily:'DM Sans', fontSize:11 }}>
                      Cancel
                    </button>
                  </>
                ) : (
                  <button
                    onClick={() => setConfirmClear(true)}
                    style={{ padding:'4px 12px', background:'transparent',
                      color:'#E87A7A', border:'1px solid rgba(232,122,122,0.3)',
                      borderRadius:6, cursor:'pointer', fontFamily:'DM Sans',
                      fontWeight:700, fontSize:11 }}>
                    ↩ Clear result
                  </button>
                )}
              </div>
            )}
          </div>
        )}
      </div>
    );
  }

  window.WcAdmin = WcAdmin;
})();
