/* global React, C, Screen, Header, Avatar, Pill, CTA, Progress, Chevron, Check, Lock, Google, Apple, Passkey, Bolt, Refresh, Flag, Football */
// screens-1-2.jsx — Sign-in + Home

function ProviderBtn({ provider, onClick }) {
  const map = {
    apple:   { Icon: Apple,   label: 'Apple' },
    google:  { Icon: Google,  label: 'Google' },
    passkey: { Icon: Passkey, label: 'Passkey' },
    phone:   { Icon: PhoneSm, label: 'Phone' },
  };
  const p = map[provider];
  return (
    <button
      onClick={onClick}
      style={{
        height: 52, borderRadius: 8, border: `1px solid ${C.hairline}`,
        background: C.surface, color: C.text,
        font: '500 14px/1 Inter', cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      }}>
      <p.Icon size={18} />
      <span>{p.label}</span>
    </button>
  );
}

function PhoneSm({ size = 18 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round">
      <path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6A19.79 19.79 0 012.12 4.18 2 2 0 014.11 2h3a2 2 0 012 1.72c.13.96.37 1.9.72 2.81a2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.91.35 1.85.59 2.81.72A2 2 0 0122 16.92z"/>
    </svg>
  );
}

function SigninScreen() {
  // Two modes:
  //   - Passkey mode (default if this device has previously enrolled): the
  //     user taps one button → the OS prompts for Face ID / Touch ID →
  //     they're signed in. No email, no magic-link, no waiting.
  //   - Email mode (no hint, or user fell back): standard magic-link.
  const api = window.cambamApi;
  const hintRef = React.useRef(api?.passkeyHint ? api.passkeyHint() : null);
  const webauthn = !!(api && api.webauthnSupported && api.webauthnSupported());
  const hint = hintRef.current;

  const [email, setEmail] = React.useState((hint && hint.email) || '');
  const [busy, setBusy]   = React.useState(false);
  const [sent, setSent]   = React.useState(false);
  const [usePasskey, setUsePasskey] = React.useState(!!(hint && hint.email && webauthn));

  const signInWithPasskey = async () => {
    setBusy(true);
    try {
      const res = await api.signInWithPasskey();
      if (res.ok) {
        window.STORE.toast('Signed in with passkey');
        // Session is set by verifyOtp inside signInWithPasskey; the
        // onAuthStateChange listener in api-client.jsx flips authed=true.
      } else if (res.cancelled) {
        // User cancelled the OS prompt; stay on screen.
      } else {
        window.STORE.toast(res.error || 'Could not sign in with passkey');
      }
    } finally { setBusy(false); }
  };

  const sendMagicLink = async () => {
    if (!email || !/.+@.+\..+/.test(email)) { window.STORE.toast('Enter a valid email'); return; }
    setBusy(true);
    try {
      if (api && api.isConfigured && api.isConfigured()) {
        const { ok, error } = await api.signInWithEmail(email);
        if (!ok) { window.STORE.toast(error || 'Could not send link'); setBusy(false); return; }
        setSent(true);
        window.STORE.toast('Magic link sent — check your email');
      } else if (api && api.signInWithEmail) {
        await api.signInWithEmail(email);
        window.STORE.toast('Welcome, ' + (window.STORE.state.me.displayName || 'CamBam'));
      } else {
        window.STORE.set({ authed: true, me: { ...window.STORE.state.me, email } });
      }
    } finally { setBusy(false); }
  };

  const signInWith = async (provider) => {
    setBusy(true);
    try {
      if (api && api.signInWithProvider) {
        const res = await api.signInWithProvider(provider);
        if (res && !res.ok) {
          window.STORE.toast(res.error || `Could not sign in with ${provider}`);
          setBusy(false);
          return;
        }
      } else {
        window.STORE.set({ authed: true });
      }
      window.STORE.toast('Signed in via ' + provider);
    } finally { setBusy(false); }
  };

  const useDifferentEmail = () => {
    setUsePasskey(false);
    setEmail('');
    // Don't wipe the stored hint — it still applies if user comes back to it.
  };

  return (
    <Screen label="01 Sign-in" bottomNav={null}>
      <div style={{
        height: '100%', display: 'flex', flexDirection: 'column',
        justifyContent: 'center', padding: '0 24px', position: 'relative',
      }}>
        <div style={{
          position: 'absolute', top: '24%', left: '50%', transform: 'translate(-50%, -50%)',
          width: 400, height: 400, borderRadius: '50%', pointerEvents: 'none',
          background: 'radial-gradient(circle, rgba(31, 138, 91,0.10) 0%, rgba(31, 138, 91,0) 60%)',
        }} />

        <div style={{ textAlign: 'center', marginBottom: 40 }}>
          <div style={{
            font: '700 56px/56px Inter', color: C.emerald, letterSpacing: -0.04 * 56,
          }}>CamBam</div>
          <div style={{
            font: '500 13px/18px Inter', color: C.text3, marginTop: 10,
            textTransform: 'uppercase', letterSpacing: 0.16,
          }}>{usePasskey ? 'Welcome back' : 'Invite-only'}</div>
        </div>

        <form
          onSubmit={(e) => { e.preventDefault(); usePasskey ? signInWithPasskey() : sendMagicLink(); }}
          style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          {usePasskey ? (
            <div style={{
              padding: '14px 16px', borderRadius: 8,
              background: C.surface2, border: `1px solid ${C.hairline}`,
              display: 'flex', alignItems: 'center', gap: 12,
            }}>
              <Passkey size={20} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ font: '500 14px/18px Inter', color: C.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{email}</div>
                <div style={{ font: '400 12px/16px Inter', color: C.text2, marginTop: 1 }}>Passkey saved on this device</div>
              </div>
            </div>
          ) : (
            <input
              type="email" inputMode="email" autoComplete="email"
              placeholder="you@email.com"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              disabled={busy || sent}
              style={{
                height: 56, padding: '0 16px',
                background: C.surface2, border: `1px solid ${C.hairline}`,
                borderRadius: 8, color: C.text,
                font: '400 16px/24px Inter', outline: 'none',
              }}
            />
          )}

          <CTA kind="primary" disabled={busy || sent} type="submit">
            {sent ? 'Link sent — check your email'
              : busy ? (usePasskey ? 'Confirming…' : 'Sending…')
              : usePasskey ? 'Sign in with passkey'
              : 'Send magic link'}
          </CTA>

          {usePasskey && !sent && (
            <button type="button" onClick={useDifferentEmail} style={{
              background: 'transparent', border: 'none', cursor: 'pointer',
              color: C.text3, font: '400 12px/16px Inter', padding: '4px 0',
            }}>Use a different email</button>
          )}

          {!usePasskey && (
            <>
              <div style={{
                display: 'flex', alignItems: 'center', gap: 12,
                color: C.text3, font: '400 12px/16px Inter', margin: '4px 0',
              }}>
                <div style={{ flex: 1, height: 1, background: C.hairline }} />
                <span>or</span>
                <div style={{ flex: 1, height: 1, background: C.hairline }} />
              </div>
              <ProviderBtn provider="google" onClick={() => signInWith('google')} />
            </>
          )}
        </form>

        <div style={{
          position: 'absolute', bottom: 32, left: 0, right: 0, textAlign: 'center',
          font: '400 11px/16px Inter', color: C.text3,
        }}>
          By signing in you agree to Terms · Privacy
        </div>
      </div>
    </Screen>
  );
}

