'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/contexts/AuthContext';
import Hero from '@/components/Hero';
import { ClaimMeter, ModelRecord, Skeleton, SkeletonList } from '@/components/vyndr';
// Session 55 — live top A-rated grades pulled from tonight's real snapshot,
// with the self-learning loop's accuracy line. The product shown, not described.
import TopSignals from '@/components/TopSignals';
// Session 17 — game-count strip mounted between the hero and the
// existing LivePropsStrip. Shows "X NBA · Y WNBA · Z MLB games
// being graded right now" with a signup CTA. Hides itself when
// every sport returns zero (off-hours / upstream outages).
import TonightsSlate from '@/components/TonightsSlate';
import LivePropsStrip from '@/components/LivePropsStrip';
// Session 23 — all-day intelligence teasers. Free/cheap content that
// keeps the landing page alive even when odds-api props are empty.
// Both self-hide when there's nothing to show.
import StreaksPanel from '@/components/StreaksPanel';
import HotListPanel from '@/components/HotListPanel';
import Features from '@/components/Features';
import HowItWorks from '@/components/HowItWorks';
import Pricing from '@/components/Pricing';
import FAQ from '@/components/FAQ';
// Session S7 (a1) — THE VYNDR REPORT capture (double opt-in via Listmonk).
import NewsletterCapture from '@/components/NewsletterCapture';
// Footer is mounted globally in the root layout (Session 34) — no per-page import.
// Session 60 (6.1) — the 3–4s "LOADING THE SLATE" first paint was THIS
// page blocking its entire render on Supabase auth initialization
// (`loading || user`), even for anonymous visitors who were never going to
// redirect. Synchronous localStorage check instead: only a visitor who
// actually HAS a stored session (and will redirect to /dashboard) sees the
// suppressed render; anonymous traffic paints the hero immediately.
function hasStoredSession(): boolean {
if (typeof window === 'undefined') return false;
try {
for (let i = 0; i < window.localStorage.length; i += 1) {
const k = window.localStorage.key(i) || '';
if (/^sb-.*-auth-token$/.test(k) || k === 'sb-token') return true;
}
} catch { /* storage blocked → treat as anonymous */ }
return false;
}
export default function Home() {
const { user, loading } = useAuth();
const router = useRouter();
// DS1 (DESIGN-SPEC §4, audit #5) — React #418 hydration fix. Reading
// localStorage inside a useState INITIALIZER runs during render: the server
// (no `window`) computes `false` and emits the marketing tree, but the first
// CLIENT render of a signed-in visitor reads the stored session, computes
// `true`, and emits the "Loading" placeholder instead — a server/client HTML
// mismatch that made React discard and re-render the whole page (#418). The
// client-only value is now deferred behind a mounted flag: the first client
// render matches the server (both treat it as `false`), then the stored-
// session check flips it post-mount. SSR HTML is never discarded.
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
const maybeSignedIn = mounted && hasStoredSession();
useEffect(() => {
if (!loading && user) router.replace('/dashboard');
}, [user, loading, router]);
// Suppress the marketing render ONLY for visitors with a stored session
// (they're about to redirect) — anonymous first paint is instant.
if (user || (loading && maybeSignedIn)) {
// Signed-in visitor mid-redirect to /dashboard — a layout-matched skeleton,
// never a text wall (§4).
return (