'use client'; import { useState, Suspense } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useAuth } from '@/contexts/AuthContext'; import { trackSignup } from '@/lib/analytics'; import Wordmark from '@/components/Wordmark'; import { GoogleIcon, AppleIcon, XIcon } from '@/components/OAuthIcons'; function SignupInner() { const router = useRouter(); const search = useSearchParams(); const next = search.get('next') || '/dashboard'; const { signUp, signInWithProvider } = useAuth(); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [ageOk, setAgeOk] = useState(false); const [error, setError] = useState(''); const [busy, setBusy] = useState(false); const [done, setDone] = useState(false); const handleSignup = async (e: React.FormEvent) => { e.preventDefault(); setError(''); if (password.length < 8) return setError('Password must be at least 8 characters.'); if (password !== confirm) return setError('Passwords do not match.'); if (!ageOk) return setError('You must confirm you are 21 or older.'); setBusy(true); const { error: err } = await signUp(email, password, ageOk); setBusy(false); if (err) { setError(err); return; } trackSignup({ method: 'password' }); setDone(true); // Supabase confirms via email by default. If session is immediate, redirect. setTimeout(() => router.replace(next), 1500); }; // Session 13 — generic OAuth dispatch. Same provider buttons as // the login page; same graceful-error contract for unconfigured // providers (Apple/X). const handleOAuth = async (provider: 'google' | 'apple' | 'twitter') => { setBusy(true); setError(''); const { error: err } = await signInWithProvider(provider); if (err) { setError(err); setBusy(false); } }; if (done) { return (

You're in.

Check your email for a confirmation link, then come back and grade something.

); } return (

Get started — free

5 free reads every month. Your first read is fully unlocked. No credit card.

{/* Session 14 — OAuth buttons with provider icons. Same ProviderButton helper as /login (inlined here to avoid a new shared module for two callsites). */}
{(['google', 'apple', 'twitter'] as const).map((provider) => { const Icon = provider === 'google' ? GoogleIcon : provider === 'apple' ? AppleIcon : XIcon; const label = provider === 'google' ? 'Google' : provider === 'apple' ? 'Apple' : 'X'; return ( ); })}
OR
setEmail(e.target.value)} />
setPassword(e.target.value)} />
setConfirm(e.target.value)} />
{error &&

{error}

}

By signing up you agree to our{' '} Terms and{' '} Privacy Policy.

Already have an account?{' '} Log in

); } export default function SignupPage() { return ( ); } const labelStyle: React.CSSProperties = { display: 'block', fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-tertiary)', marginBottom: 6, }; const dividerStyle: React.CSSProperties = { display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: 12, alignItems: 'center', margin: '16px 0', }; const dividerLine: React.CSSProperties = { height: 1, background: 'var(--border)', };