function HomeScreen() {
  const me = window.useStore((s) => s.me);
  const [now, setNow] = React.useState(null);
  const [last, setLast] = React.useState(null);
  const [nowStandings, setNowStandings] = React.useState([]);
  const [lastStandings, setLastStandings] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    const api = window.cambamApi;
    if (!api || !api.isConfigured?.()) { setLoading(false); return; }
    let cancelled = false;
    (async () => {
      try {
        const [tCurrent, tLast] = await Promise.all([
          api.currentTournament?.(),
          api.lastFinalTournament?.(),
        ]);
        if (cancelled) return;
        setNow(tCurrent || null);
        setLast(tLast || null);
        setLoading(false);
        const [s1, s2] = await Promise.all([
          tCurrent ? api.loadStandings(tCurrent.id) : Promise.resolve([]),
          tLast ? api.loadStandings(tLast.id) : Promise.resolve([]),
        ]);
        if (cancelled) return;
        setNowStandings(s1 || []);
        setLastStandings(s2 || []);
      } catch (e) {
        console.warn('home load failed', e);
        if (!cancelled) setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, []);

  const todayStr = new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric' });

  return (
    <Screen label="02 Home" bottomNav="home">
      <Header title="CamBam" subtitle={todayStr} />

      <div style={{ height: 8 }} />

      {/* Now playing — live or next tournament */}
      <SectionLabel>Now playing</SectionLabel>
      <NowCard tournament={now} standings={nowStandings} me={me} loading={loading} />

      <div style={{ height: 20 }} />

      {/* Last tournament results — tap any row to drill in */}
      <SectionLabel right={last ? 'Tap any row →' : undefined}>Last tournament</SectionLabel>
      <LastResultsCard tournament={last} standings={lastStandings} me={me} loading={loading} />

      <div style={{ height: 20 }} />

      {/* The Caddie — keep but smaller; full article on tap */}
      <SectionLabel>From The Caddie</SectionLabel>
      <CaddieCard tournament={now || last} />

      <div style={{ height: 32 }} />
    </Screen>
  );
}

// ─── Now playing — primary tappable card ───────────────────────────
function NowCard({ tournament, standings, me, loading }) {
  const goTournament = () => { if (window.appNav) window.appNav.go('pools/lineup'); };

  // Loading skeleton vs empty vs real data
  if (loading) {
    return (
      <div style={{ margin: '0 16px', background: C.surface, borderRadius: 12, padding: 24, color: C.text3, font: '400 13px/18px Inter' }}>
        Loading…
      </div>
    );
  }
  if (!tournament) {
    return (
      <div style={{ margin: '0 16px', background: C.surface, borderRadius: 12, padding: 24, color: C.text3, font: '400 13px/18px Inter' }}>
        No tournaments scheduled yet. Once the commissioner seeds the season, the next major will appear here.
      </div>
    );
  }

  const status = tournament.status; // 'upcoming' | 'live' | 'final'
  const myRow = standings?.find((r) => r.member?.id === me?.id);
  const leaderRow = standings?.[0];
  const myDelta = myRow && leaderRow ? Number(leaderRow.fantasy_pts || 0) - Number(myRow.fantasy_pts || 0) : null;
  const startsAt = tournament.starts_at ? new Date(tournament.starts_at) : null;
  const endsAt = tournament.ends_at ? new Date(tournament.ends_at) : null;
  const dateRange = startsAt && endsAt
    ? `${startsAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}–${endsAt.toLocaleDateString('en-US', { day: 'numeric' })}`
    : startsAt
      ? startsAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
      : '';
  const statusLabel =
    status === 'live'     ? 'LIVE today'
    : status === 'final'  ? 'Final'
    : startsAt            ? `Starts ${startsAt.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}`
    : 'Upcoming';

  return (
    <div onClick={goTournament} style={{
      margin: '0 16px', background: C.surface, borderRadius: 12, overflow: 'hidden',
      cursor: 'pointer',
    }}>
      <div style={{ padding: '18px 20px 12px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
          {status === 'live' && <Pill kind="live" leadingDot>Live</Pill>}
          {status === 'final' && <Pill kind="neutral">Final</Pill>}
          <span style={{
            font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
            color: C.text3,
          }}>{statusLabel}{dateRange ? ` · ${dateRange}` : ''}</span>
        </div>
        <div style={{
          font: '600 22px/28px Inter', color: C.text, letterSpacing: -0.01 * 22,
        }}>{tournament.name}</div>
        <div style={{ font: '400 13px/18px Inter', color: C.text2, marginTop: 2 }}>
          {tournament.venue || ''}
        </div>
      </div>

      <div style={{
        padding: '0 20px 18px',
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, alignItems: 'baseline',
      }}>
        <div>
          <div style={{
            font: '500 11px/14px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
            color: C.text3,
          }}>{status === 'final' ? 'Your finish' : 'Your points'}</div>
          <div className="tnum" style={{ font: '700 36px/40px Inter', color: C.emerald, marginTop: 2 }}>
            {myRow ? Number(myRow.fantasy_pts || 0).toFixed(1) : (status === 'upcoming' ? '—' : '0.0')}
          </div>
          <div style={{ font: '400 12px/16px Inter', color: C.text2, marginTop: 4 }}>
            {myRow && standings && standings.length
              ? <>Rank <span className="tnum" style={{ color: C.text, fontWeight: 600 }}>{myRow.rank ?? '—'}</span> of {standings.length}</>
              : (status === 'upcoming' ? 'Lineup locks at tee-off' : 'No score yet')}
          </div>
        </div>
        <div>
          <div style={{
            font: '500 11px/14px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
            color: C.text3,
          }}>Leader</div>
          <div className="tnum" style={{ font: '700 22px/26px Inter', color: C.text, marginTop: 2 }}>
            {leaderRow ? Number(leaderRow.fantasy_pts || 0).toFixed(1) : '—'}
          </div>
          <div style={{
            font: '400 12px/16px Inter', color: C.text2, marginTop: 4,
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            {leaderRow?.member ? (
              <>
                <Avatar name={leaderRow.member.display_name} size={16} tone={leaderRow.member.avatar_tone} champion={!!leaderRow.member.champion} />
                <span>
                  {leaderRow.member.display_name?.split(' ')[0] || leaderRow.member.handle}
                  {myDelta != null && myDelta > 0 ? ` · +${myDelta.toFixed(1)} ahead` : ''}
                </span>
              </>
            ) : (
              <span>—</span>
            )}
          </div>
        </div>
      </div>

      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)',
        borderTop: `1px solid ${C.hairline}`,
      }}>
        <JumpCell label="Lineup"     value={status === 'upcoming' ? 'Build' : 'View'} route="pools/lineup" />
        <JumpCell label="Standings"  value={status === 'live' ? 'LIVE' : (status === 'final' ? 'Final' : '—')} pulse={status === 'live'} route="pools/standings" />
        <JumpCell label="Matchup"    value="Pick" route="pools/matchup" />
        <JumpCell label="Clubhouse"  value="Open" last route="pools/clubhouse" />
      </div>
    </div>
  );
}

function JumpCell({ label, value, pulse, last, route }) {
  const onClick = (e) => {
    e.stopPropagation();
    if (route && window.appNav) window.appNav.go(route);
  };
  return (
    <button onClick={onClick} style={{
      padding: '12px 8px', minHeight: 64,
      background: 'transparent', border: 'none',
      borderRight: last ? 'none' : `1px solid ${C.hairline}`,
      cursor: 'pointer',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 3,
    }}>
      <span style={{
        font: '500 11px/14px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
        color: C.text3,
      }}>{label}</span>
      <span className={'tnum' + (pulse ? ' live-pulse' : '')} style={{
        font: '600 13px/18px Inter',
        color: pulse ? C.emerald : C.text,
      }}>{value}</span>
    </button>
  );
}

// ─── Last results — secondary tappable card ───────────────────────
function LastResultsCard({ tournament, standings, me, loading }) {
  if (loading) {
    return (
      <div style={{ margin: '0 16px', background: C.surface, borderRadius: 12, padding: 20, color: C.text3, font: '400 13px/18px Inter' }}>
        Loading…
      </div>
    );
  }
  if (!tournament) {
    return (
      <div style={{ margin: '0 16px', background: C.surface, borderRadius: 12, padding: 20, color: C.text3, font: '400 13px/18px Inter' }}>
        Season hasn't started yet. The first major's results will land here when it wraps.
      </div>
    );
  }

  const startsAt = tournament.starts_at ? new Date(tournament.starts_at) : null;
  const endsAt = tournament.ends_at ? new Date(tournament.ends_at) : null;
  const dateRange = startsAt && endsAt
    ? `${startsAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}–${endsAt.toLocaleDateString('en-US', { day: 'numeric' })}`
    : '';
  const top3 = (standings || []).slice(0, 3);
  const winner = top3[0]?.member;
  const myRow = (standings || []).find((r) => r.member?.id === me?.id);
  const myBack = myRow && top3[0] ? Number(top3[0].fantasy_pts || 0) - Number(myRow.fantasy_pts || 0) : null;
  const headline = winner ? `${winner.display_name?.split(' ')[0] || winner.handle} took ${tournament.name}.` : `${tournament.name} wrapped.`;

  return (
    <div style={{
      margin: '0 16px', background: C.surface, borderRadius: 12, overflow: 'hidden',
      cursor: 'pointer',
    }}>
      <div style={{ padding: '14px 20px 12px', borderBottom: `1px solid ${C.hairline}` }}>
        <div style={{
          font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
          color: C.text3,
        }}>{tournament.name}{dateRange ? ` · ${dateRange}` : ''}</div>
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 4, gap: 12,
        }}>
          <div style={{ minWidth: 0 }}>
            <div style={{ font: '600 18px/24px Inter', color: C.text }}>{headline}</div>
            <div style={{ font: '400 13px/18px Inter', color: C.text2, marginTop: 1 }}>
              {myRow
                ? <>You finished <span style={{ color: C.text, fontWeight: 600 }}>#{myRow.rank ?? '—'}</span>{myBack != null && myBack > 0 ? ` · ${myBack.toFixed(1)} back` : ''}</>
                : 'No lineup recorded for you'}
            </div>
          </div>
          {winner && <Avatar name={winner.display_name || winner.handle} size={36} tone={winner.avatar_tone} champion={!!winner.champion} />}
        </div>
      </div>

      {top3.length ? top3.map((r, i) => {
        const isYou = r.member?.id === me?.id;
        return (
          <div key={r.member?.id || i} style={{
            padding: '10px 16px',
            display: 'flex', alignItems: 'center', gap: 12,
            borderBottom: i < top3.length - 1 ? `1px solid ${C.hairline}` : 'none',
            background: isYou ? 'rgba(31, 138, 91, 0.04)' : 'transparent',
          }}>
            <span className="tnum" style={{
              font: '700 16px/22px Inter',
              color: isYou ? C.emerald : C.text2,
              width: 18, textAlign: 'center',
            }}>{r.rank ?? (i + 1)}</span>
            <Avatar name={r.member?.display_name || r.member?.handle || '?'} size={28} tone={r.member?.avatar_tone} champion={!!r.member?.champion} />
            <span style={{ flex: 1, font: '500 14px/18px Inter', color: C.text }}>
              {r.member?.display_name || r.member?.handle || '—'}
              {isYou && <span style={{
                marginLeft: 6, font: '500 10px/14px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 10,
                color: C.emerald, background: 'rgba(31, 138, 91, 0.15)',
                padding: '1px 5px', borderRadius: 999,
              }}>You</span>}
            </span>
            <span className="tnum" style={{ font: '600 14px/18px Inter', color: C.text }}>{Number(r.fantasy_pts || 0).toFixed(1)}</span>
          </div>
        );
      }) : (
        <div style={{ padding: '16px 20px', color: C.text3, font: '400 13px/18px Inter' }}>
          No standings recorded — scoring engine hasn't run yet.
        </div>
      )}

      {top3.length > 0 && (
        <button
          onClick={() => window.appNav && window.appNav.go('pools/standings')}
          style={{
            width: '100%', padding: '12px 16px', background: 'transparent',
            border: 'none', borderTop: `1px solid ${C.hairline}`,
            cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          }}>
          <span style={{ color: C.emerald, font: '600 13px/18px Inter' }}>See full results & recap</span>
          <Chevron dir="right" size={14} />
        </button>
      )}
    </div>
  );
}

// 5-stop horizontal strip for the season's tournaments (incl. THE PLAYERS as 5th).
function MajorsStrip() {
  const stops = [
    { id: 'players', name: 'PLAYERS',     venue: 'TPC Sawgrass',       state: 'done', winner: 'Sasha' },
    { id: 'mas',     name: 'Masters',     venue: 'Augusta National',   state: 'done', winner: 'Drew' },
    { id: 'pga',     name: 'PGA Champ.',  venue: 'Aronimink',         state: 'live' },
    { id: 'uso',     name: 'U.S. Open',   venue: 'Shinnecock Hills',       state: 'upcoming', when: 'Jun 18' },
    { id: 'opn',     name: 'The Open',    venue: 'Royal Birkdale',     state: 'upcoming', when: 'Jul 16' },
  ];
  return (
    <div style={{ margin: '0 16px', background: C.surface, borderRadius: 12, padding: '16px 14px 18px' }}>
      <div style={{
        font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
        color: C.text3, marginBottom: 14,
      }}>The Five · '26</div>

      {/* Rail with 5 stops */}
      <div style={{ position: 'relative', padding: '0 4px' }}>
        <div style={{
          position: 'absolute', left: 18, right: 18, top: 11, height: 2,
          background: C.surface2, borderRadius: 999,
        }} />
        {/* completed portion of rail — through PGA midway = 50% */}
        <div style={{
          position: 'absolute', left: 18, top: 11, height: 2,
          width: 'calc((100% - 36px) * 0.50)',
          background: C.emerald, borderRadius: 999,
        }} />

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 4, position: 'relative' }}>
          {stops.map(s => <MajorStop key={s.id} s={s} />)}
        </div>
      </div>
    </div>
  );
}

