907c7b17c1
VYNDR 2.0 conversion, Phase C (the frame every page sits inside). Frontend-only; zero backend changes. - Nav rewritten: new .wm Wordmark, mono uppercase links, More dropdown, search/ bell/read-meter/avatar, Ticker under the bar. layout main paddingTop 64 -> 96. - Routing: web/src/lib/routes.js (GATED/OPEN/HASH_ALIASES, isGatedRoute, resolveHashAlias). Client AuthGate bounces signed-out users off personal routes to /login?next=. HashRedirect maps #scan/#terminal to real routes. - Footer rewritten to system voice + Detroit signature; mounted globally in layout (removed per-page dup). - 404 converted to the north star (scanlines, crt-sweep, glitch wordmark, amber). - Stub pages for terminal/compare/invite/help/about/notifications via RouteStub. Honest reconciliations: auth gate is client-side (no auth-helpers pkg; session is client-side Supabase); GATED narrowed to protect the free-scan funnel; did not stub over existing real pages; redirect param is ?next= (what /login reads). 26 new tests. Backend 1792 -> 1818, 142 suites, zero regressions. Web build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { useAuth } from '@/contexts/AuthContext';
|
|
import Hero from '@/components/Hero';
|
|
// 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';
|
|
// Footer is mounted globally in the root layout (Session 34) — no per-page import.
|
|
|
|
export default function Home() {
|
|
const { user, loading } = useAuth();
|
|
const router = useRouter();
|
|
|
|
useEffect(() => {
|
|
if (!loading && user) router.replace('/dashboard');
|
|
}, [user, loading, router]);
|
|
|
|
// While we know the user is signed in we suppress the marketing
|
|
// render to avoid a flicker before the redirect lands.
|
|
if (loading || user) {
|
|
return (
|
|
<section style={{ minHeight: '80vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<p className="mono" style={{ color: 'var(--text-tertiary)', fontSize: 13, letterSpacing: '0.08em', textTransform: 'uppercase' }}>
|
|
Loading the slate
|
|
</p>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Hero />
|
|
<TonightsSlate />
|
|
<LivePropsStrip />
|
|
<div style={{ maxWidth: 960, margin: '0 auto', padding: '0 16px' }}>
|
|
<StreaksPanel sport="nba" tier="free" limit={3} />
|
|
<HotListPanel sport="mlb" tier="free" limit={3} />
|
|
</div>
|
|
<Features />
|
|
<HowItWorks />
|
|
<Pricing />
|
|
<FAQ />
|
|
</>
|
|
);
|
|
}
|