website refactor
This commit is contained in:
@@ -4,27 +4,20 @@ import { AlternatingSection } from '@/components/landing/AlternatingSection';
|
||||
import { FAQ } from '@/components/landing/FAQ';
|
||||
import { FeatureGrid } from '@/components/landing/FeatureGrid';
|
||||
import { LandingHero } from '@/components/landing/LandingHero';
|
||||
import { FeatureItem, ResultItem, StepItem } from '@/components/landing/LandingItems';
|
||||
import { DiscoverySection } from '@/components/landing/DiscoverySection';
|
||||
import { FeatureItem, ResultItem, StepItem } from '@/ui/LandingItems';
|
||||
import { CareerProgressionMockup } from '@/components/mockups/CareerProgressionMockup';
|
||||
import { CompanionAutomationMockup } from '@/components/mockups/CompanionAutomationMockup';
|
||||
import { RaceHistoryMockup } from '@/components/mockups/RaceHistoryMockup';
|
||||
import { SimPlatformMockup } from '@/components/mockups/SimPlatformMockup';
|
||||
import { ModeGuard } from '@/components/shared/ModeGuard';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { getMediaUrl } from '@/lib/utilities/media';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Card } from '@/ui/Card';
|
||||
import { Container } from '@/ui/Container';
|
||||
import { DiscordCTA } from '@/ui/DiscordCTA';
|
||||
import { Footer } from '@/ui/Footer';
|
||||
import { Grid } from '@/ui/Grid';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Image } from '@/ui/Image';
|
||||
import { Link } from '@/ui/Link';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { TelemetryLine } from '@/ui/TelemetryLine';
|
||||
import { Glow } from '@/ui/Glow';
|
||||
|
||||
export interface HomeViewData {
|
||||
isAlpha: boolean;
|
||||
@@ -53,220 +46,132 @@ interface HomeTemplateProps {
|
||||
|
||||
export function HomeTemplate({ viewData }: HomeTemplateProps) {
|
||||
return (
|
||||
<Box as="main">
|
||||
<Box as="main" bg="graphite-black" position="relative" overflow="hidden">
|
||||
<Glow color="primary" size="xl" position="top-right" opacity={0.05} />
|
||||
|
||||
<LandingHero />
|
||||
|
||||
<TelemetryLine color="primary" height="1px" opacity={0.3} />
|
||||
|
||||
{/* Section 1: A Persistent Identity */}
|
||||
<AlternatingSection
|
||||
heading="A Persistent Identity"
|
||||
backgroundVideo="/gameplay.mp4"
|
||||
description={
|
||||
<Stack gap={4}>
|
||||
<Text>
|
||||
Your races, your seasons, your progress — finally in one place.
|
||||
</Text>
|
||||
<Stack gap={3}>
|
||||
<FeatureItem text="Lifetime stats and season history across all your leagues" />
|
||||
<FeatureItem text="Track your performance, consistency, and team contributions" />
|
||||
<FeatureItem text="Your own rating that reflects real league competition" />
|
||||
<Box position="relative" bg="graphite-black">
|
||||
<Glow color="aqua" size="lg" position="bottom-left" opacity={0.03} />
|
||||
<AlternatingSection
|
||||
heading="A Persistent Identity"
|
||||
backgroundVideo="/gameplay.mp4"
|
||||
description={
|
||||
<Stack gap={8}>
|
||||
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||
Your races, your seasons, your progress — finally in one place.
|
||||
</Text>
|
||||
<Box display="grid" gridCols={1} gap={3}>
|
||||
<FeatureItem text="Lifetime stats and season history across all your leagues" />
|
||||
<FeatureItem text="Track your performance, consistency, and team contributions" />
|
||||
<FeatureItem text="Your own rating that reflects real league competition" />
|
||||
</Box>
|
||||
<Box borderLeft borderStyle="solid" borderColor="primary-accent" pl={4} py={1} bg="primary-accent/5">
|
||||
<Text color="text-gray-500" font="mono" size="xs" uppercase letterSpacing="widest">
|
||||
iRacing gives you physics. GridPilot gives you a career.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text>
|
||||
iRacing gives you physics. GridPilot gives you a career.
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
mockup={<CareerProgressionMockup />}
|
||||
layout="text-left"
|
||||
/>
|
||||
}
|
||||
mockup={<CareerProgressionMockup />}
|
||||
layout="text-left"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<FeatureGrid />
|
||||
|
||||
{/* Section 2: Results That Actually Stay */}
|
||||
<AlternatingSection
|
||||
heading="Results That Actually Stay"
|
||||
backgroundImage="/images/ff1600.jpeg"
|
||||
description={
|
||||
<Stack gap={4}>
|
||||
<Text size="sm">
|
||||
Every race you run stays with you.
|
||||
</Text>
|
||||
<Stack gap={3}>
|
||||
<ResultItem text="Your stats, your team, your story — all connected" color="#ef4444" />
|
||||
<ResultItem text="One race result updates your profile, team points, rating, and season history" color="#ef4444" />
|
||||
<ResultItem text="No more fragmented data across spreadsheets and forums" color="#ef4444" />
|
||||
<Box position="relative" bg="graphite-black">
|
||||
<Glow color="primary" size="lg" position="top-right" opacity={0.03} />
|
||||
<AlternatingSection
|
||||
heading="Results That Actually Stay"
|
||||
backgroundImage="/images/ff1600.jpeg"
|
||||
description={
|
||||
<Stack gap={8}>
|
||||
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||
Every race you run stays with you.
|
||||
</Text>
|
||||
<Box display="grid" gridCols={1} gap={3}>
|
||||
<ResultItem text="Your stats, your team, your story — all connected" color="#198CFF" />
|
||||
<ResultItem text="One race result updates your profile, team points, rating, and season history" color="#198CFF" />
|
||||
<ResultItem text="No more fragmented data across spreadsheets and forums" color="#198CFF" />
|
||||
</Box>
|
||||
<Box borderLeft borderStyle="solid" borderColor="telemetry-aqua" pl={4} py={1} bg="telemetry-aqua/5">
|
||||
<Text color="text-gray-500" font="mono" size="xs" uppercase letterSpacing="widest">
|
||||
Your racing career, finally in one place.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text size="sm">
|
||||
Your racing career, finally in one place.
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
mockup={<RaceHistoryMockup />}
|
||||
layout="text-right"
|
||||
/>
|
||||
}
|
||||
mockup={<RaceHistoryMockup />}
|
||||
layout="text-right"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<TelemetryLine color="aqua" height="1px" opacity={0.2} />
|
||||
|
||||
{/* Section 3: Automatic Session Creation */}
|
||||
<AlternatingSection
|
||||
heading="Automatic Session Creation"
|
||||
description={
|
||||
<Stack gap={4}>
|
||||
<Text size="sm">
|
||||
Setting up league races used to mean clicking through iRacing's wizard 20 times.
|
||||
</Text>
|
||||
<Stack gap={3}>
|
||||
<StepItem step={1} text="Our companion app syncs with your league schedule" />
|
||||
<StepItem step={2} text="When it's race time, it creates the iRacing session automatically" />
|
||||
<StepItem step={3} text="No clicking through wizards. No manual setup" />
|
||||
<Box position="relative" bg="graphite-black">
|
||||
<Glow color="amber" size="lg" position="bottom-right" opacity={0.02} />
|
||||
<AlternatingSection
|
||||
heading="Automatic Session Creation"
|
||||
description={
|
||||
<Stack gap={8}>
|
||||
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||
Setting up league races used to mean clicking through iRacing's wizard 20 times.
|
||||
</Text>
|
||||
<Box display="grid" gridCols={1} gap={3}>
|
||||
<StepItem step={1} text="Our companion app syncs with your league schedule" />
|
||||
<StepItem step={2} text="When it's race time, it creates the iRacing session automatically" />
|
||||
<StepItem step={3} text="No clicking through wizards. No manual setup" />
|
||||
</Box>
|
||||
<Box borderLeft borderStyle="solid" borderColor="warning-amber" pl={4} py={1} bg="warning-amber/5">
|
||||
<Text color="text-gray-500" font="mono" size="xs" uppercase letterSpacing="widest">
|
||||
Automation instead of repetition.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Text size="sm">
|
||||
Automation instead of repetition.
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
mockup={<CompanionAutomationMockup />}
|
||||
layout="text-left"
|
||||
/>
|
||||
}
|
||||
mockup={<CompanionAutomationMockup />}
|
||||
layout="text-left"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Section 4: Game-Agnostic Platform */}
|
||||
<AlternatingSection
|
||||
heading="Built for iRacing. Ready for the future."
|
||||
backgroundImage="/images/lmp3.jpeg"
|
||||
description={
|
||||
<Stack gap={4}>
|
||||
<Text size="sm">
|
||||
Right now, we're focused on making iRacing league racing better.
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
But sims come and go. Your leagues, your teams, your rating — those stay.
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
GridPilot is built to outlast any single platform.
|
||||
</Text>
|
||||
<Text size="sm">
|
||||
When the next sim arrives, your competitive identity moves with you.
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
mockup={<SimPlatformMockup />}
|
||||
layout="text-right"
|
||||
/>
|
||||
<Box position="relative" bg="graphite-black">
|
||||
<Glow color="primary" size="xl" position="center" opacity={0.03} />
|
||||
<AlternatingSection
|
||||
heading="Built for iRacing. Ready for the future."
|
||||
backgroundImage="/images/lmp3.jpeg"
|
||||
description={
|
||||
<Stack gap={8}>
|
||||
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||
Right now, we're focused on making iRacing league racing better.
|
||||
</Text>
|
||||
<Text color="text-gray-400" leading="relaxed">
|
||||
But sims come and go. Your leagues, your teams, your rating — those stay.
|
||||
</Text>
|
||||
<Box borderLeft borderStyle="solid" borderColor="border-gray" pl={4} py={1} bg="white/5">
|
||||
<Text color="text-gray-500" font="mono" size="xs" uppercase letterSpacing="widest" leading="relaxed">
|
||||
GridPilot is built to outlast any single platform. When the next sim arrives, your competitive identity moves with you.
|
||||
</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
}
|
||||
mockup={<SimPlatformMockup />}
|
||||
layout="text-right"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Alpha-only discovery section */}
|
||||
<ModeGuard feature="alpha_discovery">
|
||||
<Container size="lg" py={12}>
|
||||
<Stack gap={8}>
|
||||
<Box>
|
||||
<Heading level={2}>Discover the grid</Heading>
|
||||
<Text size="sm" color="text-gray-400" block mt={2}>
|
||||
Explore leagues, teams, and races that make up the GridPilot ecosystem.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Grid cols={3} gap={8}>
|
||||
{/* Top leagues */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Heading level={3} fontSize="sm">Featured leagues</Heading>
|
||||
<Link href={routes.public.leagues}>
|
||||
<Button variant="secondary" size="sm">
|
||||
View all
|
||||
</Button>
|
||||
</Link>
|
||||
</Stack>
|
||||
<Stack gap={3}>
|
||||
{viewData.topLeagues.slice(0, 4).map((league) => (
|
||||
<Box key={league.id}>
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Surface variant="muted" rounded="md" border padding={1} w="2.5rem" h="2.5rem" bg="bg-blue-500/10" borderColor="border-blue-500/30" display="flex" alignItems="center" justifyContent="center">
|
||||
<Text size="xs" weight="bold" color="text-primary-blue">
|
||||
{league.name.split(' ').map((word) => word[0]).join('').slice(0, 3).toUpperCase()}
|
||||
</Text>
|
||||
</Surface>
|
||||
<Box flex={1} minWidth="0">
|
||||
<Text color="text-white" block truncate>{league.name}</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1} truncate>{league.description}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* Teams */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Heading level={3} fontSize="sm">Teams on the grid</Heading>
|
||||
<Link href={routes.public.teams}>
|
||||
<Button variant="secondary" size="sm">
|
||||
Browse teams
|
||||
</Button>
|
||||
</Link>
|
||||
</Stack>
|
||||
<Stack gap={3}>
|
||||
{viewData.teams.slice(0, 4).map(team => (
|
||||
<Box key={team.id}>
|
||||
<Stack direction="row" align="start" gap={3}>
|
||||
<Surface variant="muted" rounded="md" border padding={1} w="2.5rem" h="2.5rem" overflow="hidden" bg="bg-neutral-800">
|
||||
<Image
|
||||
src={team.logoUrl || getMediaUrl('team-logo', team.id)}
|
||||
alt={team.name}
|
||||
width={40}
|
||||
height={40}
|
||||
objectFit="cover"
|
||||
fullWidth
|
||||
fullHeight
|
||||
/>
|
||||
</Surface>
|
||||
<Box flex={1} minWidth="0">
|
||||
<Text color="text-white" block truncate>{team.name}</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1} truncate>{team.description}</Text>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
{/* Upcoming races */}
|
||||
<Card>
|
||||
<Stack gap={4}>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Heading level={3} fontSize="sm">Upcoming races</Heading>
|
||||
<Link href={routes.public.races}>
|
||||
<Button variant="secondary" size="sm">
|
||||
View schedule
|
||||
</Button>
|
||||
</Link>
|
||||
</Stack>
|
||||
{viewData.upcomingRaces.length === 0 ? (
|
||||
<Text size="xs" color="text-gray-400">
|
||||
No races scheduled in this demo snapshot.
|
||||
</Text>
|
||||
) : (
|
||||
<Stack gap={3}>
|
||||
{viewData.upcomingRaces.map(race => (
|
||||
<Box key={race.id}>
|
||||
<Stack direction="row" align="start" justify="between" gap={3}>
|
||||
<Box flex={1} minWidth="0">
|
||||
<Text color="text-white" block truncate>{race.track}</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1} truncate>{race.car}</Text>
|
||||
</Box>
|
||||
<Text size="xs" color="text-gray-500">
|
||||
{race.formattedDate}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Container>
|
||||
<Box bg="panel-gray/20" py={20} borderTop borderBottom borderColor="border-gray/50" position="relative">
|
||||
<Glow color="aqua" size="xl" position="center" opacity={0.02} />
|
||||
<DiscoverySection viewData={viewData} />
|
||||
</Box>
|
||||
</ModeGuard>
|
||||
|
||||
<DiscordCTA />
|
||||
|
||||
Reference in New Issue
Block a user