function MajorStop({ s }) {
  const done = s.state === 'done';
  const live = s.state === 'live';
  const bg = done ? C.emerald : live ? C.emerald : C.surface2;
  const fg = done ? '#0a0a0a' : live ? '#0a0a0a' : C.text3;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
      <div style={{
        width: 24, height: 24, borderRadius: 999,
        background: bg, color: fg,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        boxShadow: live ? `0 0 0 4px rgba(31, 138, 91, 0.18)` : 'none',
        position: 'relative',
      }}>
        {done && <Check size={12} />}
        {live && <span className="live-pulse" style={{
          width: 8, height: 8, borderRadius: 999, background: '#0a0a0a',
        }} />}
        {!done && !live && (
          <span style={{ width: 6, height: 6, borderRadius: 999, background: C.hairline }} />
        )}
      </div>
      <div style={{ textAlign: 'center' }}>
        <div style={{ font: '600 12px/16px Inter', color: live ? C.text : done ? C.text : C.text2 }}>{s.name}</div>
        <div style={{ font: '400 10px/14px Inter', color: C.text3, marginTop: 1 }}>
          {done ? `✓ ${s.winner}` : live ? 'LIVE today' : s.when}
        </div>
      </div>
    </div>
  );
}

function PoolCard({ pool, meta, live, lockText, lockKind = 'neutral', statRows, cta, ctaWarning }) {
  return (
    <div style={{
      margin: '0 16px', background: C.surface, borderRadius: 12, overflow: 'hidden',
    }}>
      {/* head */}
      <div style={{ padding: '20px 20px 16px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <span style={{
            font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
            color: C.text3,
          }}>{pool}</span>
          {live && <Pill kind="live" leadingDot>Live</Pill>}
        </div>
        <div style={{ font: '600 20px/26px Inter', color: C.text }}>{meta}</div>
      </div>

      {/* stats row */}
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)',
        padding: '0 20px 16px', gap: 8,
      }}>
        {statRows.map((s, i) => (
          <div key={i}>
            <div style={{
              font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
              color: C.text3,
            }}>{s.label}</div>
            <div className="tnum" style={{
              font: '700 24px/30px Inter', color: C.text, marginTop: 2,
            }}>{s.value}</div>
          </div>
        ))}
      </div>

      {/* lock countdown row */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '12px 20px', background: C.surface2,
        color: lockKind === 'amber' ? C.amber : C.text2,
        font: '500 13px/18px Inter',
      }}>
        <Lock size={14} />
        <span style={{ flex: 1 }}>{lockText}</span>
        {ctaWarning && (
          <span style={{ color: C.amber, font: '600 13px/18px Inter' }}>{ctaWarning}</span>
        )}
      </div>

      {/* CTA */}
      <div style={{
        padding: '12px 20px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      }}>
        <span style={{ color: C.emerald, font: '600 15px/22px Inter' }}>{cta}</span>
        <Chevron dir="right" size={18} stroke={2} />
      </div>
    </div>
  );
}

