'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 (
); } return ( <> {/* Session 55 — tonight's real top signals + live accuracy (the system works). */} {/* Session 58 (work-order 1.4) — the public model record (proof-strip footer). Deferred-render: "RECORD BUILDING" until 20 settles, then the real hit% + beat-close%. Self-hides with no data. */}
{/* Wave 5A (D2) — the shareable public model record (the house profile). */} VIEW PUBLIC RECORD →
{/* Founder-seat scarcity meter (§12) */}
{/* Session 60 (night2/C) — the free hook: top-3 REAL streaks through the lens. MLB is the in-season board (was nba — off-season = the panel self-hid and the teaser never showed). */}
{/* Session S7 (a1) — the daily wire, by email. Sits above the global footer. */}
); }