import { useEffect, useRef, useState } from "react"; import { Link } from "react-router"; import { Users, Shuffle, Trophy, ChevronDown } from "lucide-react"; import { Button } from "~/components/ui/button"; import { Card, CardContent } from "~/components/ui/card"; import { SectionCardHeader } from "~/components/ui/SectionCardHeader"; // ─── Slot Machine ──────────────────────────────────────────────────────────── const SPORTS = [ "Football", "Baseball", "Hockey", "Basketball", "Soccer", "Tennis", "Golf", "eSports", "Formula 1", "Aussie Rules", "Lacrosse", "Darts", "College Football", "Snooker", "March Madness", ]; function shuffle(arr: T[]): T[] { const a = [...arr]; for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } const TOTAL_TICKS = 12; // WCAG 2.3.1: no more than 3 flashes/second → minimum interval 334 ms. // Curve runs 800 ms → 340 ms over 12 ticks (ramps to full speed at tick 8). function getInterval(tick: number): number { const pct = Math.min((tick / TOTAL_TICKS) * 1.5, 1); return Math.round(800 - (800 - 340) * (pct * pct)); } function SlotMachineHeadline() { const [text, setText] = useState(SPORTS[0]); const [landed, setLanded] = useState(false); const [animKey, setAnimKey] = useState(0); const tickRef = useRef(0); const timerRef = useRef | null>(null); const sportsSeq = useRef([]); useEffect(() => { sportsSeq.current = shuffle(SPORTS); let idx = 0; function tick() { tickRef.current++; const sport = sportsSeq.current[idx % sportsSeq.current.length]; idx++; setText(sport); setAnimKey((k) => k + 1); if (tickRef.current >= TOTAL_TICKS) { setLanded(true); return; } timerRef.current = setTimeout(tick, getInterval(tickRef.current)); } timerRef.current = setTimeout(tick, 800); return () => { if (timerRef.current) clearTimeout(timerRef.current); }; }, []); if (landed) { return ( Every Sport. ); } return ( {text} ); } // ─── Bracket Decoration ────────────────────────────────────────────────────── function BracketDecor({ flip = false, mobileTall = false }: { flip?: boolean; mobileTall?: boolean }) { const base = flip ? "r" : "l"; const id = mobileTall ? `${base}-m` : base; const W = 399; const H = mobileTall ? 2000 : 560; const pad = 36; const spacing = (H - 2 * pad) / 7; const r1Ys = Array.from({ length: 8 }, (_, i) => pad + i * spacing); const r2Ys = [0, 1, 2, 3].map((i) => (r1Ys[i * 2] + r1Ys[i * 2 + 1]) / 2); const r3Ys = [0, 1].map((i) => (r2Ys[i * 2] + r2Ys[i * 2 + 1]) / 2); const champY = (r3Ys[0] + r3Ys[1]) / 2; const x0 = 4; const x1 = 77; const x2 = 161; const x3 = 252; const gradId = `grad-${id}`; const glowId = `glow-${id}`; const dotsGlowId = `dots-glow-${id}`; const finalsGlowId = `finals-glow-${id}`; const finalsHaloId = `finals-halo-${id}`; const finalsFadeMaskGradId = `finals-fade-mask-grad-${id}`; const finalsFadeMaskId = `finals-fade-mask-${id}`; const finalsLineGlowId = `finals-line-glow-${id}`; return ( {r3Ys.map((midY) => ( ))} {r3Ys.map((midY) => ( ))} {r1Ys.map((y) => ( ))} {r2Ys.map((midY, i) => ( ))} {r3Ys.map((midY, i) => ( ))} ); } // ─── Scroll Hint ───────────────────────────────────────────────────────────── function ScrollHint() { const [visible, setVisible] = useState(true); useEffect(() => { const onScroll = () => { if (window.scrollY > 50) setVisible(false); }; window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); return (
); } // ─── Sport Ticker ───────────────────────────────────────────────────────────── const TICKER_ITEMS = [ ...SPORTS.map((s) => ({ sport: s.toUpperCase(), key: `${s}-a` })), ...SPORTS.map((s) => ({ sport: s.toUpperCase(), key: `${s}-b` })), ]; function SportTicker() { return (
{TICKER_ITEMS.map(({ sport, key }) => ( {sport} ))}
); } // ─── Static data ────────────────────────────────────────────────────────────── const HOW_IT_WORKS_STEPS = [ { icon: Users, title: "Create a League", body: "Start a league, pick your sports (we can help), and invite your friends. It takes 30 seconds to set the stage.", }, { icon: Shuffle, title: "Draft Your Teams", body: "Pick teams from all the sports in order to build a superteam to win you bragging rights! Draft your way, slow or fast, with standard timers or high-pressure chess clocks.", }, { icon: Trophy, title: "Watch & Win", body: "Your teams earn points for how well they perform in the playoffs. Follow your rooting interests along the way, and gloat over your leaguemates when they fall.", }, ]; const FEATURES = [ { title: "Dozens of Sports and Counting", body: "Draft from a massive selection of sports including major US leagues, college athletics, and global niches like professional darts or snooker. We are constantly adding new sports to ensure your league has competitive action throughout the entire year.", }, { title: "The Smart Draft Room", body: "Our interface supports any pace. Use a tactical chess clock with Fischer increments to reward fast decisions, or run a multi-day slow draft with customizable overnight pauses. The system ensures no one is forced to make a pick in the middle of the night.", }, { title: "Your Master Calendar", body: "Stop switching between different apps to track your roster. Every game, match, and tournament for your specific teams is synced into a single view. You will always know exactly when your points are on the line without any manual tracking.", }, { title: "Zero Maintenance", body: "Brackt is a draft and done experience. Once the draft is finished, your work is over because we handle all scoring and rankings automatically. You can even paste a Discord webhook URL to get live scoring updates sent directly to your group chat.", }, ]; // ─── Main Landing Page ──────────────────────────────────────────────────────── export function LandingPage() { // Interleave step cards with gradient triangle separators const howItWorksItems: React.ReactNode[] = []; HOW_IT_WORKS_STEPS.forEach((step, i) => { howItWorksItems.push(

{step.body}

); if (i < HOW_IT_WORKS_STEPS.length - 1) { // Triangles reference #brackt-primary-gradient defined in (mounted in root.tsx) howItWorksItems.push(
); } }); return (
{/* ── Hero ── */}
{/* Dot-grid texture */}
{/* Bracket decorations */}
{/* Center content */}

Fantasy

Draft teams across every league and sport, like the NFL, NHL, EPL, F1, and more. Compete against your friends in a season-long league.

{/* ── How It Works ── */}
{howItWorksItems}
{/* ── Features Grid ── */}
{FEATURES.map(({ title, body }) => (

{title}

{body}

))}
{/* ── Footer CTA ── */}

Stop managing your league and start watching it.

Brackt is built for the sports fan who wants a stake in everything without the daily chore of a traditional fantasy roster. Setting up a league takes less than five minutes. Choose your sports, invite your friends, and let the draft decide the winner.

); }