// ─── The Caddie — AI dispatch card ──────────────────────────────────
// The Caddie is generated by a CLI script the commissioner runs after each
// major: `node server/cli/caddie.js --tournament=<uuid>`. The script calls
// Claude Code in headless mode (`claude -p`) and inserts the JSON into
// `caddie_articles`. This card just reads the latest published row.
// The canvas demo still falls back to `window.claude.complete` so the design
// canvas works inside claude.ai's sandbox.

// Standalone prompt used only by the canvas/claude.ai sandbox path so the
// design demo has a live Caddie article without a database. The real Caddie
// dispatch is built inside `server/cli/caddie.js` with the snarky/standard
// tone defaults and pool data pulled from Supabase.
const SANDBOX_DEMO_PROMPT = `You are "The Caddie" — an AI columnist for a friends-only fantasy golf pool of 47 people. Sharp, dry, funny, almost-rude. Punch up at the winner with mock-reverence; tease members for bad picks. Avoid golf clichés. No emoji. No "folks".

CONTEXT — the major just wrapped:
- Tournament: The Masters at Augusta National (April 9–12, 2026)
- Real-world winner: Scottie Scheffler at -11
- Pool champion: Drew Petersen (@drewp) — 312.4 pts; his key pick was Scheffler ($52) → 134.2 pts
- Top 5: 1. Drew Petersen 312.4 · 2. CamBam 287.1 · 3. Marco Reyes 274.8 · 4. Sasha Patel 261.2 · 5. Henry Liu 248.9
- Biggest bust: Sahith Theegala ($42, rostered by 12 of 47, missed cut, 4.0 pts)
- Best value: Akshay Bhatia ($18, rostered by only 4 of 47, T11, 79.6 pts)
- Sunday arc: Drew sat fourth through 54 holes; Scheffler eagled 13, McIlroy bogeyed 17, Drew leapfrogged three people in 90 minutes
- Running gag: Drew has worn the green jacket to brunch every weekend since.

Body 80–110 words. One pull quote 15–25 words. Describe the Sunday roller-coaster and the bust.

Return ONLY raw JSON, no markdown fences, with these exact keys:
{ "headline": "8–12 word punchy headline", "body": "one paragraph, no line breaks", "quote": "a single pull quote" }`;

// Per-tournament accent for the Caddie card. The dispatch wears the colors of
// whichever tournament it's covering.
const CADDIE_THEMES = {
  players: { accent: '#0d7c87', tint: 'rgba(13, 124, 135, 0.16)' },
  masters: { accent: '#1f8a5b', tint: 'rgba(31, 138, 91, 0.16)' },
  pga:     { accent: '#b08d2f', tint: 'rgba(176, 141, 47, 0.16)' },
  'us-open': { accent: '#c8102e', tint: 'rgba(200, 16, 46, 0.14)' },
  open:    { accent: '#7a2e2e', tint: 'rgba(122, 46, 46, 0.18)' },
};

function CaddieCard({ tournament }) {
  // Use the passed tournament's major to pick an accent + look up the dispatch.
  const majorKey = tournament?.major || 'masters';
  const theme = CADDIE_THEMES[majorKey] || CADDIE_THEMES['masters'];
  const [article, setArticle] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr]         = React.useState(null);

  const load = React.useCallback(async (force) => {
    // Cache key is per-major so switching tournaments doesn't show stale text.
    const cacheKey = `caddie-article-v3:${majorKey}`;
    if (!force) {
      const cached = localStorage.getItem(cacheKey);
      if (cached) {
        try { setArticle(JSON.parse(cached)); setLoading(false); return; } catch {}
      }
    }
    setLoading(true);
    setErr(null);

    try {
      let json = null;

      // Production path: read the latest published article for this major.
      if (window.cambamApi && window.cambamApi.isConfigured && window.cambamApi.isConfigured()) {
        json = await window.cambamApi.loadLatestCaddie(tournament?.id || majorKey);
      }

      // Design-canvas sandbox path: generate on-the-fly so the standalone
      // canvas/HTML demo (NOT the production app) shows a fresh article
      // without needing a database. Gated on `window.APP_MODE !== true` so
      // it never fires in the real app — the production deploy sets
      // APP_MODE=true before mounting React.
      if (!json && window.APP_MODE !== true && typeof window.claude !== 'undefined' && window.claude.complete) {
        const raw = await window.claude.complete(SANDBOX_DEMO_PROMPT);
        const cleaned = raw.replace(/^```json\s*|\s*```$|^```\s*/g, '').trim();
        json = JSON.parse(cleaned);
      }

      if (!json) throw new Error('No dispatch filed yet');
      if (!json.headline || !json.body || !json.quote) throw new Error('missing keys');
      setArticle(json);
      localStorage.setItem(cacheKey, JSON.stringify(json));
    } catch (e) {
      setErr(e?.message || 'Could not load');
    } finally {
      setLoading(false);
    }
  }, []);

  React.useEffect(() => { load(false); }, [load]);

  // Listen for the admin's "Refresh dispatch" button (broadcast via window event).
  // The CLI script writes to Supabase out-of-band; this just re-fetches.
  React.useEffect(() => {
    const onRegen = () => load(true);
    window.addEventListener('caddie:regen', onRegen);
    return () => window.removeEventListener('caddie:regen', onRegen);
  }, [load]);

  // Production: only show real Caddie article if one exists in Supabase.
  // No static "Drew Petersen" fallback — show an honest empty state instead.
  const data = article;
  const tournamentName = tournament?.name || '';
  const filedDate = data?.created_at ? new Date(data.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '';

  return (
    <div style={{
      margin: '0 16px', background: C.surface, borderRadius: 12, overflow: 'hidden',
    }}>
      {/* Byline strip */}
      <div style={{
        padding: '12px 16px',
        display: 'flex', alignItems: 'center', gap: 10,
        borderBottom: `1px solid ${C.hairline}`,
      }}>
        <div style={{
          width: 28, height: 28, borderRadius: 999,
          background: C.surface2, color: C.text,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          font: '700 11px/1 Inter', letterSpacing: 0.5,
        }}>TC</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ font: '600 13px/16px Inter', color: C.text }}>The Caddie</div>
          <div style={{ font: '400 11px/14px Inter', color: C.text3, marginTop: 1 }}>
            {data
              ? [filedDate && `Filed ${filedDate}`, tournamentName && `${tournamentName} recap`].filter(Boolean).join(' · ')
              : 'No dispatch filed yet'}
          </div>
        </div>
        <button
          onClick={(e) => { e.stopPropagation(); load(true); }}
          aria-label="Refresh dispatch"
          style={{
            width: 26, height: 26, borderRadius: 999, background: C.surface2,
            border: 'none', cursor: 'pointer', color: C.text2,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
          <Refresh size={13} />
        </button>
        <span style={{
          display: 'inline-flex', alignItems: 'center', gap: 4,
          padding: '0 8px', height: 20, borderRadius: 999,
          background: C.surface2, color: C.text2,
          font: '500 10px/1 Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 10,
        }}>
          <span style={{ width: 4, height: 4, borderRadius: 999, background: C.text3 }} />
          AI dispatch
        </span>
      </div>

      {/* Body */}
      {loading && !data ? (
        <CaddieSkeleton />
      ) : !data ? (
        <div style={{ padding: '20px 16px', color: C.text2, font: '400 13px/20px Inter' }}>
          The Caddie hasn't filed for {tournamentName || 'this tournament'} yet. The commissioner runs <span style={{ color: C.text3, font: '500 12px/16px ui-monospace, Menlo, monospace' }}>node cli/caddie.js</span> after each major.
        </div>
      ) : (
        <>
          <div style={{ padding: '14px 16px 4px' }}>
            <div style={{
              font: '700 20px/26px Inter', color: C.text, letterSpacing: -0.01 * 20,
              textWrap: 'pretty',
            }}>{data.headline}</div>
            <div style={{
              font: '400 14px/22px Inter', color: C.text2, marginTop: 8,
              textWrap: 'pretty',
            }}>{data.body}</div>
          </div>

          {data.quote && (
            <div style={{
              margin: '12px 16px 0', padding: '10px 14px',
              borderLeft: `2px solid ${theme.accent}`,
              font: 'italic 500 14px/22px Inter', color: C.text,
              textWrap: 'pretty',
            }}>"{data.quote.replace(/^["'*_\s]+|["'*_\s]+$/g, '')}"</div>
          )}

          <div style={{
            padding: '14px 16px 16px',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          }}>
            <span style={{ font: '600 14px/18px Inter', color: theme.accent }}>Read full recap</span>
            <Chevron dir="right" size={16} stroke={2} />
          </div>
        </>
      )}
    </div>
  );
}

function CaddieSkeleton() {
  const bar = (w, h = 14) => (
    <div style={{
      width: w, height: h, borderRadius: 4, marginBottom: 8,
      background: `linear-gradient(90deg, ${C.surface2} 0%, #232323 50%, ${C.surface2} 100%)`,
      backgroundSize: '300px 100%',
      animation: 'shimmer 1.2s linear infinite',
    }} />
  );
  return (
    <div style={{ padding: '16px' }}>
      {bar('86%', 22)}
      {bar('72%', 22)}
      <div style={{ height: 6 }} />
      {bar('100%')}
      {bar('96%')}
      {bar('64%')}
      <div style={{ marginTop: 10, font: '500 11px/16px Inter', color: C.text3 }}>
        The Caddie is filing… (regenerates from your pool's actual numbers)
      </div>
    </div>
  );
}

function SectionLabel({ children, right }) {
  return (
    <div style={{
      padding: '0 16px 12px',
      display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
    }}>
      <span style={{
        font: '500 11px/16px Inter', textTransform: 'uppercase', letterSpacing: 0.04 * 11,
        color: C.text3,
      }}>{children}</span>
      {right && <span style={{
        font: '400 11px/16px Inter', color: C.text3,
      }}>{right}</span>}
    </div>
  );
}

function ActivityRow({ name, tone, champion, text, time }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 0' }}>
      <Avatar name={name} size={32} tone={tone} champion={champion} />
      <div style={{ flex: 1, minWidth: 0, font: '400 14px/20px Inter', color: C.text2 }}>
        <span style={{ color: C.text, fontWeight: 600 }}>{name}</span> {text}
      </div>
      <span style={{ font: '400 12px/16px Inter', color: C.text3 }}>{time}</span>
    </div>
  );
}

Object.assign(window, { SigninScreen, HomeScreen });
