website refactor
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
|
|
||||||
|
|
||||||
import { useParallax } from "@/hooks/useScrollProgress";
|
import { useParallax } from "@/hooks/useScrollProgress";
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Container } from '@/ui/Container';
|
import { Container } from '@/ui/Container';
|
||||||
import { Heading } from '@/ui/Heading';
|
import { Heading } from '@/ui/Heading';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
|
import { Stack } from '@/ui/Stack';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
interface AlternatingSectionProps {
|
interface AlternatingSectionProps {
|
||||||
@@ -25,8 +24,7 @@ export function AlternatingSection({
|
|||||||
backgroundVideo
|
backgroundVideo
|
||||||
}: AlternatingSectionProps) {
|
}: AlternatingSectionProps) {
|
||||||
const sectionRef = useRef<HTMLElement>(null);
|
const sectionRef = useRef<HTMLElement>(null);
|
||||||
|
const bgParallax = useParallax(sectionRef, 0.1);
|
||||||
const bgParallax = useParallax(sectionRef, 0.2);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -34,96 +32,100 @@ export function AlternatingSection({
|
|||||||
ref={sectionRef}
|
ref={sectionRef}
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
bg="bg-deep-graphite"
|
bg="graphite-black"
|
||||||
px={{ base: 'calc(1rem+var(--sal))', lg: 8 }}
|
py={{ base: 20, md: 32 }}
|
||||||
py={{ base: 20, sm: 24, md: 32 }}
|
className="border-b border-border-gray"
|
||||||
>
|
>
|
||||||
{backgroundVideo && (
|
{backgroundVideo && (
|
||||||
<>
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
inset="0"
|
||||||
|
fullWidth
|
||||||
|
fullHeight
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
as="video"
|
as="video"
|
||||||
autoPlay
|
autoPlay
|
||||||
loop
|
loop
|
||||||
muted
|
muted
|
||||||
playsInline
|
playsInline
|
||||||
position="absolute"
|
|
||||||
inset="0"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
fullHeight
|
fullHeight
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
opacity={0.2}
|
opacity={0.1}
|
||||||
maskImage="radial-gradient(ellipse at center, black 0%, rgba(0,0,0,0.8) 40%, transparent 70%)"
|
|
||||||
webkitMaskImage="radial-gradient(ellipse at center, black 0%, rgba(0,0,0,0.8) 40%, transparent 70%)"
|
|
||||||
>
|
>
|
||||||
<Box as="source" src={backgroundVideo} type="video/mp4" />
|
<Box as="source" src={backgroundVideo} type="video/mp4" />
|
||||||
</Box>
|
</Box>
|
||||||
{/* Racing red accent for sections with background videos */}
|
{/* Dark overlay to ensure readability */}
|
||||||
<Box position="absolute" top="0" left="0" right="0" h="px" bg="linear-gradient(to right, transparent, rgba(239, 68, 68, 0.3), transparent)" />
|
<Box position="absolute" inset="0" bg="linear-gradient(to bottom, #0C0D0F, transparent, #0C0D0F)" />
|
||||||
</>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{backgroundImage && !backgroundVideo && (
|
{backgroundImage && !backgroundVideo && (
|
||||||
<>
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
inset="0"
|
||||||
|
fullWidth
|
||||||
|
fullHeight
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
inset="0"
|
inset="0"
|
||||||
bg={`url(${backgroundImage})`}
|
bg={`url(${backgroundImage})`}
|
||||||
backgroundSize="cover"
|
backgroundSize="cover"
|
||||||
backgroundPosition="center"
|
backgroundPosition="center"
|
||||||
maskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.1) 40%, transparent 70%)"
|
opacity={0.1}
|
||||||
webkitMaskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.1) 40%, transparent 70%)"
|
style={{ transform: `translateY(${bgParallax * 0.3}px)` }}
|
||||||
transform={`translateY(${bgParallax * 0.3}px)`}
|
|
||||||
/>
|
/>
|
||||||
{/* Racing red accent for sections with background images */}
|
{/* Dark overlay to ensure readability */}
|
||||||
<Box position="absolute" top="0" left="0" right="0" h="px" bg="linear-gradient(to right, transparent, rgba(239, 68, 68, 0.3), transparent)" />
|
<Box position="absolute" inset="0" bg="linear-gradient(to bottom, #0C0D0F, transparent, #0C0D0F)" />
|
||||||
</>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Carbon fiber texture on sections without images or videos */}
|
|
||||||
{!backgroundImage && !backgroundVideo && (
|
|
||||||
<Box position="absolute" inset="0" opacity={0.3} bg="carbon-fiber" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Checkered pattern accent */}
|
|
||||||
<Box position="absolute" inset="0" opacity={0.1} bg="checkered-pattern" />
|
|
||||||
|
|
||||||
<Container size="lg" position="relative" zIndex={10}>
|
<Container size="lg" position="relative" zIndex={10}>
|
||||||
<Box display="grid" gridCols={{ base: 1, lg: 2 }} gap={{ base: 8, md: 12, lg: 16 }} alignItems="center">
|
<Box display="grid" gridCols={{ base: 1, lg: 2 }} gap={{ base: 12, lg: 24 }} alignItems="center">
|
||||||
{/* Text Content - Always first on mobile, respects layout on desktop */}
|
{/* Text Content */}
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
gap={{ base: 4, md: 6, lg: 8 }}
|
gap={8}
|
||||||
order={{ lg: layout === 'text-right' ? 2 : 1 }}
|
order={{ lg: layout === 'text-right' ? 2 : 1 }}
|
||||||
>
|
>
|
||||||
<Heading level={2} fontSize={{ base: 'xl', md: '2xl', lg: '3xl', xl: '4xl' }} weight="medium" style={{ background: 'linear-gradient(to right, #dc2626, #ffffff, #2563eb)', backgroundClip: 'text', WebkitBackgroundClip: 'text', color: 'transparent', filter: 'drop-shadow(0 0 15px rgba(220,0,0,0.4))', WebkitTextStroke: '0.5px rgba(220,0,0,0.2)' }}>
|
<Stack gap={4}>
|
||||||
{heading}
|
<Box w="12" h="1" bg="primary-accent" />
|
||||||
</Heading>
|
<Heading level={2} fontSize={{ base: '3xl', md: '4xl' }} weight="bold" className="tracking-tight">
|
||||||
<Box display="flex" flexDirection="column" gap={{ base: 3, md: 5 }}>
|
{heading}
|
||||||
<Text size={{ base: 'sm', md: 'base', lg: 'lg' }} color="text-slate-400" weight="light" leading="relaxed">
|
</Heading>
|
||||||
{description}
|
</Stack>
|
||||||
</Text>
|
<Box className="text-gray-400">
|
||||||
|
{typeof description === 'string' ? (
|
||||||
|
<Text size="lg" leading="relaxed" weight="normal">{description}</Text>
|
||||||
|
) : (
|
||||||
|
description
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Mockup - Always second on mobile, respects layout on desktop */}
|
{/* Mockup */}
|
||||||
<Box
|
<Box
|
||||||
position="relative"
|
position="relative"
|
||||||
order={{ lg: layout === 'text-right' ? 1 : 2 }}
|
order={{ lg: layout === 'text-right' ? 1 : 2 }}
|
||||||
group
|
className="bg-panel-gray/30 border border-border-gray/50 rounded-none p-2 shadow-2xl group"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
fullWidth
|
fullWidth
|
||||||
minHeight={{ base: '240px', md: '380px', lg: '440px' }}
|
minHeight={{ base: '240px', md: '380px' }}
|
||||||
transition
|
className="overflow-hidden rounded-none border border-border-gray/30 bg-graphite-black"
|
||||||
hoverScale
|
|
||||||
maskImage={`linear-gradient(to ${layout === 'text-left' ? 'right' : 'left'}, white 50%, rgba(255,255,255,0.8) 70%, rgba(255,255,255,0.4) 85%, transparent 100%)`}
|
|
||||||
webkitMaskImage={`linear-gradient(to ${layout === 'text-left' ? 'right' : 'left'}, white 50%, rgba(255,255,255,0.8) 70%, rgba(255,255,255,0.4) 85%, transparent 100%)`}
|
|
||||||
>
|
>
|
||||||
{mockup}
|
{mockup}
|
||||||
</Box>
|
</Box>
|
||||||
|
{/* Decorative corner accents */}
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="4" h="4" borderTop borderLeft borderColor="primary-accent/50" />
|
||||||
|
<Box position="absolute" bottom="-1px" right="-1px" w="4" h="4" borderBottom borderRight borderColor="primary-accent/50" />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
123
apps/website/components/landing/DiscoverySection.tsx
Normal file
123
apps/website/components/landing/DiscoverySection.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { routes } from '@/lib/routing/RouteConfig';
|
||||||
|
import { Box } from '@/ui/Box';
|
||||||
|
import { Button } from '@/ui/Button';
|
||||||
|
import { Container } from '@/ui/Container';
|
||||||
|
import { Grid } from '@/ui/Grid';
|
||||||
|
import { Heading } from '@/ui/Heading';
|
||||||
|
import { Link } from '@/ui/Link';
|
||||||
|
import { Stack } from '@/ui/Stack';
|
||||||
|
import { Text } from '@/ui/Text';
|
||||||
|
import { LeagueCard } from '@/ui/LeagueCard';
|
||||||
|
import { TeamCard } from '@/ui/TeamCard';
|
||||||
|
import { UpcomingRaceItem } from '@/ui/UpcomingRaceItem';
|
||||||
|
import { HomeViewData } from '@/templates/HomeTemplate';
|
||||||
|
|
||||||
|
interface DiscoverySectionProps {
|
||||||
|
viewData: HomeViewData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DiscoverySection({ viewData }: DiscoverySectionProps) {
|
||||||
|
return (
|
||||||
|
<Box py={{ base: 20, md: 32 }} bg="graphite-black" borderBottom borderColor="border-gray/50">
|
||||||
|
<Container size="lg">
|
||||||
|
<Stack gap={16}>
|
||||||
|
<Box maxWidth="2xl">
|
||||||
|
<Box borderLeft borderStyle="solid" borderColor="primary-accent" pl={4} mb={4}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-[0.2em]">
|
||||||
|
Live Ecosystem
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Heading level={2} weight="bold" fontSize={{ base: '3xl', md: '4xl' }} className="tracking-tight">
|
||||||
|
Discover the Grid
|
||||||
|
</Heading>
|
||||||
|
<Text size="lg" color="text-gray-400" block mt={6} leading="relaxed">
|
||||||
|
Explore leagues, teams, and races that make up the GridPilot ecosystem.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid cols={1} lgCols={3} gap={12}>
|
||||||
|
{/* Top leagues */}
|
||||||
|
<Stack gap={8}>
|
||||||
|
<Stack direction="row" align="center" justify="between" className="border-b border-border-gray/30 pb-4">
|
||||||
|
<Heading level={5} color="text-gray-400" weight="bold" className="tracking-widest">FEATURED LEAGUES</Heading>
|
||||||
|
<Link href={routes.public.leagues}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-widest hover:text-white transition-colors">View all</Text>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={4}>
|
||||||
|
{viewData.topLeagues.slice(0, 2).map((league) => (
|
||||||
|
<LeagueCard
|
||||||
|
key={league.id}
|
||||||
|
name={league.name}
|
||||||
|
description={league.description}
|
||||||
|
coverUrl="/images/ff1600.jpeg"
|
||||||
|
slotLabel="Drivers"
|
||||||
|
usedSlots={18}
|
||||||
|
maxSlots={24}
|
||||||
|
fillPercentage={75}
|
||||||
|
hasOpenSlots={true}
|
||||||
|
openSlotsCount={6}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* Teams */}
|
||||||
|
<Stack gap={8}>
|
||||||
|
<Stack direction="row" align="center" justify="between" className="border-b border-border-gray/30 pb-4">
|
||||||
|
<Heading level={5} color="text-gray-400" weight="bold" className="tracking-widest">TEAMS ON THE GRID</Heading>
|
||||||
|
<Link href={routes.public.teams}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-widest hover:text-white transition-colors">Browse</Text>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
<Stack gap={4}>
|
||||||
|
{viewData.teams.slice(0, 2).map(team => (
|
||||||
|
<TeamCard
|
||||||
|
key={team.id}
|
||||||
|
name={team.name}
|
||||||
|
description={team.description}
|
||||||
|
logo={team.logoUrl}
|
||||||
|
memberCount={12}
|
||||||
|
isRecruiting={true}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{/* Upcoming races */}
|
||||||
|
<Stack gap={8}>
|
||||||
|
<Stack direction="row" align="center" justify="between" className="border-b border-border-gray/30 pb-4">
|
||||||
|
<Heading level={5} color="text-gray-400" weight="bold" className="tracking-widest">UPCOMING RACES</Heading>
|
||||||
|
<Link href={routes.public.races}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-widest hover:text-white transition-colors">Schedule</Text>
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
{viewData.upcomingRaces.length === 0 ? (
|
||||||
|
<Box p={12} border borderStyle="dashed" borderColor="border-gray/30" rounded="none" center bg="panel-gray/10">
|
||||||
|
<Text size="sm" color="text-gray-600" font="mono" uppercase letterSpacing="widest">
|
||||||
|
No races scheduled.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Stack gap={3}>
|
||||||
|
{viewData.upcomingRaces.map(race => (
|
||||||
|
<UpcomingRaceItem
|
||||||
|
key={race.id}
|
||||||
|
track={race.track}
|
||||||
|
car={race.car}
|
||||||
|
formattedDate={race.formattedDate}
|
||||||
|
formattedTime="20:00 GMT"
|
||||||
|
isMyLeague={false}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { motion } from 'framer-motion';
|
|||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
import { Heading } from '@/ui/Heading';
|
import { Heading } from '@/ui/Heading';
|
||||||
|
import { Stack } from '@/ui/Stack';
|
||||||
import { Icon } from '@/ui/Icon';
|
import { Icon } from '@/ui/Icon';
|
||||||
import { ChevronDown } from 'lucide-react';
|
import { ChevronDown } from 'lucide-react';
|
||||||
|
|
||||||
@@ -47,27 +48,31 @@ function FAQItem({ faq, index }: { faq: typeof faqs[0]; index: number }) {
|
|||||||
transition={{ delay: index * 0.1 }}
|
transition={{ delay: index * 0.1 }}
|
||||||
group
|
group
|
||||||
>
|
>
|
||||||
<Box rounded="lg" bg="bg-iron-gray" border borderColor="border-charcoal-outline" transition hoverBorderColor="border-primary-blue/50" transform={isOpen ? '' : 'translateY(0)'} hoverScale={!isOpen}>
|
<Box rounded="none" bg="panel-gray/40" border borderColor="border-gray/50" transition hoverBorderColor="primary-accent/30">
|
||||||
<Box
|
<Box
|
||||||
as="button"
|
as="button"
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
fullWidth
|
fullWidth
|
||||||
p={{ base: 2, md: 3, lg: 4 }}
|
p={{ base: 4, md: 6 }}
|
||||||
textAlign="left"
|
textAlign="left"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
minHeight="44px"
|
minHeight="44px"
|
||||||
|
className="relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" gap={{ base: 1.5, md: 2 }}>
|
<Box display="flex" alignItems="center" justifyContent="between" gap={{ base: 2, md: 4 }}>
|
||||||
<Heading level={3} fontSize={{ base: 'xs', md: 'sm' }} weight="semibold" color="text-white" groupHoverColor="primary-blue" transition>
|
<Stack direction="row" align="center" gap={4}>
|
||||||
{faq.question}
|
<Box w="1" h="4" bg={isOpen ? "primary-accent" : "border-gray"} transition className="group-hover:bg-primary-accent" />
|
||||||
</Heading>
|
<Heading level={3} fontSize={{ base: 'sm', md: 'base' }} weight="bold" color="text-white" groupHoverColor="primary-accent" transition className="tracking-wide">
|
||||||
|
{faq.question}
|
||||||
|
</Heading>
|
||||||
|
</Stack>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
animate={{ rotate: isOpen ? 180 : 0 }}
|
animate={{ rotate: isOpen ? 180 : 0 }}
|
||||||
transition={{ duration: 0.15, ease: 'easeInOut' }}
|
transition={{ duration: 0.15, ease: 'easeInOut' }}
|
||||||
w={{ base: "3.5", md: "4" }}
|
w={{ base: "4", md: "5" }}
|
||||||
h={{ base: "3.5", md: "4" }}
|
h={{ base: "4", md: "5" }}
|
||||||
color="text-neon-aqua"
|
color={isOpen ? "text-primary-accent" : "text-gray-500"}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
<Icon icon={ChevronDown} size="full" />
|
<Icon icon={ChevronDown} size="full" />
|
||||||
@@ -82,13 +87,13 @@ function FAQItem({ faq, index }: { faq: typeof faqs[0]; index: number }) {
|
|||||||
opacity: isOpen ? 1 : 0
|
opacity: isOpen ? 1 : 0
|
||||||
}}
|
}}
|
||||||
transition={{
|
transition={{
|
||||||
height: { duration: 0.3, ease: [0.34, 1.56, 0.64, 1] },
|
height: { duration: 0.2, ease: 'easeInOut' },
|
||||||
opacity: { duration: 0.2, ease: 'easeInOut' }
|
opacity: { duration: 0.15, ease: 'easeInOut' }
|
||||||
}}
|
}}
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Box px={{ base: 2, md: 3 }} pb={{ base: 2, md: 3 }} pt={1}>
|
<Box px={{ base: 4, md: 6 }} pb={{ base: 4, md: 6 }} pt={0} ml={5}>
|
||||||
<Text size={{ base: 'xs', md: 'xs' }} color="text-gray-300" weight="light" leading="relaxed">
|
<Text size="sm" color="text-gray-400" weight="normal" leading="relaxed" className="max-w-2xl">
|
||||||
{faq.answer}
|
{faq.answer}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -100,7 +105,7 @@ function FAQItem({ faq, index }: { faq: typeof faqs[0]; index: number }) {
|
|||||||
|
|
||||||
export function FAQ() {
|
export function FAQ() {
|
||||||
return (
|
return (
|
||||||
<Box as="section" position="relative" py={{ base: 3, md: 12, lg: 16 }} bg="bg-deep-graphite" overflow="hidden">
|
<Box as="section" position="relative" py={{ base: 20, md: 32 }} bg="graphite-black" overflow="hidden" borderBottom borderColor="border-gray/50">
|
||||||
{/* Background image with mask */}
|
{/* Background image with mask */}
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
@@ -108,20 +113,21 @@ export function FAQ() {
|
|||||||
bg="url(/images/porsche.jpeg)"
|
bg="url(/images/porsche.jpeg)"
|
||||||
backgroundSize="cover"
|
backgroundSize="cover"
|
||||||
backgroundPosition="center"
|
backgroundPosition="center"
|
||||||
maskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.1) 40%, transparent 70%)"
|
opacity={0.03}
|
||||||
webkitMaskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.18) 0%, rgba(0,0,0,0.1) 40%, transparent 70%)"
|
|
||||||
/>
|
/>
|
||||||
{/* Racing red accent */}
|
|
||||||
<Box position="absolute" top="0" left="0" right="0" h="px" bg="linear-gradient(to right, transparent, rgba(239, 68, 68, 0.3), transparent)" />
|
|
||||||
|
|
||||||
<Box maxWidth="3xl" mx="auto" px={{ base: 4, md: 6 }} position="relative" zIndex={10}>
|
<Box maxWidth="4xl" mx="auto" px={{ base: 4, md: 6 }} position="relative" zIndex={10}>
|
||||||
<Box textAlign="center" mb={{ base: 4, md: 8 }}>
|
<Box textAlign="center" mb={{ base: 12, md: 16 }}>
|
||||||
<Heading level={2} fontSize={{ base: 'base', md: 'xl', lg: '2xl' }} weight="semibold" color="text-white" mb={1}>
|
<Box borderLeft borderRight borderStyle="solid" borderColor="primary-accent" px={4} display="inline-block" mb={4}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-[0.2em]">
|
||||||
|
Support & Information
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Heading level={2} fontSize={{ base: '3xl', md: '4xl' }} weight="bold" color="text-white" mb={4} className="tracking-tight">
|
||||||
Frequently Asked Questions
|
Frequently Asked Questions
|
||||||
</Heading>
|
</Heading>
|
||||||
<Box mx="auto" rounded="full" w={{ base: "24", md: "32" }} h="1" bg="linear-gradient(to right, var(--primary-blue), var(--neon-aqua))" />
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" flexDirection="column" gap={{ base: 1.5, md: 2 }}>
|
<Box display="flex" flexDirection="column" gap={4}>
|
||||||
{faqs.map((faq, index) => (
|
{faqs.map((faq, index) => (
|
||||||
<FAQItem key={faq.question} faq={faq} index={index} />
|
<FAQItem key={faq.question} faq={faq} index={index} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -53,50 +53,64 @@ function FeatureCard({ feature, index }: { feature: typeof features[0], index: n
|
|||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
gap={6}
|
gap={6}
|
||||||
group
|
className="p-8 bg-panel-gray/40 border border-border-gray/50 rounded-none hover:border-primary-accent/30 transition-all duration-300 ease-smooth group relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<Box aspectRatio="video" fullWidth position="relative">
|
<Box aspectRatio="video" fullWidth position="relative" className="bg-graphite-black rounded-none overflow-hidden border border-border-gray/30">
|
||||||
<Box position="absolute" inset="-0.5" bg="linear-gradient(to right, rgba(239, 68, 68, 0.2), rgba(59, 130, 246, 0.2), rgba(239, 68, 68, 0.2))" rounded="lg" opacity={0} groupHoverOpacity={1} transition blur="sm" />
|
<MockupStack index={index}>
|
||||||
<Box position="relative">
|
<feature.MockupComponent />
|
||||||
<MockupStack index={index}>
|
</MockupStack>
|
||||||
<feature.MockupComponent />
|
|
||||||
</MockupStack>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Stack gap={3}>
|
<Stack gap={4}>
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
<Heading level={3} weight="medium" style={{ background: 'linear-gradient(to right, #dc2626, #ffffff, #2563eb)', backgroundClip: 'text', WebkitBackgroundClip: 'text', color: 'transparent', filter: 'drop-shadow(0 0 15px rgba(220,0,0,0.4))', WebkitTextStroke: '0.5px rgba(220,0,0,0.2)' }}>
|
<Box w="1" h="4" bg="primary-accent" />
|
||||||
|
<Heading level={3} weight="bold" fontSize="xl" className="tracking-tight">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size={{ base: 'sm', sm: 'base' }} color="text-gray-400" weight="light" leading="relaxed">
|
<Text size="sm" color="text-gray-400" leading="relaxed" weight="normal">
|
||||||
{feature.description}
|
{feature.description}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
{/* Subtle hover effect */}
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
bottom="0"
|
||||||
|
left="0"
|
||||||
|
w="full"
|
||||||
|
h="1"
|
||||||
|
bg="primary-accent"
|
||||||
|
className="scale-x-0 group-hover:scale-x-100 transition-transform duration-500 origin-left"
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeatureGrid() {
|
export function FeatureGrid() {
|
||||||
return (
|
return (
|
||||||
<Section variant="default">
|
<Section className="bg-graphite-black border-b border-border-gray/50 py-24">
|
||||||
<Container position="relative" zIndex={10}>
|
<Container position="relative" zIndex={10}>
|
||||||
<Container size="sm" center>
|
<Stack gap={16}>
|
||||||
<Box>
|
<Box maxWidth="2xl">
|
||||||
<Heading level={2} weight="semibold" style={{ background: 'linear-gradient(to right, #dc2626, #ffffff, #2563eb)', backgroundClip: 'text', WebkitBackgroundClip: 'text', color: 'transparent', filter: 'drop-shadow(0 0 20px rgba(220,0,0,0.5))', WebkitTextStroke: '1px rgba(220,0,0,0.2)' }}>
|
<Box borderLeft borderStyle="solid" borderColor="primary-accent" pl={4} mb={4}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-[0.2em]">
|
||||||
|
Engineered for Competition
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Heading level={2} weight="bold" fontSize={{ base: '3xl', md: '4xl' }} className="tracking-tight">
|
||||||
Building for League Racing
|
Building for League Racing
|
||||||
</Heading>
|
</Heading>
|
||||||
<Text size={{ base: 'base', sm: 'lg' }} color="text-gray-400" block mt={{ base: 4, sm: 6 }}>
|
<Text size="lg" color="text-gray-400" block mt={6} leading="relaxed">
|
||||||
These features are in development. Join the community to help shape what gets built first
|
Every feature is designed to reduce friction and increase immersion. Join our Discord to help shape the future of the platform.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
|
||||||
<Box mx="auto" mt={{ base: 8, sm: 12, md: 16 }} display="grid" gridCols={{ base: 1, lg: 2, xl: 3 }} gap={{ base: 10, sm: 12, md: 16 }} maxWidth={{ base: '2xl', lg: 'none' }}>
|
<Box display="grid" gridCols={{ base: 1, md: 2, lg: 3 }} gap={8}>
|
||||||
{features.map((feature, index) => (
|
{features.map((feature, index) => (
|
||||||
<FeatureCard key={feature.title} feature={feature} index={index} />
|
<FeatureCard key={feature.title} feature={feature} index={index} />
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useParallax } from '@/hooks/useScrollProgress';
|
import { useParallax } from '@/hooks/useScrollProgress';
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
@@ -7,17 +6,13 @@ import { Container } from '@/ui/Container';
|
|||||||
import { Heading } from '@/ui/Heading';
|
import { Heading } from '@/ui/Heading';
|
||||||
import { Stack } from '@/ui/Stack';
|
import { Stack } from '@/ui/Stack';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
|
import { Glow } from '@/ui/Glow';
|
||||||
|
|
||||||
const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#';
|
const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#';
|
||||||
|
|
||||||
if (!process.env.NEXT_PUBLIC_DISCORD_URL) {
|
|
||||||
console.warn('NEXT_PUBLIC_DISCORD_URL is not set. Discord button will use "#" as fallback.');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LandingHero() {
|
export function LandingHero() {
|
||||||
const sectionRef = useRef<HTMLElement>(null);
|
const sectionRef = useRef<HTMLElement>(null);
|
||||||
|
const bgParallax = useParallax(sectionRef, 0.2);
|
||||||
const bgParallax = useParallax(sectionRef, 0.3);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -25,11 +20,10 @@ export function LandingHero() {
|
|||||||
ref={sectionRef}
|
ref={sectionRef}
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
bg="bg-deep-graphite"
|
bg="graphite-black"
|
||||||
px={{ base: 'calc(1.5rem+var(--sal))', lg: 8 }}
|
pt={{ base: 24, md: 32, lg: 40 }}
|
||||||
pt={{ base: 'calc(3rem+var(--sat))', sm: 'calc(4rem+var(--sat))' }}
|
pb={{ base: 16, md: 24, lg: 32 }}
|
||||||
pb={{ base: 16, sm: 24 }}
|
className="border-b border-border-gray"
|
||||||
py={{ md: 32 }}
|
|
||||||
>
|
>
|
||||||
{/* Background image layer with parallax */}
|
{/* Background image layer with parallax */}
|
||||||
<Box
|
<Box
|
||||||
@@ -38,131 +32,93 @@ export function LandingHero() {
|
|||||||
bg="url(/images/header.jpeg)"
|
bg="url(/images/header.jpeg)"
|
||||||
backgroundSize="cover"
|
backgroundSize="cover"
|
||||||
backgroundPosition="center"
|
backgroundPosition="center"
|
||||||
maskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.35) 40%, transparent 70%)"
|
opacity={0.2}
|
||||||
webkitMaskImage="radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.35) 40%, transparent 70%)"
|
style={{ transform: `translateY(${bgParallax * 0.5}px)` }}
|
||||||
transform={`translateY(${bgParallax * 0.5}px)`}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Racing red accent gradient */}
|
{/* Robust gradient overlay */}
|
||||||
<Box position="absolute" top="0" left="0" right="0" h="1" bg="linear-gradient(to right, transparent, rgba(220, 38, 38, 0.4), transparent)" />
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
inset="0"
|
||||||
|
bg="linear-gradient(to bottom, #0C0D0F 0%, transparent 50%, #0C0D0F 100%)"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Racing stripes background */}
|
<Box
|
||||||
<Box position="absolute" inset="0" opacity={0.3} bg="racing-stripes" />
|
position="absolute"
|
||||||
|
inset="0"
|
||||||
|
bg="radial-gradient(circle at center, transparent 0%, #0C0D0F 100%)"
|
||||||
|
opacity={0.6}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Checkered pattern overlay */}
|
<Glow color="primary" size="xl" position="center" opacity={0.1} />
|
||||||
<Box position="absolute" inset="0" opacity={0.2} bg="checkered-pattern" />
|
|
||||||
|
|
||||||
{/* Speed lines - left side */}
|
<Container size="lg" position="relative" zIndex={10}>
|
||||||
<Box position="absolute" left="0" top="1/4" w="32" h="px" bg="linear-gradient(to right, transparent, rgba(59, 130, 246, 0.3))" className="animate-speed-lines" style={{ animationDelay: '0s' }} />
|
<Stack gap={{ base: 8, md: 12 }}>
|
||||||
<Box position="absolute" left="0" top="1/3" w="24" h="px" bg="linear-gradient(to right, transparent, rgba(59, 130, 246, 0.2))" className="animate-speed-lines" style={{ animationDelay: '0.3s' }} />
|
<Stack gap={6} maxWidth="3xl">
|
||||||
<Box position="absolute" left="0" top="2/5" w="28" h="px" bg="linear-gradient(to right, transparent, rgba(59, 130, 246, 0.25))" className="animate-speed-lines" style={{ animationDelay: '0.6s' }} />
|
<Box borderLeft borderStyle="solid" borderColor="primary-accent" pl={4} mb={2}>
|
||||||
|
<Text size="xs" weight="bold" color="text-primary-accent" className="uppercase tracking-[0.2em]">
|
||||||
{/* Carbon fiber accent - bottom */}
|
Precision Racing Infrastructure
|
||||||
<Box position="absolute" bottom="0" left="0" right="0" h="32" opacity={0.5} bg="carbon-fiber" />
|
</Text>
|
||||||
|
|
||||||
{/* Radial gradient overlay with racing red accent */}
|
|
||||||
<Box position="absolute" inset="0" bg="radial-gradient(circle at center, rgba(220, 38, 38, 0.05), rgba(59, 130, 246, 0.05), transparent)" opacity={0.6} pointerEvents="none" />
|
|
||||||
|
|
||||||
<Container size="sm" position="relative" zIndex={10}>
|
|
||||||
<Stack gap={{ base: 6, sm: 8, md: 12 }}>
|
|
||||||
<Heading
|
|
||||||
level={1}
|
|
||||||
fontSize={{ base: '2xl', sm: '4xl', md: '5xl', lg: '6xl' }}
|
|
||||||
weight="semibold"
|
|
||||||
style={{
|
|
||||||
background: 'linear-gradient(to right, #dc2626, #ffffff, #2563eb)',
|
|
||||||
backgroundClip: 'text',
|
|
||||||
WebkitBackgroundClip: 'text',
|
|
||||||
color: 'transparent',
|
|
||||||
filter: 'drop-shadow(0 0 15px rgba(220,0,0,0.4))',
|
|
||||||
WebkitTextStroke: '0.5px rgba(220,0,0,0.2)'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
League racing is incredible. What's missing is everything around it.
|
|
||||||
</Heading>
|
|
||||||
<Box
|
|
||||||
display="flex"
|
|
||||||
flexDirection="column"
|
|
||||||
gap={{ base: 4, sm: 6 }}
|
|
||||||
>
|
|
||||||
<Text size={{ base: 'sm', md: 'lg' }} align={{ base: 'left', md: 'center' }} color="text-slate-200" weight="light">
|
|
||||||
If you've been in any league, you know the feeling:
|
|
||||||
</Text>
|
|
||||||
{/* Problem badges - mobile optimized */}
|
|
||||||
<Box display="flex" flexDirection={{ base: 'col', sm: 'row' }} flexWrap="wrap" gap={{ base: 2, sm: 3 }} alignItems={{ base: 'stretch', sm: 'center' }} justifyContent="center" maxWidth="2xl" mx="auto">
|
|
||||||
{[
|
|
||||||
'Results scattered across Discord',
|
|
||||||
'No long-term identity',
|
|
||||||
'No career progression',
|
|
||||||
'Forgotten after each season'
|
|
||||||
].map((text) => (
|
|
||||||
<Box
|
|
||||||
key={text}
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
gap={2.5}
|
|
||||||
px={{ base: 3, sm: 5 }}
|
|
||||||
py={2.5}
|
|
||||||
bg="linear-gradient(to bottom right, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.8))"
|
|
||||||
border
|
|
||||||
borderColor="border-red-500/20"
|
|
||||||
rounded="lg"
|
|
||||||
transition
|
|
||||||
hoverBorderColor="border-red-500/40"
|
|
||||||
hoverScale
|
|
||||||
>
|
|
||||||
<Text color="text-red-500" weight="semibold">×</Text>
|
|
||||||
<Text size={{ base: 'sm', sm: 'base' }} weight="medium" color="text-slate-100">{text}</Text>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
</Box>
|
||||||
<Text size={{ base: 'sm', md: 'lg' }} align={{ base: 'left', md: 'center' }} color="text-slate-200" weight="light">
|
<Heading
|
||||||
The ecosystem isn't built for this.
|
level={1}
|
||||||
|
fontSize={{ base: '3xl', sm: '4xl', md: '5xl', lg: '7xl' }}
|
||||||
|
weight="bold"
|
||||||
|
className="text-white leading-[1.1] tracking-tight"
|
||||||
|
>
|
||||||
|
Modern Motorsport <br />
|
||||||
|
<span className="text-primary-accent">Infrastructure.</span>
|
||||||
|
</Heading>
|
||||||
|
<Text size={{ base: 'lg', md: 'xl' }} color="text-gray-400" weight="normal" leading="relaxed" className="max-w-2xl">
|
||||||
|
GridPilot gives your league racing a real home. Results, standings, teams, and career progression — engineered for precision and control.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size={{ base: 'sm', md: 'lg' }} align={{ base: 'left', md: 'center' }} color="text-white" weight="semibold">
|
</Stack>
|
||||||
GridPilot gives your league racing a real home.
|
|
||||||
</Text>
|
<Box display="flex" flexDirection={{ base: 'column', sm: 'row' }} gap={4}>
|
||||||
</Box>
|
|
||||||
<Box display="flex" alignItems="center" justifyContent="center">
|
|
||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
href={discordUrl}
|
href={discordUrl}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
px={8}
|
|
||||||
py={4}
|
|
||||||
size="lg"
|
size="lg"
|
||||||
bg="bg-[#5865F2]"
|
className="px-10 rounded-none uppercase tracking-widest text-xs font-bold"
|
||||||
hoverBg="bg-[#4752C4]"
|
|
||||||
shadow="0 0 20px rgba(88,101,242,0.3)"
|
|
||||||
hoverScale
|
|
||||||
transition
|
|
||||||
aria-label="Join us on Discord"
|
|
||||||
>
|
>
|
||||||
{/* Discord Logo SVG */}
|
Join the Grid
|
||||||
<Box
|
|
||||||
as="svg"
|
|
||||||
w="7"
|
|
||||||
h="7"
|
|
||||||
viewBox="0 0 71 55"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
transition
|
|
||||||
groupHoverScale
|
|
||||||
>
|
|
||||||
<g clipPath="url(#clip0)">
|
|
||||||
<path
|
|
||||||
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<clipPath id="clip0">
|
|
||||||
<rect width="71" height="55" fill="white"/>
|
|
||||||
</clipPath>
|
|
||||||
</defs>
|
|
||||||
</Box>
|
|
||||||
<Text>Join us on Discord</Text>
|
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="lg"
|
||||||
|
className="px-10 rounded-none border-border-gray hover:border-primary-accent/50 uppercase tracking-widest text-xs font-bold"
|
||||||
|
>
|
||||||
|
Explore Leagues
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Problem list - more professional */}
|
||||||
|
<Box
|
||||||
|
display="grid"
|
||||||
|
gridCols={{ base: 1, sm: 2, lg: 4 }}
|
||||||
|
gap={8}
|
||||||
|
mt={12}
|
||||||
|
className="border-t border-border-gray/30 pt-12"
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
{ label: 'IDENTITY', text: 'Your racing career in one place', color: 'primary' },
|
||||||
|
{ label: 'AUTOMATION', text: 'No more manual session setup', color: 'aqua' },
|
||||||
|
{ label: 'PRECISION', text: 'Real-time results and standings', color: 'amber' },
|
||||||
|
{ label: 'COMMUNITY', text: 'Built for teams and leagues', color: 'green' }
|
||||||
|
].map((item) => (
|
||||||
|
<Stack key={item.label} gap={3} className="group">
|
||||||
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
|
<Box w="2" h="2" rounded="full" className={`bg-${item.color === 'primary' ? 'primary-accent' : item.color === 'aqua' ? 'telemetry-aqua' : item.color === 'amber' ? 'warning-amber' : 'success-green'}`} />
|
||||||
|
<Text size="xs" weight="bold" color="text-gray-500" className="uppercase tracking-[0.2em] group-hover:text-white transition-colors">
|
||||||
|
{item.label}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<Text size="sm" color="text-gray-400" className="group-hover:text-gray-200 transition-colors">
|
||||||
|
{item.text}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Check } from 'lucide-react';
|
|
||||||
import { Stack } from '@/ui/Stack';
|
|
||||||
import { Text } from '@/ui/Text';
|
|
||||||
import { Surface } from '@/ui/Surface';
|
|
||||||
import { Icon } from '@/ui/Icon';
|
|
||||||
|
|
||||||
export function FeatureItem({ text }: { text: string }) {
|
|
||||||
return (
|
|
||||||
<Surface variant="muted" rounded="lg" border padding={4} bg="rgba(15, 23, 42, 0.6)" borderColor="rgba(51, 65, 85, 0.4)">
|
|
||||||
<Stack direction="row" align="start" gap={3}>
|
|
||||||
<Surface variant="muted" rounded="lg" padding={2} bg="rgba(59, 130, 246, 0.1)" border borderColor="rgba(59, 130, 246, 0.3)">
|
|
||||||
<Icon icon={Check} size={5} color="#3b82f6" />
|
|
||||||
</Surface>
|
|
||||||
<Text color="text-slate-200" leading="relaxed" weight="light">
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Surface>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ResultItem({ text, color }: { text: string, color: string }) {
|
|
||||||
return (
|
|
||||||
<Surface variant="muted" rounded="lg" border padding={4} bg="rgba(15, 23, 42, 0.6)" borderColor="rgba(51, 65, 85, 0.4)">
|
|
||||||
<Stack direction="row" align="start" gap={3}>
|
|
||||||
<Surface variant="muted" rounded="lg" padding={2} bg={`${color}1A`} border borderColor={`${color}4D`}>
|
|
||||||
<Icon icon={Check} size={5} color={color} />
|
|
||||||
</Surface>
|
|
||||||
<Text color="text-slate-200" leading="relaxed" weight="light">
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Surface>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function StepItem({ step, text }: { step: number, text: string }) {
|
|
||||||
return (
|
|
||||||
<Surface variant="muted" rounded="lg" border padding={4} bg="rgba(15, 23, 42, 0.7)" borderColor="rgba(51, 65, 85, 0.5)">
|
|
||||||
<Stack direction="row" align="start" gap={3}>
|
|
||||||
<Surface variant="muted" rounded="lg" padding={2} bg="rgba(59, 130, 246, 0.1)" border borderColor="rgba(59, 130, 246, 0.4)" w="10" h="10" display="flex" center>
|
|
||||||
<Text weight="bold" size="sm" color="text-primary-blue">{step}</Text>
|
|
||||||
</Surface>
|
|
||||||
<Text color="text-slate-200" leading="relaxed" weight="light">
|
|
||||||
{text}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
</Surface>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -5,21 +5,34 @@ import { Text } from '@/ui/Text';
|
|||||||
|
|
||||||
export function HeaderContent() {
|
export function HeaderContent() {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between h-16 md:h-20">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-6">
|
||||||
<Link href="/" className="inline-flex items-center">
|
<Link href="/" className="inline-flex items-center group">
|
||||||
<Image
|
<div className="relative">
|
||||||
src="/images/logos/wordmark-rectangle-dark.svg"
|
<Image
|
||||||
alt="GridPilot"
|
src="/images/logos/wordmark-rectangle-dark.svg"
|
||||||
width={160}
|
alt="GridPilot"
|
||||||
height={30}
|
width={160}
|
||||||
className="h-6 w-auto md:h-8"
|
height={30}
|
||||||
priority
|
className="h-6 w-auto md:h-7 transition-opacity group-hover:opacity-80"
|
||||||
/>
|
priority
|
||||||
|
/>
|
||||||
|
<div className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary-accent transition-all group-hover:w-full" />
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Text size="sm" color="text-gray-400" className="hidden sm:block font-light">
|
<div className="hidden sm:flex items-center space-x-2 border-l border-border-gray/50 pl-6">
|
||||||
Making league racing less chaotic
|
<div className="w-1.5 h-1.5 rounded-full bg-primary-accent animate-pulse" />
|
||||||
</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-[0.2em] font-mono">
|
||||||
|
Motorsport Infrastructure
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="hidden md:flex items-center space-x-1 px-3 py-1 border border-border-gray/30 bg-panel-gray/20">
|
||||||
|
<Text size="xs" color="text-gray-600" weight="bold" font="mono" className="uppercase">Status:</Text>
|
||||||
|
<Text size="xs" color="text-success-green" weight="bold" font="mono" className="uppercase">Operational</Text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function CareerProgressionMockup() {
|
|||||||
// Simple mobile version - just the essence
|
// Simple mobile version - just the essence
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite to-iron-gray" rounded="lg" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -43,8 +43,8 @@ export function CareerProgressionMockup() {
|
|||||||
{ value: '48', label: 'Podiums' },
|
{ value: '48', label: 'Podiums' },
|
||||||
{ value: '156', label: 'Races' }
|
{ value: '156', label: 'Races' }
|
||||||
].map((stat, i) => (
|
].map((stat, i) => (
|
||||||
<Box key={i} bg="bg-iron-gray/60" rounded="lg" p={3} border borderColor="border-primary-blue/20" textAlign="center">
|
<Box key={i} bg="panel-gray/60" rounded="none" p={3} border borderColor="primary-accent/20" textAlign="center">
|
||||||
<Text size="2xl" weight="bold" color="text-primary-blue" font="mono" block>{stat.value}</Text>
|
<Text size="2xl" weight="bold" color="text-primary-accent" font="mono" block>{stat.value}</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '11px' }}
|
style={{ fontSize: '11px' }}
|
||||||
@@ -52,6 +52,8 @@ export function CareerProgressionMockup() {
|
|||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
mt={1}
|
mt={1}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
|
uppercase
|
||||||
>
|
>
|
||||||
{stat.label}
|
{stat.label}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -61,11 +63,11 @@ export function CareerProgressionMockup() {
|
|||||||
|
|
||||||
{/* Single elegant season card */}
|
{/* Single elegant season card */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box bg="bg-iron-gray/40" rounded="lg" p={3} border borderColor="border-charcoal-outline">
|
<Box bg="panel-gray/40" rounded="none" p={3} border borderColor="border-gray/50">
|
||||||
<Box display="flex" alignItems="center" justifyContent="between">
|
<Box display="flex" alignItems="center" justifyContent="between">
|
||||||
<Text size="sm" color="text-white" opacity={0.7}>GT3 Championship</Text>
|
<Text size="sm" color="text-white" opacity={0.7} weight="bold">GT3 Championship</Text>
|
||||||
<Box as="span" px={2.5} py={1} bg="bg-performance-green/20" rounded="sm">
|
<Box as="span" px={2.5} py={1} bg="success-green/10" border borderColor="success-green/30">
|
||||||
<Text size="xs" color="text-performance-green" weight="semibold">P2</Text>
|
<Text size="xs" color="text-success-green" weight="bold" font="mono">P2</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -78,7 +80,7 @@ export function CareerProgressionMockup() {
|
|||||||
|
|
||||||
// Desktop version - more detailed
|
// Desktop version - more detailed
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -89,26 +91,29 @@ export function CareerProgressionMockup() {
|
|||||||
>
|
>
|
||||||
<Stack gap={{ base: 1.5, sm: 3, md: 4, lg: 6 }}>
|
<Stack gap={{ base: 1.5, sm: 3, md: 4, lg: 6 }}>
|
||||||
{/* Driver Header */}
|
{/* Driver Header */}
|
||||||
<Box as={motion.div} variants={itemVariants} display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} pb={{ base: 1.5, sm: 3, md: 4, lg: 6 }} borderBottom borderColor="border-charcoal-outline">
|
<Box as={motion.div} variants={itemVariants} display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} pb={{ base: 1.5, sm: 3, md: 4, lg: 6 }} borderBottom borderColor="border-gray/50">
|
||||||
<Box h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} bg="bg-charcoal-outline" rounded="full" border borderWidth="2px" borderColor="border-primary-blue/30" display="flex" alignItems="center" justifyContent="center" overflow="hidden">
|
<Box h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} bg="panel-gray" rounded="none" border borderWidth="1px" borderColor="primary-accent/30" display="flex" alignItems="center" justifyContent="center" overflow="hidden" className="relative">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏎️</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏎️</Text>
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" opacity={0.9} mb={{ base: 1, sm: 1.5, md: 2 }} block>Your Racing Identity</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="bold" color="text-white" opacity={0.9} mb={{ base: 1, sm: 1.5, md: 2 }} block className="uppercase tracking-widest">Your Racing Identity</Text>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 2, md: 3 }}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-primary-blue"
|
color="text-primary-accent"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Multi-league profile
|
Multi-league profile
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-performance-green"
|
color="text-success-green"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Career tracking
|
Career tracking
|
||||||
</Text>
|
</Text>
|
||||||
@@ -118,7 +123,7 @@ export function CareerProgressionMockup() {
|
|||||||
|
|
||||||
{/* Career Stats */}
|
{/* Career Stats */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" opacity={0.6} mb={{ base: 1, sm: 2, md: 3 }} block>Career Overview</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="bold" color="text-gray-500" mb={{ base: 1, sm: 2, md: 3 }} block className="uppercase tracking-[0.2em]">Career Overview</Text>
|
||||||
<Box display="grid" gridCols={3} gap={{ base: 1, sm: 2, md: 3 }}>
|
<Box display="grid" gridCols={3} gap={{ base: 1, sm: 2, md: 3 }}>
|
||||||
{[
|
{[
|
||||||
{ label: 'Wins', value: '24' },
|
{ label: 'Wins', value: '24' },
|
||||||
@@ -128,19 +133,19 @@ export function CareerProgressionMockup() {
|
|||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/40"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1, sm: 2, md: 3 }}
|
p={{ base: 1, sm: 2, md: 3 }}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
y: -2,
|
y: -2,
|
||||||
boxShadow: '0 4px 16px rgba(0,0,0,0.3)',
|
borderColor: '#198CFF80',
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text size={{ base: 'sm', sm: 'base', md: 'lg' }} weight="bold" color="text-primary-blue" font="mono" mb={0.5} block>{stat.value}</Text>
|
<Text size={{ base: 'sm', sm: 'base', md: 'lg' }} weight="bold" color="text-primary-accent" font="mono" mb={0.5} block>{stat.value}</Text>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-white" opacity={0.4} block>{stat.label}</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-gray-500" weight="bold" className="uppercase tracking-widest" block>{stat.label}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -148,7 +153,7 @@ export function CareerProgressionMockup() {
|
|||||||
|
|
||||||
{/* Season Timeline */}
|
{/* Season Timeline */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" opacity={0.6} mb={{ base: 1, sm: 2, md: 3 }} block>Season History</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="bold" color="text-gray-500" mb={{ base: 1, sm: 2, md: 3 }} block className="uppercase tracking-[0.2em]">Season History</Text>
|
||||||
<Stack gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Stack gap={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
{[
|
{[
|
||||||
{ league: 'GT3 Championship', season: 'S3', position: 'P2', points: '248' },
|
{ league: 'GT3 Championship', season: 'S3', position: 'P2', points: '248' },
|
||||||
@@ -161,30 +166,30 @@ export function CareerProgressionMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 1.5, sm: 2, md: 3 }}
|
gap={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/20"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1.5, sm: 2, md: 3 }}
|
p={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/30"
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
x: 4,
|
x: 4,
|
||||||
boxShadow: '0 2px 12px rgba(25,140,255,0.2)',
|
borderColor: '#198CFF50',
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box h={{ base: 5, sm: 6, md: 8 }} w={{ base: 5, sm: 6, md: 8 }} bg="bg-charcoal-outline" rounded border borderColor="border-primary-blue/20" display="flex" alignItems="center" justifyContent="center">
|
<Box h={{ base: 5, sm: 6, md: 8 }} w={{ base: 5, sm: 6, md: 8 }} bg="panel-gray" rounded="none" border borderColor="primary-accent/20" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }}>🏁</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }}>🏁</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-white" opacity={0.8} mb={{ base: 0.5, sm: 1 }} block>{season.league}</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-white" weight="bold" mb={{ base: 0.5, sm: 1 }} block>{season.league}</Text>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-white" opacity={0.4} block>Season complete</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-gray-500" font="mono" uppercase block>Season complete</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box display="flex" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="bg-performance-green/20" rounded="sm">
|
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="success-green/10" border borderColor="success-green/30">
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-performance-green" weight="semibold">{season.position}</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-success-green" weight="bold" font="mono">{season.position}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="bg-primary-blue/20" rounded="sm">
|
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="primary-accent/10" border borderColor="primary-accent/30">
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-primary-blue" font="mono">{season.points}</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-primary-accent" font="mono" weight="bold">{season.points}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -194,18 +199,18 @@ export function CareerProgressionMockup() {
|
|||||||
|
|
||||||
{/* Multi-League Badge */}
|
{/* Multi-League Badge */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }} bg="bg-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 2, md: 3 }} border borderColor="border-primary-blue/30">
|
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }} bg="panel-gray/40" rounded="none" p={{ base: 1.5, sm: 2, md: 3 }} border borderColor="primary-accent/20">
|
||||||
<Box display="flex"
|
<Box display="flex"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
className="-space-x-2"
|
className="-space-x-2"
|
||||||
>
|
>
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<Box key={i} h={{ base: 4, sm: 5, md: 6 }} w={{ base: 4, sm: 5, md: 6 }} bg="bg-iron-gray" rounded="full" border borderWidth="2px" borderColor="border-charcoal-outline" display="flex" alignItems="center" justifyContent="center">
|
<Box key={i} h={{ base: 4, sm: 5, md: 6 }} w={{ base: 4, sm: 5, md: 6 }} bg="panel-gray" rounded="full" border borderWidth="2px" borderColor="border-gray" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }}>🏆</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }}>🏆</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} color="text-white" opacity={0.6}>Active in 3 leagues across seasons</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} color="text-gray-400" weight="medium">Active in 3 leagues across seasons</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
import { motion, useReducedMotion } from 'framer-motion';
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Check } from 'lucide-react';
|
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
import { Stack } from '@/ui/Stack';
|
import { Stack } from '@/ui/Stack';
|
||||||
import { Heading } from '@/ui/Heading';
|
import { Heading } from '@/ui/Heading';
|
||||||
import { Icon } from '@/ui/Icon';
|
|
||||||
|
|
||||||
export function CompanionAutomationMockup() {
|
export function CompanionAutomationMockup() {
|
||||||
const shouldReduceMotion = useReducedMotion();
|
const shouldReduceMotion = useReducedMotion();
|
||||||
@@ -23,7 +21,7 @@ export function CompanionAutomationMockup() {
|
|||||||
// Simple mobile version - just the essence of automation
|
// Simple mobile version - just the essence of automation
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite to-iron-gray" rounded="lg" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
@@ -32,39 +30,41 @@ export function CompanionAutomationMockup() {
|
|||||||
>
|
>
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
{/* Simple progress indicator */}
|
{/* Simple progress indicator */}
|
||||||
<Box bg="bg-iron-gray/60" rounded="xl" p={4} border borderColor="border-primary-blue/40">
|
<Box bg="panel-gray/60" rounded="none" p={4} border borderColor="primary-accent/40">
|
||||||
<Box display="flex" alignItems="center" gap={3} mb={3}>
|
<Box display="flex" alignItems="center" gap={3} mb={3}>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
h="8"
|
h="8"
|
||||||
w="8"
|
w="8"
|
||||||
bg="bg-performance-green/40"
|
bg="success-green/20"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
borderColor="border-performance-green/60"
|
borderColor="success-green/40"
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
animate={shouldReduceMotion ? {} : {
|
animate={shouldReduceMotion ? {} : {
|
||||||
scale: [1, 1.1, 1],
|
scale: [1, 1.05, 1],
|
||||||
opacity: [0.6, 1, 0.6]
|
opacity: [0.6, 1, 0.6]
|
||||||
}}
|
}}
|
||||||
transition={{ duration: 1.5, repeat: Infinity }}
|
transition={{ duration: 1.5, repeat: Infinity }}
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
<Box h="3" w="3" bg="bg-performance-green" rounded="full" />
|
<Box h="2" w="2" bg="success-green" />
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="success-green" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" color="text-white" weight="medium" block>Creating Session</Text>
|
<Text size="sm" color="text-white" weight="bold" block className="uppercase tracking-widest">Creating Session</Text>
|
||||||
<Text size="xs" color="text-white" opacity={0.5} block>Automated</Text>
|
<Text size="xs" color="text-gray-500" block font="mono">AUTOMATED</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box h="2.5" fullWidth bg="bg-white/5" rounded="full" overflow="hidden">
|
<Box h="1" fullWidth bg="white/5" rounded="none" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
h="full"
|
h="full"
|
||||||
bg="bg-primary-blue/60"
|
bg="primary-accent"
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: '75%' }}
|
animate={{ width: '75%' }}
|
||||||
transition={{ duration: 2, ease: 'easeInOut' }}
|
transition={{ duration: 2, ease: 'easeInOut' }}
|
||||||
@@ -74,8 +74,8 @@ export function CompanionAutomationMockup() {
|
|||||||
|
|
||||||
{/* Simple CTA */}
|
{/* Simple CTA */}
|
||||||
<Box display="flex" justifyContent="center">
|
<Box display="flex" justifyContent="center">
|
||||||
<Box bg="bg-primary-blue/20" color="text-primary-blue" px={6} py={2.5} rounded="lg" border borderColor="border-primary-blue/40">
|
<Box bg="primary-accent/10" color="text-primary-accent" px={6} py={2.5} rounded="none" border borderColor="primary-accent/30">
|
||||||
<Text size="sm" weight="semibold">One Click</Text>
|
<Text size="xs" weight="bold" className="uppercase tracking-[0.2em]">One Click</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -86,7 +86,7 @@ export function CompanionAutomationMockup() {
|
|||||||
|
|
||||||
// Desktop version - richer with more automation steps
|
// Desktop version - richer with more automation steps
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 3, md: 4, lg: 6 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 3, md: 4, lg: 6 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -103,12 +103,14 @@ export function CompanionAutomationMockup() {
|
|||||||
{/* Companion App Header - Enhanced */}
|
{/* Companion App Header - Enhanced */}
|
||||||
<Box as={motion.div} variants={{ hidden: { opacity: 0, y: -10 }, visible: { opacity: 1, y: 0 } }}>
|
<Box as={motion.div} variants={{ hidden: { opacity: 0, y: -10 }, visible: { opacity: 1, y: 0 } }}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 2.5, md: 3, lg: 4 }} mb={{ base: 3, md: 4, lg: 5 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 2.5, md: 3, lg: 4 }} mb={{ base: 3, md: 4, lg: 5 }}>
|
||||||
<Box h={{ base: 10, md: 12, lg: 14 }} w={{ base: 10, md: 12, lg: 14 }} bg="bg-primary-blue/20" rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/40" display="flex" alignItems="center" justifyContent="center" shadow="lg">
|
<Box h={{ base: 10, md: 12, lg: 14 }} w={{ base: 10, md: 12, lg: 14 }} bg="primary-accent/10" rounded="none" border borderWidth="1px" borderColor="primary-accent/30" display="flex" alignItems="center" justifyContent="center" className="relative">
|
||||||
<Box h={{ base: 6, md: 7, lg: 8 }} w={{ base: 6, md: 7, lg: 8 }} bg="bg-primary-blue/60" rounded="sm" />
|
<Box h={{ base: 6, md: 7, lg: 8 }} w={{ base: 6, md: 7, lg: 8 }} bg="primary-accent/40" />
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
|
<Box position="absolute" bottom="-1px" right="-1px" w="2" h="2" borderBottom borderRight borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Heading level={2} fontSize={{ base: 'base', md: 'lg', lg: 'xl' }} weight="semibold" color="text-white">GridPilot Companion</Heading>
|
<Heading level={2} fontSize={{ base: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" className="uppercase tracking-widest">GridPilot Companion</Heading>
|
||||||
<Text size={{ base: 'xs', md: 'sm', lg: 'base' }} color="text-white" opacity={0.5} block>Automated Session Creator</Text>
|
<Text size={{ base: 'xs', md: 'sm', lg: 'base' }} color="text-gray-500" block font="mono">AUTOMATED SESSION CREATOR</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -118,26 +120,25 @@ export function CompanionAutomationMockup() {
|
|||||||
as={motion.div}
|
as={motion.div}
|
||||||
variants={{ hidden: { opacity: 0, y: 10 }, visible: { opacity: 1, y: 0 } }}
|
variants={{ hidden: { opacity: 0, y: 10 }, visible: { opacity: 1, y: 0 } }}
|
||||||
position="relative"
|
position="relative"
|
||||||
bg="bg-charcoal-outline"
|
bg="panel-gray/40"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 3, md: 4, lg: 5 }}
|
p={{ base: 3, md: 4, lg: 5 }}
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
borderColor="border-primary-blue/40"
|
borderColor="border-gray/50"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
{/* Browser Window Mockup */}
|
{/* Browser Window Mockup */}
|
||||||
<Stack gap={{ base: 3, md: 4 }}>
|
<Stack gap={{ base: 3, md: 4 }}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 2, md: 2.5 }} pb={{ base: 3, md: 4 }} borderBottom borderColor="border-white/10">
|
<Box display="flex" alignItems="center" gap={{ base: 2, md: 2.5 }} pb={{ base: 3, md: 4 }} borderBottom borderColor="border-gray/30">
|
||||||
<Box h={{ base: 2.5, md: 3 }} w={{ base: 2.5, md: 3 }} bg="bg-red-500/60" rounded="full" />
|
<Box h="2" w="2" bg="critical-red/40" rounded="none" />
|
||||||
<Box h={{ base: 2.5, md: 3 }} w={{ base: 2.5, md: 3 }} bg="bg-warning-amber/60" rounded="full" />
|
<Box h="2" w="2" bg="warning-amber/40" rounded="none" />
|
||||||
<Box h={{ base: 2.5, md: 3 }} w={{ base: 2.5, md: 3 }} bg="bg-performance-green/60" rounded="full" />
|
<Box h="2" w="2" bg="success-green/40" rounded="none" />
|
||||||
<Box flexGrow={1} h={{ base: 2.5, md: 3 }} bg="bg-white/5" rounded="sm" ml={2} px={2} display="flex" alignItems="center">
|
<Box flexGrow={1} h="4" bg="graphite-black" rounded="none" ml={2} px={2} display="flex" alignItems="center" border borderColor="border-gray/30">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '8px' }}
|
style={{ fontSize: '8px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.3}
|
|
||||||
font="mono"
|
font="mono"
|
||||||
>
|
>
|
||||||
members.iracing.com
|
members.iracing.com
|
||||||
@@ -169,54 +170,53 @@ export function CompanionAutomationMockup() {
|
|||||||
as={motion.div}
|
as={motion.div}
|
||||||
h={{ base: 7, md: 8, lg: 9 }}
|
h={{ base: 7, md: 8, lg: 9 }}
|
||||||
w={{ base: 7, md: 8, lg: 9 }}
|
w={{ base: 7, md: 8, lg: 9 }}
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
bg={
|
bg={
|
||||||
step.status === 'Complete'
|
step.status === 'Complete'
|
||||||
? 'bg-performance-green/40'
|
? 'success-green/10'
|
||||||
: step.status === 'Running'
|
: step.status === 'Running'
|
||||||
? 'bg-primary-blue/40'
|
? 'primary-accent/10'
|
||||||
: 'bg-charcoal-outline'
|
: 'transparent'
|
||||||
}
|
}
|
||||||
borderColor={
|
borderColor={
|
||||||
step.status === 'Complete'
|
step.status === 'Complete'
|
||||||
? 'border-performance-green/60'
|
? 'success-green/40'
|
||||||
: step.status === 'Running'
|
: step.status === 'Running'
|
||||||
? 'border-primary-blue/60'
|
? 'primary-accent/40'
|
||||||
: 'border-white/20'
|
: 'border-gray/30'
|
||||||
}
|
}
|
||||||
animate={shouldReduceMotion ? {} : step.status === 'Running' ? {
|
animate={shouldReduceMotion ? {} : step.status === 'Running' ? {
|
||||||
scale: [1, 1.15, 1],
|
|
||||||
opacity: [0.4, 1, 0.4]
|
opacity: [0.4, 1, 0.4]
|
||||||
} : {}}
|
} : {}}
|
||||||
transition={{ duration: 1.5, repeat: Infinity }}
|
transition={{ duration: 1.5, repeat: Infinity }}
|
||||||
>
|
>
|
||||||
{step.status === 'Complete' && (
|
{step.status === 'Complete' && (
|
||||||
<Icon icon={Check} size={5} color="text-performance-green" />
|
<Box w="2" h="2" bg="success-green" />
|
||||||
)}
|
)}
|
||||||
{step.status === 'Running' && (
|
{step.status === 'Running' && (
|
||||||
<Box h={{ base: 3, md: 4 }} w={{ base: 3, md: 4 }} bg="bg-primary-blue" rounded="full" />
|
<Box h="2" w="2" bg="primary-accent" />
|
||||||
)}
|
)}
|
||||||
{step.status === 'Pending' && (
|
{step.status === 'Pending' && (
|
||||||
<Box h={{ base: 2, md: 2.5 }} w={{ base: 2, md: 2.5 }} bg="bg-white/30" rounded="full" />
|
<Box h="1" w="1" bg="gray-700" />
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Text size={{ base: 'sm', md: 'base', lg: 'lg' }} color="text-white" weight="medium" block>{step.label}</Text>
|
<Text size={{ base: 'sm', md: 'base' }} color="text-white" weight="bold" block className="uppercase tracking-widest">{step.label}</Text>
|
||||||
<Text size={{ base: 'xs', md: 'sm' }} color="text-white" opacity={0.5} block>{step.detail}</Text>
|
<Text size="xs" color="text-gray-500" block font="mono">{step.detail.toUpperCase()}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{step.status !== 'Pending' && (
|
{step.status !== 'Pending' && (
|
||||||
<Box h={{ base: 2.5, md: 3 }} fullWidth bg="bg-white/5" rounded="full" overflow="hidden" ml={{ base: 9, md: 10, lg: 11 }}>
|
<Box h="1" fullWidth bg="white/5" rounded="none" overflow="hidden" ml={{ base: 9, md: 10, lg: 11 }}>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
h="full"
|
h="full"
|
||||||
bg={step.status === 'Complete' ? 'bg-performance-green/60' : 'bg-primary-blue/60'}
|
bg={step.status === 'Complete' ? 'success-green/60' : 'primary-accent/60'}
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: step.status === 'Complete' ? '100%' : '65%' }}
|
animate={{ width: step.status === 'Complete' ? '100%' : '65%' }}
|
||||||
transition={{ duration: 2, ease: 'easeInOut' }}
|
transition={{ duration: 2, ease: 'easeInOut' }}
|
||||||
@@ -238,36 +238,33 @@ export function CompanionAutomationMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={2}
|
gap={2}
|
||||||
bg="bg-deep-graphite/90"
|
bg="graphite-black"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="backdrop-blur-sm"
|
|
||||||
px={3}
|
px={3}
|
||||||
py={2}
|
py={1.5}
|
||||||
rounded="full"
|
rounded="none"
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
borderColor="border-primary-blue/40"
|
borderColor="primary-accent/40"
|
||||||
animate={shouldReduceMotion ? {} : {
|
animate={shouldReduceMotion ? {} : {
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
'0 0 12px rgba(25,140,255,0.3)',
|
'0 0 8px rgba(25,140,255,0.2)',
|
||||||
'0 0 20px rgba(25,140,255,0.5)',
|
'0 0 15px rgba(25,140,255,0.4)',
|
||||||
'0 0 12px rgba(25,140,255,0.3)'
|
'0 0 8px rgba(25,140,255,0.2)'
|
||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
transition={{ duration: 2, repeat: Infinity }}
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
h={{ base: 2.5, md: 3 }}
|
h="1.5"
|
||||||
w={{ base: 2.5, md: 3 }}
|
w="1.5"
|
||||||
bg="bg-primary-blue"
|
bg="primary-accent"
|
||||||
rounded="full"
|
|
||||||
animate={shouldReduceMotion ? {} : {
|
animate={shouldReduceMotion ? {} : {
|
||||||
opacity: [1, 0.5, 1]
|
opacity: [1, 0.3, 1]
|
||||||
}}
|
}}
|
||||||
transition={{ duration: 1.5, repeat: Infinity }}
|
transition={{ duration: 1.5, repeat: Infinity }}
|
||||||
/>
|
/>
|
||||||
<Text size={{ base: 'xs', md: 'sm' }} color="text-primary-blue" weight="medium">Running</Text>
|
<Text size="xs" color="text-primary-accent" weight="bold" className="uppercase tracking-widest">Running</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -278,32 +275,36 @@ export function CompanionAutomationMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
flexDirection="col"
|
flexDirection="col"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 2, md: 3 }}
|
gap={3}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
bg="bg-primary-blue/20"
|
bg="primary-accent/10"
|
||||||
color="text-primary-blue"
|
color="text-primary-accent"
|
||||||
px={8}
|
px={8}
|
||||||
py={4}
|
py={3}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
borderColor="border-primary-blue/40"
|
borderColor="primary-accent/40"
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.03,
|
scale: 1.02,
|
||||||
boxShadow: '0 4px 24px rgba(25,140,255,0.3)',
|
borderColor: '#198CFF',
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
<Text size={{ base: 'base', md: 'lg' }} weight="semibold">Create Session</Text>
|
<Text size="sm" weight="bold" className="uppercase tracking-[0.2em]">Create Session</Text>
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
|
<Box position="absolute" bottom="-1px" right="-1px" w="2" h="2" borderBottom borderRight borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.4}
|
font="mono"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
One click. All fields automated.
|
One click. All fields automated.
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ export function DriverProfileMockup() {
|
|||||||
const [isMobile, setIsMobile] = useState(false);
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsMobile(window.innerWidth < 768);
|
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
||||||
|
checkMobile();
|
||||||
|
window.addEventListener('resize', checkMobile);
|
||||||
|
return () => window.removeEventListener('resize', checkMobile);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const stats = [
|
const stats = [
|
||||||
@@ -26,77 +29,58 @@ export function DriverProfileMockup() {
|
|||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={3} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={3} overflow="hidden">
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<Box>
|
<Box>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||||
<Box display="flex" alignItems="center" gap={3}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
<Box position="relative" h="12" w="12" rounded="full" border borderWidth="2px" borderColor="border-primary-blue/50" overflow="hidden" bg="bg-charcoal-outline">
|
<Box position="relative" h="12" w="12" rounded="none" border borderWidth="1px" borderColor="primary-accent/50" overflow="hidden" bg="panel-gray" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Box position="absolute" inset="0" display="flex" alignItems="center" justifyContent="center">
|
<Text size="2xl">🏎️</Text>
|
||||||
<Text size="2xl">🏎️</Text>
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text weight="bold" color="text-white" block>Driver Profile</Text>
|
<Text weight="bold" color="text-white" block className="uppercase tracking-widest">Driver Profile</Text>
|
||||||
<Text size="xs" color="text-white" opacity={0.5} block>Cross-league</Text>
|
<Text size="xs" color="text-gray-500" block font="mono">CROSS-LEAGUE</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="2xl" weight="bold" color="text-charcoal-outline">#33</Text>
|
<Text size="2xl" weight="bold" color="text-gray-800" font="mono">#33</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box position="relative" h="2" bg="bg-charcoal-outline" rounded="full" overflow="hidden" mb={1}>
|
<Box position="relative" h="1" bg="white/5" rounded="none" overflow="hidden" mb={1}>
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg="bg-gradient-to-r from-primary-blue to-neon-aqua"
|
bg="primary-accent"
|
||||||
rounded="full"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ width: '86%' }}
|
style={{ width: '86%' }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" justifyContent="end">
|
<Box display="flex" justifyContent="end">
|
||||||
<Text size="xs" color="text-gray-400">2150 GP Rating</Text>
|
<Text size="xs" color="text-primary-accent" font="mono" weight="bold">2150 GP RATING</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={2} block>Career Stats</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={2} block className="uppercase tracking-widest">Career Stats</Text>
|
||||||
<Box display="grid" gridCols={3} gap={2}>
|
<Box display="grid" gridCols={3} gap={2}>
|
||||||
{stats.slice(0, 3).map((stat) => (
|
{stats.slice(0, 3).map((stat) => (
|
||||||
<Box
|
<Box
|
||||||
key={stat.label}
|
key={stat.label}
|
||||||
bg="bg-iron-gray/50"
|
bg="panel-gray/40"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={2}
|
p={2}
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
>
|
>
|
||||||
<Text weight="bold" color="text-white" font="mono" block>
|
<Text weight="bold" color="text-white" font="mono" block>
|
||||||
{stat.value}{stat.suffix}
|
{stat.value}{stat.suffix}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" color="text-gray-400" mt={0.5} block>{stat.label}</Text>
|
<Text size="xs" color="text-gray-500" mt={0.5} block className="uppercase tracking-tighter">{stat.label}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={2} block>Recent Form</Text>
|
|
||||||
<Box h="16" bg="bg-iron-gray/30" border borderColor="border-charcoal-outline" rounded="lg" p={2} display="flex" alignItems="end" gap={1}>
|
|
||||||
{formData.slice(-6).map((value, i) => (
|
|
||||||
<Box
|
|
||||||
key={i}
|
|
||||||
flexGrow={1}
|
|
||||||
bg="bg-gradient-to-t from-performance-green to-primary-blue"
|
|
||||||
rounded="sm"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ height: `${value}%` }}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -120,7 +104,7 @@ export function DriverProfileMockup() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : -10 }}
|
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : -10 }}
|
||||||
@@ -129,50 +113,46 @@ export function DriverProfileMockup() {
|
|||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
||||||
<Box position="relative" h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} rounded="full" border borderWidth="2px" borderColor="border-primary-blue/50" overflow="hidden" bg="bg-charcoal-outline">
|
<Box position="relative" h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} rounded="none" border borderWidth="1px" borderColor="primary-accent/50" overflow="hidden" bg="panel-gray" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Box position="absolute" inset="0" display="flex" alignItems="center" justifyContent="center">
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏎️</Text>
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏎️</Text>
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" mb={{ base: 1, sm: 1.5, md: 2 }} block>Driver Profile</Text>
|
<Text size={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" mb={{ base: 1, sm: 1.5, md: 2 }} block className="uppercase tracking-widest">Driver Profile</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.5}
|
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Cross-league racing identity
|
Cross-league racing identity
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} weight="bold" color="text-charcoal-outline">#33</Text>
|
<Text size={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} weight="bold" color="text-gray-800" font="mono">#33</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box display="flex" flexWrap="wrap" alignItems="center" gap={{ base: 1, sm: 2, md: 3, lg: 4 }} mb={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box display="flex" flexWrap="wrap" alignItems="center" gap={{ base: 1, sm: 2, md: 3, lg: 4 }} mb={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Text size="xs" color="text-gray-400">GridPilot Rating:</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest">GP RATING:</Text>
|
||||||
<AnimatedRating shouldReduceMotion={shouldReduceMotion ?? false} value={2150} />
|
<AnimatedRating shouldReduceMotion={shouldReduceMotion ?? false} value={2150} />
|
||||||
<Text size="xs" color="text-gray-400">iRating:</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest ml-4">iRATING:</Text>
|
||||||
<AnimatedRating shouldReduceMotion={shouldReduceMotion ?? false} value={3200} />
|
<AnimatedRating shouldReduceMotion={shouldReduceMotion ?? false} value={3200} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box position="relative" h={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} bg="bg-charcoal-outline" rounded="full" overflow="hidden">
|
<Box position="relative" h="1" bg="white/5" rounded="none" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg="bg-gradient-to-r from-primary-blue to-neon-aqua"
|
bg="primary-accent"
|
||||||
rounded="full"
|
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: '86%' }}
|
animate={{ width: '86%' }}
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 0.4, duration: 0.8, ease: 'easeOut' }}
|
transition={{ delay: shouldReduceMotion ? 0 : 0.4, duration: 0.8, ease: 'easeOut' }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" justifyContent="end" mt={1}>
|
|
||||||
<Text size="xs" color="text-gray-400">86%</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@@ -182,8 +162,7 @@ export function DriverProfileMockup() {
|
|||||||
animate="visible"
|
animate="visible"
|
||||||
mb={{ base: 1.5, sm: 3, md: 4, lg: 6 }}
|
mb={{ base: 1.5, sm: 3, md: 4, lg: 6 }}
|
||||||
>
|
>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={1} block>Career Statistics</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-[0.2em]">Career Statistics</Text>
|
||||||
<Text size="xs" color="text-white" opacity={0.5} mb={{ base: 1, sm: 2, md: 3 }} block>Aggregated across all leagues</Text>
|
|
||||||
|
|
||||||
<Box display="grid" gridCols={{ base: 2, md: 5 }} gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="grid" gridCols={{ base: 2, md: 5 }} gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
{stats.map((stat, index) => (
|
{stats.map((stat, index) => (
|
||||||
@@ -191,10 +170,10 @@ export function DriverProfileMockup() {
|
|||||||
key={stat.label}
|
key={stat.label}
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
bg="bg-iron-gray/50"
|
bg="panel-gray/40"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1.5, sm: 2, md: 3 }}
|
p={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
>
|
>
|
||||||
@@ -204,7 +183,7 @@ export function DriverProfileMockup() {
|
|||||||
delay={index * 0.1}
|
delay={index * 0.1}
|
||||||
suffix={stat.suffix ?? ''}
|
suffix={stat.suffix ?? ''}
|
||||||
/>
|
/>
|
||||||
<Text size="xs" color="text-gray-400" mt={0.5} block>{stat.label}</Text>
|
<Text size="xs" color="text-gray-500" mt={0.5} block className="uppercase tracking-tighter font-bold">{stat.label}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -212,22 +191,20 @@ export function DriverProfileMockup() {
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 10 }}
|
initial={{ opacity: 0, y: 10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 0.6 }}
|
transition={{ delay: 0.6 }}
|
||||||
mb={{ base: 1.5, sm: 3, md: 4, lg: 6 }}
|
|
||||||
>
|
>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={1} block>Recent Form</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-[0.2em]">Recent Form</Text>
|
||||||
<Text size="xs" color="text-white" opacity={0.5} mb={{ base: 1, sm: 2, md: 3 }} block>Performance trend over last 10 races</Text>
|
<Box h={{ base: 12, sm: 16, md: 20 }} bg="panel-gray/20" border borderColor="border-gray/50" rounded="none" p={{ base: 1.5, sm: 2, md: 3 }} display="flex" alignItems="end" gap={1}>
|
||||||
|
|
||||||
<Box h={{ base: 12, sm: 16, md: 20 }} bg="bg-iron-gray/30" border borderColor="border-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 2, md: 3 }} display="flex" alignItems="end" gap={0.5}>
|
|
||||||
{formData.map((value, i) => (
|
{formData.map((value, i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
bg="bg-gradient-to-t from-performance-green to-primary-blue"
|
bg="primary-accent"
|
||||||
rounded="sm"
|
opacity={0.4 + (i * 0.06)}
|
||||||
|
rounded="none"
|
||||||
initial={{ height: 0 }}
|
initial={{ height: 0 }}
|
||||||
animate={{ height: `${value}%` }}
|
animate={{ height: `${value}%` }}
|
||||||
transition={{
|
transition={{
|
||||||
@@ -238,66 +215,6 @@ export function DriverProfileMockup() {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" justifyContent="between" mt={0.5}>
|
|
||||||
<Text size="xs" color="text-gray-500">Last 10 races</Text>
|
|
||||||
<Text size="xs" color="text-gray-500">Recent</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
as={motion.div}
|
|
||||||
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 10 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 0.8 }}
|
|
||||||
>
|
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={1} block>Teams</Text>
|
|
||||||
<Text size="xs" color="text-white" opacity={0.5} mb={{ base: 1, sm: 2, md: 3 }} block>Current and past team memberships</Text>
|
|
||||||
|
|
||||||
<Stack gap={{ base: 1, sm: 1.5, md: 2 }}>
|
|
||||||
{[
|
|
||||||
{ team: 'Red Bull Racing', status: 'Current', color: 'primary-blue' },
|
|
||||||
{ team: 'Mercedes AMG', status: '2023', color: 'charcoal-outline' }
|
|
||||||
].map((team, i) => (
|
|
||||||
<Box
|
|
||||||
key={team.team}
|
|
||||||
as={motion.div}
|
|
||||||
initial={{ opacity: 0, x: shouldReduceMotion ? 0 : -10 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 0.9 + i * 0.1 }}
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="between"
|
|
||||||
bg="bg-iron-gray/30"
|
|
||||||
border
|
|
||||||
borderColor="border-charcoal-outline"
|
|
||||||
rounded="lg"
|
|
||||||
p={{ base: 1, sm: 1.5, md: 2 }}
|
|
||||||
>
|
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 2, sm: 3 }}>
|
|
||||||
<Box h={{ base: 5, sm: 6, md: 8 }} w={{ base: 5, sm: 6, md: 8 }} rounded="sm" border borderColor="border-primary-blue/30" bg="bg-charcoal-outline" display="flex" alignItems="center" justifyContent="center">
|
|
||||||
<Text size={{ base: 'sm', sm: 'base', md: 'lg' }}>🏁</Text>
|
|
||||||
</Box>
|
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 3 }} w={{ base: 16, sm: 20, md: 32 }} bg="bg-white/10" rounded="sm" />
|
|
||||||
</Box>
|
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} rounded="sm" bg={team.status === 'Current' ? 'bg-primary-blue/20' : 'bg-charcoal-outline'}>
|
|
||||||
<Text size="xs" color={team.status === 'Current' ? 'text-primary-blue' : 'text-gray-400'}>
|
|
||||||
{team.status}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
as={motion.div}
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 1.2 }}
|
|
||||||
mt={{ base: 2, sm: 3, md: 4 }}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
<Text size="xs" color="text-gray-400">Active in 3 leagues</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -318,7 +235,7 @@ function AnimatedRating({ shouldReduceMotion, value }: { shouldReduceMotion: boo
|
|||||||
}, [shouldReduceMotion, count, value]);
|
}, [shouldReduceMotion, count, value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box as={motion.span} weight="bold" color="text-primary-blue" font="mono" size={{ base: 'sm', sm: 'base', md: 'lg' }}>
|
<Box as={motion.span} weight="bold" color="text-primary-accent" font="mono" size={{ base: 'sm', sm: 'base' }}>
|
||||||
{shouldReduceMotion ? value : <Box as={motion.span}>{rounded}</Box>}
|
{shouldReduceMotion ? value : <Box as={motion.span}>{rounded}</Box>}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@@ -348,7 +265,7 @@ function AnimatedCounter({
|
|||||||
}, [shouldReduceMotion, count, value, delay]);
|
}, [shouldReduceMotion, count, value, delay]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box weight="bold" color="text-white" font="mono" size={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }}>
|
<Box weight="bold" color="text-white" font="mono" size={{ base: 'sm', sm: 'base', md: 'lg' }}>
|
||||||
{shouldReduceMotion ? value : <Box as={motion.span}>{rounded}</Box>}{suffix}
|
{shouldReduceMotion ? value : <Box as={motion.span}>{rounded}</Box>}{suffix}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -43,86 +43,58 @@ export function LeagueDiscoveryMockup() {
|
|||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={3} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={3} overflow="hidden">
|
||||||
<Box mb={3}>
|
<Box mb={3}>
|
||||||
<Box h="4" w="40" bg="bg-white/10" rounded="sm" mb={3} />
|
<Box h="1.5" w="40" bg="white/10" rounded="none" mb={3} />
|
||||||
<Box display="flex" gap={2} flexWrap="wrap">
|
<Box display="flex" gap={2} flexWrap="wrap">
|
||||||
{['Game', 'Region'].map((filter) => (
|
{['Game', 'Region'].map((filter) => (
|
||||||
<Box
|
<Box
|
||||||
key={filter}
|
key={filter}
|
||||||
h="6"
|
h="5"
|
||||||
px={3}
|
px={3}
|
||||||
bg="bg-charcoal-outline"
|
bg="panel-gray"
|
||||||
border
|
border
|
||||||
borderColor="border-primary-blue/30"
|
borderColor="primary-accent/30"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Box h="1.5" w="10" bg="bg-white/10" rounded="sm" />
|
<Box h="1" w="8" bg="white/10" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={3}>
|
<Stack gap={2}>
|
||||||
{leagues.map((league) => (
|
{leagues.map((league) => (
|
||||||
<Box
|
<Box
|
||||||
key={league.name}
|
key={league.name}
|
||||||
bg="bg-iron-gray/80"
|
bg="panel-gray/40"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={3}
|
p={3}
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="start" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="start" justifyContent="between" mb={2}>
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
<Box h="10" w="10" rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/30" bg="bg-charcoal-outline" display="flex" alignItems="center" justifyContent="center">
|
<Box h="10" w="10" rounded="none" border borderWidth="1px" borderColor="primary-accent/30" bg="graphite-black" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size="xl">{league.icon}</Text>
|
<Text size="xl">{league.icon}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box h="3" w="32" bg="bg-white/10" rounded="sm" mb={1} />
|
<Box h="1.5" w="32" bg="white/10" rounded="none" mb={1.5} />
|
||||||
<Box display="flex" gap={1}>
|
<Box display="flex" gap={1}>
|
||||||
<Box as="span" px={1.5} py={0.5} bg="bg-primary-blue/20" rounded="sm">
|
<Box as="span" px={1.5} py={0.5} bg="primary-accent/10" border borderColor="primary-accent/20">
|
||||||
<Text size="xs" color="text-primary-blue">{league.carClass}</Text>
|
<Text size="xs" color="text-primary-accent" weight="bold" font="mono">{league.carClass}</Text>
|
||||||
</Box>
|
|
||||||
<Box as="span" px={1.5} py={0.5} bg="bg-neon-aqua/20" rounded="sm">
|
|
||||||
<Text size="xs" color="text-neon-aqua">{league.region}</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
|
||||||
<Box display="flex" alignItems="center" gap={2}>
|
|
||||||
<Text size="xs" color="text-gray-400">{league.drivers} drivers</Text>
|
|
||||||
<Text size="xs" color="text-gray-400">•</Text>
|
|
||||||
<Text size="xs" color="text-gray-400">{league.schedule}</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box display="flex" alignItems="center" justifyContent="between">
|
<Box display="flex" alignItems="center" justifyContent="between">
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
<Text size="xs" color="text-gray-500" font="mono">{league.drivers} DRIVERS • {league.schedule.toUpperCase()}</Text>
|
||||||
{[...Array(5)].map((_, i) => (
|
<Box px={2} py={0.5} bg="primary-accent" rounded="none">
|
||||||
<Box
|
<Text size="xs" color="text-white" weight="bold">JOIN</Text>
|
||||||
as="svg"
|
|
||||||
key={i}
|
|
||||||
w="3"
|
|
||||||
h="3"
|
|
||||||
color={i < Math.floor(league.rating) ? 'text-warning-amber' : 'text-charcoal-outline'}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="fill-current"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
>
|
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box as="button" px={2} py={1} bg="bg-primary-blue" rounded="sm">
|
|
||||||
<Text size="xs" color="text-white">Join</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -141,25 +113,25 @@ export function LeagueDiscoveryMockup() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cardVariants = {
|
const cardVariants = {
|
||||||
hidden: { opacity: 0, y: shouldReduceMotion ? 0 : 20 },
|
hidden: { opacity: 0, x: shouldReduceMotion ? 0 : -20 },
|
||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
x: 0,
|
||||||
transition: { type: 'spring' as const, stiffness: 200, damping: 20 }
|
transition: { duration: 0.4 }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : -10 }}
|
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : -10 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
mb={{ base: 1.5, sm: 3, md: 4, lg: 6 }}
|
mb={{ base: 1.5, sm: 3, md: 4, lg: 6 }}
|
||||||
>
|
>
|
||||||
<Box h={{ base: 3, sm: 4, md: 5, lg: 6 }} w={{ base: 32, sm: 40, md: 52 }} bg="bg-white/10" rounded="sm" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} />
|
<Box h="2" w="48" bg="white/10" rounded="none" mb={4} />
|
||||||
|
|
||||||
<Box display="flex" gap={{ base: 1, sm: 1.5, md: 2 }} flexWrap="wrap">
|
<Box display="flex" gap={2} flexWrap="wrap">
|
||||||
{['Game', 'Region', 'Skill'].map((filter, i) => (
|
{['Game', 'Region', 'Skill'].map((filter, i) => (
|
||||||
<Box
|
<Box
|
||||||
key={filter}
|
key={filter}
|
||||||
@@ -167,16 +139,16 @@ export function LeagueDiscoveryMockup() {
|
|||||||
initial={{ opacity: 0, scale: shouldReduceMotion ? 1 : 0.9 }}
|
initial={{ opacity: 0, scale: shouldReduceMotion ? 1 : 0.9 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : i * 0.1 }}
|
transition={{ delay: shouldReduceMotion ? 0 : i * 0.1 }}
|
||||||
h={{ base: 5, sm: 6, md: 7, lg: 8 }}
|
h="6"
|
||||||
px={{ base: 2, sm: 3, md: 4 }}
|
px={4}
|
||||||
bg="bg-charcoal-outline"
|
bg="panel-gray"
|
||||||
border
|
border
|
||||||
borderColor="border-primary-blue/30"
|
borderColor="primary-accent/30"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
<Box h={{ base: 1, sm: 1.5, md: 2 }} w={{ base: 8, sm: 10, md: 12 }} bg="bg-white/10" rounded="sm" />
|
<Box h="1" w="10" bg="white/10" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -188,7 +160,7 @@ export function LeagueDiscoveryMockup() {
|
|||||||
initial="hidden"
|
initial="hidden"
|
||||||
animate="visible"
|
animate="visible"
|
||||||
>
|
>
|
||||||
<Stack gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
<Stack gap={2}>
|
||||||
{leagues.map((league, index) => (
|
{leagues.map((league, index) => (
|
||||||
<Box
|
<Box
|
||||||
key={league.name}
|
key={league.name}
|
||||||
@@ -197,157 +169,90 @@ export function LeagueDiscoveryMockup() {
|
|||||||
onHoverStart={() => !shouldReduceMotion && setHoveredIndex(index)}
|
onHoverStart={() => !shouldReduceMotion && setHoveredIndex(index)}
|
||||||
onHoverEnd={() => setHoveredIndex(null)}
|
onHoverEnd={() => setHoveredIndex(null)}
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.02,
|
x: 4,
|
||||||
y: -4,
|
borderColor: '#198CFF30',
|
||||||
transition: { duration: 0.2 }
|
transition: { duration: 0.2 }
|
||||||
}}
|
}}
|
||||||
bg="bg-iron-gray/80"
|
bg="panel-gray/20"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
p={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="backdrop-blur-sm"
|
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="start" justifyContent="between" mb={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="start" justifyContent="between" mb={3}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
<Box h={{ base: 7, sm: 8, md: 10, lg: 12 }} w={{ base: 7, sm: 8, md: 10, lg: 12 }} rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/30" bg="bg-charcoal-outline" display="flex" alignItems="center" justifyContent="center" shadow="0_0_12px_rgba(25,140,255,0.2)">
|
<Box h={{ base: 7, sm: 8, md: 10, lg: 12 }} w={{ base: 7, sm: 8, md: 10, lg: 12 }} rounded="none" border borderWidth="1px" borderColor="primary-accent/30" bg="graphite-black" display="flex" alignItems="center" justifyContent="center" className="relative">
|
||||||
<Text size={{ base: 'base', sm: 'lg', md: 'xl', lg: '2xl' }}>{league.icon}</Text>
|
<Text size={{ base: 'base', sm: 'lg', md: 'xl', lg: '2xl' }}>{league.icon}</Text>
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Box h={{ base: 2.5, sm: 3, md: 4 }} w={{ base: 28, sm: 32, md: 40 }} bg="bg-white/10" rounded="sm" mb={{ base: 1, sm: 1.5, md: 2 }} />
|
<Box h="2" w="40" bg="white/10" rounded="none" mb={2} />
|
||||||
<Box display="flex" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box display="flex" gap={2}>
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="bg-primary-blue/20" rounded="sm">
|
<Box as="span" px={2} py={0.5} bg="primary-accent/10" border borderColor="primary-accent/20">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-primary-blue"
|
color="text-primary-accent"
|
||||||
|
weight="bold"
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
{league.carClass}
|
{league.carClass}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="bg-neon-aqua/20" rounded="sm">
|
<Box as="span" px={2} py={0.5} bg="telemetry-aqua/10" border borderColor="telemetry-aqua/20">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-neon-aqua"
|
color="text-telemetry-aqua"
|
||||||
|
weight="bold"
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
{league.region}
|
{league.region}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box as="span" px={{ base: 1, sm: 1.5, md: 2 }} py={0.5} bg="bg-charcoal-outline" rounded="sm">
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-gray-400"
|
|
||||||
>
|
|
||||||
{league.skill}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={{ base: 1.5, sm: 2, md: 3 }}>
|
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<Box as="svg" w={{ base: 2.5, sm: 3, md: 4 }} h={{ base: 2.5, sm: 3, md: 4 }} color="text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-gray-400"
|
|
||||||
>
|
|
||||||
{league.drivers} drivers
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
|
||||||
<Box as="svg" w={{ base: 2.5, sm: 3, md: 4 }} h={{ base: 2.5, sm: 3, md: 4 }} color="text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
||||||
</Box>
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-gray-400"
|
|
||||||
>
|
|
||||||
{league.schedule}
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box display="flex" alignItems="center" justifyContent="between">
|
<Box display="flex" alignItems="center" justifyContent="between">
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
<Box display="flex" alignItems="center" gap={4}>
|
||||||
{[...Array(5)].map((_, i) => (
|
|
||||||
<Box
|
|
||||||
as={motion.svg}
|
|
||||||
key={i}
|
|
||||||
w={{ base: 2.5, sm: 3, md: 4 }}
|
|
||||||
h={{ base: 2.5, sm: 3, md: 4 }}
|
|
||||||
color={i < Math.floor(league.rating) ? 'text-warning-amber' : 'text-charcoal-outline'}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="fill-current"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
animate={hoveredIndex === index && !shouldReduceMotion ? {
|
|
||||||
scale: [1, 1.2, 1],
|
|
||||||
transition: { delay: i * 0.05, duration: 0.3 }
|
|
||||||
} : {}}
|
|
||||||
>
|
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
ml={1}
|
font="mono"
|
||||||
|
weight="bold"
|
||||||
>
|
>
|
||||||
{league.rating}
|
{league.drivers} DRIVERS
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
|
style={{ fontSize: '10px' }}
|
||||||
|
color="text-gray-500"
|
||||||
|
font="mono"
|
||||||
|
weight="bold"
|
||||||
|
>
|
||||||
|
{league.schedule.toUpperCase()}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box display="flex" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box display="flex" gap={2}>
|
||||||
<Box
|
<Box
|
||||||
as={motion.button}
|
as={motion.button}
|
||||||
whileHover={shouldReduceMotion ? {} : { scale: 1.05 }}
|
whileHover={shouldReduceMotion ? {} : { scale: 1.05 }}
|
||||||
whileTap={shouldReduceMotion ? {} : { scale: 0.95 }}
|
px={3}
|
||||||
px={{ base: 1.5, sm: 2, md: 3 }}
|
py={1}
|
||||||
py={0.5}
|
bg="primary-accent"
|
||||||
bg="bg-primary-blue"
|
|
||||||
color="text-white"
|
color="text-white"
|
||||||
rounded="sm"
|
rounded="none"
|
||||||
transition
|
|
||||||
hoverBg="bg-primary-blue/80"
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
|
weight="bold"
|
||||||
>
|
>
|
||||||
Join
|
JOIN
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
as={motion.button}
|
|
||||||
whileHover={shouldReduceMotion ? {} : { scale: 1.05 }}
|
|
||||||
whileTap={shouldReduceMotion ? {} : { scale: 0.95 }}
|
|
||||||
px={{ base: 1.5, sm: 2, md: 3 }}
|
|
||||||
py={0.5}
|
|
||||||
bg="bg-charcoal-outline"
|
|
||||||
color="text-gray-300"
|
|
||||||
rounded="sm"
|
|
||||||
transition
|
|
||||||
hoverBg="bg-charcoal-outline/80"
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
>
|
|
||||||
View
|
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -17,47 +17,45 @@ export function LeagueHomeMockup() {
|
|||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={3} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={3} overflow="hidden">
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<Box display="flex" alignItems="center" gap={3} mb={2}>
|
<Box display="flex" alignItems="center" gap={3} mb={2}>
|
||||||
<Box h="12" w="12" rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/50" bg="bg-charcoal-outline" display="flex" alignItems="center" justifyContent="center" shadow="0_0_20px_rgba(25,140,255,0.3)">
|
<Box h="12" w="12" rounded="none" border borderWidth="1px" borderColor="primary-accent/50" bg="panel-gray" display="flex" alignItems="center" justifyContent="center" className="relative">
|
||||||
<Text size="2xl">🏆</Text>
|
<Text size="2xl">🏆</Text>
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="base" weight="bold" color="text-white" block>Super GT</Text>
|
<Text size="base" weight="bold" color="text-white" block className="uppercase tracking-widest">Super GT</Text>
|
||||||
<Text size="xs" color="text-gray-400" block>Round 8/12</Text>
|
<Text size="xs" color="text-gray-500" block font="mono">ROUND 8/12</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={3} block>Next Race</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-widest">Next Race</Text>
|
||||||
<Box position="relative" display="flex" alignItems="center" gap={3} bg="bg-iron-gray" rounded="lg" p={3} border borderColor="border-charcoal-outline">
|
<Box position="relative" display="flex" alignItems="center" gap={3} bg="panel-gray/40" rounded="none" p={3} border borderColor="border-gray/50">
|
||||||
<Box h="8" w="8" bg="bg-charcoal-outline" rounded border borderColor="border-primary-blue/20" display="flex" alignItems="center" justifyContent="center">
|
<Box h="8" w="8" bg="graphite-black" rounded="none" border borderColor="primary-accent/20" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size="base">🏁</Text>
|
<Text size="base">🏁</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box h="2.5" w="28" bg="bg-white/10" rounded="sm" mb={2} />
|
<Box h="2" w="28" bg="white/10" rounded="none" mb={2} />
|
||||||
<Box h="2" w="20" bg="bg-white/5" rounded="sm" />
|
<Box h="1.5" w="20" bg="white/5" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box w="2" h="2" bg="bg-primary-blue" rounded="full"
|
<Box w="1.5" h="1.5" bg="primary-accent" rounded="full" className="animate-pulse" />
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="shadow-glow"
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={3} block>Latest Result</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-widest">Latest Result</Text>
|
||||||
<Box bg="bg-iron-gray" rounded="lg" p={3} border borderColor="border-charcoal-outline">
|
<Box bg="panel-gray/40" rounded="none" p={3} border borderColor="border-gray/50">
|
||||||
<Box display="flex" alignItems="center" gap={3} py={2} borderBottom borderColor="border-charcoal-outline">
|
<Box display="flex" alignItems="center" gap={3} py={2} borderBottom borderColor="border-gray/30">
|
||||||
<Box h="2.5" w="6" bg="bg-white/10" rounded="sm" />
|
<Box h="1.5" w="6" bg="white/10" rounded="none" />
|
||||||
<Box h="2.5" flexGrow={1} bg="bg-white/10" rounded="sm" />
|
<Box h="1.5" flexGrow={1} bg="white/10" rounded="none" />
|
||||||
<Box h="2.5" w="10" bg="bg-white/10" rounded="sm" />
|
<Box h="1.5" w="10" bg="white/10" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" alignItems="center" gap={3} py={2}>
|
<Box display="flex" alignItems="center" gap={3} py={2}>
|
||||||
<Box h="2" w="6" bg="bg-white/5" rounded="sm" />
|
<Box h="1.5" w="6" bg="white/5" rounded="none" />
|
||||||
<Box h="2" flexGrow={1} bg="bg-white/5" rounded="sm" />
|
<Box h="1.5" flexGrow={1} bg="white/5" rounded="none" />
|
||||||
<Box h="2" w="10" bg="bg-performance-green/20" rounded="sm" />
|
<Box h="1.5" w="10" bg="success-green/20" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -80,7 +78,7 @@ export function LeagueHomeMockup() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
variants={containerVariants}
|
variants={containerVariants}
|
||||||
@@ -90,44 +88,27 @@ export function LeagueHomeMockup() {
|
|||||||
<Stack gap={{ base: 1.5, sm: 3, md: 4, lg: 6 }}>
|
<Stack gap={{ base: 1.5, sm: 3, md: 4, lg: 6 }}>
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} mb={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} mb={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Box h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/50" bg="bg-charcoal-outline" display="flex" alignItems="center" justifyContent="center" shadow="0_0_20px_rgba(25,140,255,0.3)">
|
<Box h={{ base: 8, sm: 10, md: 12, lg: 16 }} w={{ base: 8, sm: 10, md: 12, lg: 16 }} rounded="none" border borderWidth="1px" borderColor="primary-accent/50" bg="panel-gray" display="flex" alignItems="center" justifyContent="center" className="relative">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏆</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl', lg: '3xl' }}>🏆</Text>
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Heading level={2} fontSize={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" mb={0.5}>Super GT Championship</Heading>
|
<Heading level={2} fontSize={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" mb={0.5} className="uppercase tracking-widest">Super GT Championship</Heading>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '9px' }}
|
style={{ fontSize: '9px' }}
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
|
font="mono"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Season 3 • Round 8/12
|
SEASON 3 • ROUND 8/12
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '8px' }}
|
|
||||||
color="text-white"
|
|
||||||
opacity={0.5}
|
|
||||||
mt={{ base: 1, sm: 1.5, md: 2 }}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
Your league's dedicated home page
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} block>Upcoming Races</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-500" mb={{ base: 1.5, sm: 2, md: 3 }} block className="uppercase tracking-[0.2em]">Upcoming Races</Text>
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '8px' }}
|
|
||||||
color="text-white"
|
|
||||||
opacity={0.5}
|
|
||||||
mb={{ base: 1.5, sm: 2, md: 3 }}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
Calendar automatically synced from iRacing
|
|
||||||
</Text>
|
|
||||||
<Stack gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Stack gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
@@ -137,25 +118,23 @@ export function LeagueHomeMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/40"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
p={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
shadow="inset_0_1px_2px_rgba(0,0,0,0.2)"
|
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
y: -2,
|
x: 4,
|
||||||
boxShadow: '0 4px 24px rgba(0,0,0,0.4), 0 0 20px rgba(25,140,255,0.3)',
|
borderColor: '#198CFF50',
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
transition={{ type: 'spring', stiffness: 200, damping: 20 }}
|
|
||||||
>
|
>
|
||||||
<Box h={{ base: 6, sm: 7, md: 8, lg: 10 }} w={{ base: 6, sm: 7, md: 8, lg: 10 }} bg="bg-charcoal-outline" rounded border borderColor="border-primary-blue/20" display="flex" alignItems="center" justifyContent="center">
|
<Box h={{ base: 6, sm: 7, md: 8, lg: 10 }} w={{ base: 6, sm: 7, md: 8, lg: 10 }} bg="graphite-black" rounded="none" border borderColor="primary-accent/20" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }}>🏁</Text>
|
<Text size={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }}>🏁</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} w={{ base: 20, sm: 24, md: 28, lg: 32 }} bg="bg-white/10" rounded="sm" mb={{ base: 1, sm: 1.5, md: 2 }} />
|
<Box h="1.5" w={i === 1 ? "32" : "24"} bg="white/10" rounded="none" mb={{ base: 1, sm: 1.5, md: 2 }} />
|
||||||
<Box h={{ base: 1, sm: 1.5, md: 2, lg: 2.5 }} w={{ base: 12, sm: 16, md: 20, lg: 24 }} bg="bg-white/5" rounded="sm" font="mono" />
|
<Box h="1" w="16" bg="white/5" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
{i === 1 && (
|
{i === 1 && (
|
||||||
<Box
|
<Box
|
||||||
@@ -163,19 +142,11 @@ export function LeagueHomeMockup() {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
right="4"
|
right="4"
|
||||||
animate={shouldReduceMotion ? {} : {
|
animate={shouldReduceMotion ? {} : {
|
||||||
scale: [1, 1.2, 1],
|
opacity: [1, 0.4, 1]
|
||||||
boxShadow: [
|
|
||||||
'0 0 20px rgba(25,140,255,0.3)',
|
|
||||||
'0 0 32px rgba(67,201,230,0.4)',
|
|
||||||
'0 0 20px rgba(25,140,255,0.3)'
|
|
||||||
]
|
|
||||||
}}
|
}}
|
||||||
transition={{ duration: 2, repeat: Infinity }}
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
>
|
>
|
||||||
<Box w={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} h={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} bg="bg-primary-blue" rounded="full"
|
<Box w="1.5" h="1.5" bg="primary-accent" rounded="full" />
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="shadow-glow"
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
@@ -184,28 +155,18 @@ export function LeagueHomeMockup() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} block>Recent Results</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-500" mb={{ base: 1.5, sm: 2, md: 3 }} block className="uppercase tracking-[0.2em]">Recent Results</Text>
|
||||||
<Text
|
<Box bg="panel-gray/20" rounded="none" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} border borderColor="border-gray/50">
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }} mb={{ base: 1.5, sm: 2, md: 3 }} pb={{ base: 1.5, sm: 2, md: 3 }} borderBottom borderColor="border-gray/30">
|
||||||
style={{ fontSize: '8px' }}
|
<Box h="1" w="6" bg="white/10" rounded="none" />
|
||||||
color="text-white"
|
<Box h="1" flexGrow={1} bg="white/10" rounded="none" />
|
||||||
opacity={0.5}
|
<Box h="1" w="10" bg="white/10" rounded="none" />
|
||||||
mb={{ base: 1.5, sm: 2, md: 3 }}
|
|
||||||
block
|
|
||||||
>
|
|
||||||
Results appear instantly after each race
|
|
||||||
</Text>
|
|
||||||
<Box bg="bg-iron-gray" rounded="lg" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} border borderColor="border-charcoal-outline" shadow="0_4px_24px_rgba(0,0,0,0.4)">
|
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }} mb={{ base: 1.5, sm: 2, md: 3 }} pb={{ base: 1.5, sm: 2, md: 3 }} borderBottom borderColor="border-charcoal-outline">
|
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w={{ base: 5, sm: 6, md: 8 }} bg="bg-white/10" rounded="sm" font="mono" />
|
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} flexGrow={1} bg="bg-white/10" rounded="sm" />
|
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w={{ base: 8, sm: 10, md: 12 }} bg="bg-white/10" rounded="sm" font="mono" />
|
|
||||||
</Box>
|
</Box>
|
||||||
{[1, 2].map((i) => (
|
{[1, 2].map((i) => (
|
||||||
<Box key={i} display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }} py={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box key={i} display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }} py={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w={{ base: 5, sm: 6, md: 8 }} bg="bg-white/5" rounded="sm" font="mono" />
|
<Box h="1" w="6" bg="white/5" rounded="none" />
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} flexGrow={1} bg="bg-white/5" rounded="sm" />
|
<Box h="1" flexGrow={1} bg="white/5" rounded="none" />
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w={{ base: 8, sm: 10, md: 12 }} bg="bg-performance-green/20" rounded="sm" textAlign="center" font="mono" color="text-performance-green" />
|
<Box h="1" w="10" bg={i === 1 ? "success-green/20" : "white/5"} rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full scale-60 sm:scale-70 md:scale-85 lg:scale-95 max-w-[85vw] mx-auto my-4 sm:my-0" style={{ perspective: '1200px' }}>
|
<div className="relative w-full h-full scale-60 sm:scale-70 md:scale-85 lg:scale-95 max-w-[85vw] mx-auto my-4 sm:my-0" style={{ perspective: '1200px' }}>
|
||||||
<div
|
<div
|
||||||
className="absolute rounded-lg bg-iron-gray/80 border border-charcoal-outline"
|
className="absolute rounded-none bg-panel-gray/80 border border-border-gray/50"
|
||||||
style={{
|
style={{
|
||||||
rotate: `${rotation1}deg`,
|
rotate: `${rotation1}deg`,
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
@@ -44,7 +44,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="absolute rounded-lg bg-iron-gray/90 border border-charcoal-outline"
|
className="absolute rounded-none bg-panel-gray/90 border border-border-gray/50"
|
||||||
style={{
|
style={{
|
||||||
rotate: `${rotation2}deg`,
|
rotate: `${rotation2}deg`,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
@@ -58,7 +58,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="relative z-10 w-full h-full rounded-lg overflow-hidden"
|
className="relative z-10 w-full h-full rounded-none overflow-hidden border border-border-gray/30"
|
||||||
style={{
|
style={{
|
||||||
boxShadow: '0 20px 60px rgba(0,0,0,0.45)',
|
boxShadow: '0 20px 60px rgba(0,0,0,0.45)',
|
||||||
}}
|
}}
|
||||||
@@ -73,7 +73,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full scale-60 sm:scale-70 md:scale-85 lg:scale-95 max-w-[85vw] mx-auto my-4 sm:my-0" style={{ perspective: '1200px' }}>
|
<div className="relative w-full h-full scale-60 sm:scale-70 md:scale-85 lg:scale-95 max-w-[85vw] mx-auto my-4 sm:my-0" style={{ perspective: '1200px' }}>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute rounded-lg bg-iron-gray/80 border border-charcoal-outline"
|
className="absolute rounded-none bg-panel-gray/80 border border-border-gray/50"
|
||||||
style={{
|
style={{
|
||||||
rotate: `${rotation1}deg`,
|
rotate: `${rotation1}deg`,
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
@@ -89,7 +89,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute rounded-lg bg-iron-gray/90 border border-charcoal-outline"
|
className="absolute rounded-none bg-panel-gray/90 border border-border-gray/50"
|
||||||
style={{
|
style={{
|
||||||
rotate: `${rotation2}deg`,
|
rotate: `${rotation2}deg`,
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
@@ -105,7 +105,7 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="relative z-10 w-full h-full rounded-lg overflow-hidden"
|
className="relative z-10 w-full h-full rounded-none overflow-hidden border border-border-gray/30"
|
||||||
style={{
|
style={{
|
||||||
boxShadow: '0 20px 60px rgba(0,0,0,0.45)',
|
boxShadow: '0 20px 60px rgba(0,0,0,0.45)',
|
||||||
}}
|
}}
|
||||||
@@ -129,12 +129,12 @@ export function MockupStack({ children, index = 0 }: MockupStackProps) {
|
|||||||
transition={{ duration: 0.4, delay: 0.2 }}
|
transition={{ duration: 0.4, delay: 0.2 }}
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute inset-0 pointer-events-none rounded-lg"
|
className="absolute inset-0 pointer-events-none rounded-none"
|
||||||
whileHover={
|
whileHover={
|
||||||
shouldReduceMotion
|
shouldReduceMotion
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
boxShadow: '0 0 40px rgba(25, 140, 255, 0.4)',
|
boxShadow: '0 0 40px rgba(25, 140, 255, 0.2)',
|
||||||
transition: { duration: 0.2 },
|
transition: { duration: 0.2 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,35 +21,32 @@ export function ProtestWorkflowMockup() {
|
|||||||
{
|
{
|
||||||
name: 'Submit',
|
name: 'Submit',
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
color: 'charcoal-outline',
|
|
||||||
icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z'
|
icon: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Review',
|
name: 'Review',
|
||||||
status: 'active',
|
status: 'active',
|
||||||
color: 'warning-amber',
|
|
||||||
icon: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4'
|
icon: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Resolve',
|
name: 'Resolve',
|
||||||
status: 'resolved',
|
status: 'resolved',
|
||||||
color: 'performance-green',
|
|
||||||
icon: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'
|
icon: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z'
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusStyles = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'pending': return 'bg-charcoal-outline border-charcoal-outline text-gray-500';
|
case 'pending': return 'bg-panel-gray border-gray-700 text-gray-600';
|
||||||
case 'active': return 'bg-warning-amber/20 border-warning-amber text-warning-amber';
|
case 'active': return 'bg-warning-amber/10 border-warning-amber text-warning-amber';
|
||||||
case 'resolved': return 'bg-performance-green/20 border-performance-green text-performance-green';
|
case 'resolved': return 'bg-success-green/10 border-success-green text-success-green';
|
||||||
default: return 'bg-charcoal-outline border-charcoal-outline text-gray-500';
|
default: return 'bg-panel-gray border-gray-700 text-gray-600';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" overflow="hidden" p={3} display="flex" flexDirection="col" justifyContent="center" gap={4}>
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" overflow="hidden" p={3} display="flex" flexDirection="col" justifyContent="center" gap={4}>
|
||||||
<Box display="flex" alignItems="center" justifyContent="center" gap={3}>
|
<Box display="flex" alignItems="center" justifyContent="center" gap={3}>
|
||||||
{steps.map((step, i) => (
|
{steps.map((step, i) => (
|
||||||
<Box key={step.name} display="flex" alignItems="center">
|
<Box key={step.name} display="flex" alignItems="center">
|
||||||
@@ -57,41 +54,36 @@ export function ProtestWorkflowMockup() {
|
|||||||
<Box
|
<Box
|
||||||
w="10"
|
w="10"
|
||||||
h="10"
|
h="10"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
mb={1}
|
mb={1}
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
className={getStatusStyles(step.status)}
|
||||||
className={getStatusColor(step.status)}
|
|
||||||
>
|
>
|
||||||
<Box as="svg" w="5" h="5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<Box as="svg" w="5" h="5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={step.icon} />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={step.icon} />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="xs" color="text-white" opacity={0.7} textAlign="center">{step.name}</Text>
|
<Text size="xs" color="text-white" weight="bold" textAlign="center" className="uppercase tracking-widest">{step.name}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{i < steps.length - 1 && (
|
{i < steps.length - 1 && (
|
||||||
<Box as="svg" w="4" h="4" mx={1} viewBox="0 0 24 24" fill="none">
|
<Box as="svg" w="4" h="4" mx={1} viewBox="0 0 24 24" fill="none">
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
<path d="M5 12h14m-7-7l7 7-7 7" stroke="#198CFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
<path d="M5 12h14m-7-7l7 7-7 7" stroke="#43C9E6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box position="relative" h="1" bg="bg-charcoal-outline" rounded="full" overflow="hidden">
|
<Box position="relative" h="1" bg="white/5" rounded="none" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg="bg-gradient-to-r from-neon-aqua to-primary-blue"
|
bg="primary-accent"
|
||||||
rounded="full"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ width: `${((activeStep + 1) / steps.length) * 100}%` }}
|
style={{ width: `${((activeStep + 1) / steps.length) * 100}%` }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -100,21 +92,19 @@ export function ProtestWorkflowMockup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const stepVariants = {
|
const stepVariants = {
|
||||||
hidden: { opacity: 0, scale: shouldReduceMotion ? 1 : 0.8 },
|
hidden: { opacity: 0, y: shouldReduceMotion ? 0 : 10 },
|
||||||
visible: (i: number) => ({
|
visible: (i: number) => ({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
scale: 1,
|
y: 0,
|
||||||
transition: {
|
transition: {
|
||||||
delay: shouldReduceMotion ? 0 : i * 0.2,
|
delay: shouldReduceMotion ? 0 : i * 0.2,
|
||||||
type: 'spring' as const,
|
duration: 0.4
|
||||||
stiffness: 200,
|
|
||||||
damping: 20
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" overflow="hidden" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} display="flex" flexDirection="col" justifyContent="center" gap={{ base: 2, sm: 4, md: 6, lg: 8 }}>
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" overflow="hidden" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} display="flex" flexDirection="col" justifyContent="center" gap={{ base: 2, sm: 4, md: 6, lg: 8 }}>
|
||||||
<Box display="flex" flexDirection={{ base: 'col', md: 'row' }} alignItems="center" justifyContent="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
<Box display="flex" flexDirection={{ base: 'col', md: 'row' }} alignItems="center" justifyContent="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
||||||
{steps.map((step, i) => (
|
{steps.map((step, i) => (
|
||||||
<Box key={step.name} display="flex" alignItems="center" flexShrink={0}>
|
<Box key={step.name} display="flex" alignItems="center" flexShrink={0}>
|
||||||
@@ -134,84 +124,48 @@ export function ProtestWorkflowMockup() {
|
|||||||
position="relative"
|
position="relative"
|
||||||
w={{ base: 8, sm: 10, md: 12, lg: 14 }}
|
w={{ base: 8, sm: 10, md: 12, lg: 14 }}
|
||||||
h={{ base: 8, sm: 10, md: 12, lg: 14 }}
|
h={{ base: 8, sm: 10, md: 12, lg: 14 }}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
mb={{ base: 1, sm: 1.5, md: 2 }}
|
mb={{ base: 1, sm: 1.5, md: 2 }}
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
className={getStatusStyles(step.status)}
|
||||||
className={getStatusColor(step.status)}
|
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.1,
|
scale: 1.05,
|
||||||
boxShadow: step.status === 'active'
|
borderColor: '#198CFF',
|
||||||
? '0 0 32px rgba(255,197,86,0.4)'
|
|
||||||
: step.status === 'resolved'
|
|
||||||
? '0 0 32px rgba(111,227,122,0.4)'
|
|
||||||
: '0 0 20px rgba(34,38,42,0.4)',
|
|
||||||
transition: { duration: 0.2 }
|
transition: { duration: 0.2 }
|
||||||
}}
|
}}
|
||||||
transition={{ type: 'spring', stiffness: 300, damping: 15 }}
|
|
||||||
>
|
>
|
||||||
<Box as="svg" w={{ base: 4, sm: 5, md: 6, lg: 7 }} h={{ base: 4, sm: 5, md: 6, lg: 7 }} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<Box as="svg" w={{ base: 4, sm: 5, md: 6, lg: 7 }} h={{ base: 4, sm: 5, md: 6, lg: 7 }} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={step.icon} />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={step.icon} />
|
||||||
</Box>
|
</Box>
|
||||||
{step.status === 'active' && (
|
{step.status === 'active' && (
|
||||||
<Box
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="warning-amber" />
|
||||||
as={motion.div}
|
|
||||||
position="absolute"
|
|
||||||
inset="0"
|
|
||||||
rounded="lg"
|
|
||||||
animate={shouldReduceMotion ? {} : {
|
|
||||||
boxShadow: [
|
|
||||||
'0 0 20px rgba(25,140,255,0.3)',
|
|
||||||
'0 0 32px rgba(255,197,86,0.5)',
|
|
||||||
'0 0 20px rgba(25,140,255,0.3)'
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
transition={{ duration: 2, repeat: Infinity }}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '8px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-white"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
mb={0.5}
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
{step.name}
|
{step.name}
|
||||||
</Text>
|
</Text>
|
||||||
<Box
|
|
||||||
as={motion.div}
|
|
||||||
h={{ base: 0.5, sm: 1, md: 1.5 }}
|
|
||||||
w={{ base: 6, sm: 8, md: 10, lg: 12 }}
|
|
||||||
rounded="sm"
|
|
||||||
bg={
|
|
||||||
step.status === 'pending' ? 'bg-charcoal-outline' :
|
|
||||||
step.status === 'active' ? 'bg-warning-amber/30' :
|
|
||||||
'bg-performance-green/30'
|
|
||||||
}
|
|
||||||
animate={shouldReduceMotion ? {} : step.status === 'active' ? {
|
|
||||||
opacity: [0.5, 1, 0.5]
|
|
||||||
} : {}}
|
|
||||||
transition={{ duration: 2, repeat: Infinity }}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{i < steps.length - 1 && (
|
{i < steps.length - 1 && (
|
||||||
<Box
|
<Box
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="hidden md:block"
|
className="hidden md:block"
|
||||||
position="relative"
|
position="relative"
|
||||||
ml={1}
|
ml={2}
|
||||||
|
mr={2}
|
||||||
>
|
>
|
||||||
<Box as="svg" w={{ base: 3, sm: 4, md: 5 }} h={{ base: 3, sm: 4, md: 5 }} viewBox="0 0 24 24" fill="none">
|
<Box as="svg" w={{ base: 3, sm: 4, md: 5 }} h={{ base: 3, sm: 4, md: 5 }} viewBox="0 0 24 24" fill="none">
|
||||||
{/* eslint-disable-next-line gridpilot-rules/component-classification */}
|
<path d="M5 12h14m-7-7l7 7-7 7" stroke="#198CFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" opacity={0.5} />
|
||||||
<path d="M5 12h14m-7-7l7 7-7 7" stroke="#43C9E6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -225,9 +179,9 @@ export function ProtestWorkflowMockup() {
|
|||||||
animate={{ opacity: 1, scaleX: 1 }}
|
animate={{ opacity: 1, scaleX: 1 }}
|
||||||
transition={{ delay: shouldReduceMotion ? 0 : 0.8, duration: 0.6 }}
|
transition={{ delay: shouldReduceMotion ? 0 : 0.8, duration: 0.6 }}
|
||||||
position="relative"
|
position="relative"
|
||||||
h={{ base: 0.5, md: 1 }}
|
h="1"
|
||||||
bg="bg-charcoal-outline"
|
bg="white/5"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@@ -235,8 +189,7 @@ export function ProtestWorkflowMockup() {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg="bg-gradient-to-r from-neon-aqua to-primary-blue"
|
bg="primary-accent"
|
||||||
rounded="full"
|
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: `${((activeStep + 1) / steps.length) * 100}%` }}
|
animate={{ width: `${((activeStep + 1) / steps.length) * 100}%` }}
|
||||||
transition={{ duration: 0.5, ease: 'easeOut' }}
|
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function RaceHistoryMockup() {
|
|||||||
// Simple, elegant mobile version - just the core story
|
// Simple, elegant mobile version - just the core story
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite to-iron-gray" rounded="lg" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -40,14 +40,14 @@ export function RaceHistoryMockup() {
|
|||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
{/* Race result - clean and simple */}
|
{/* Race result - clean and simple */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box bg="bg-iron-gray/60" rounded="xl" p={4} border borderWidth="2px" borderColor="border-primary-blue/40">
|
<Box bg="panel-gray/60" rounded="none" p={4} border borderWidth="1px" borderColor="primary-accent/40">
|
||||||
<Box display="flex" alignItems="center" gap={3}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
<Box h="14" w="14" bg="bg-primary-blue/20" rounded="lg" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
<Box h="14" w="14" bg="primary-accent/10" rounded="none" display="flex" alignItems="center" justifyContent="center" flexShrink={0} border borderColor="primary-accent/30">
|
||||||
<Text size="2xl" weight="bold" color="text-primary-blue">P3</Text>
|
<Text size="2xl" weight="bold" color="text-primary-accent" font="mono">P3</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="base" weight="semibold" color="text-white" block>Watkins Glen</Text>
|
<Text size="base" weight="bold" color="text-white" block className="uppercase tracking-widest">Watkins Glen</Text>
|
||||||
<Text size="xs" color="text-white" opacity={0.6} block>GT3 Sprint</Text>
|
<Text size="xs" color="text-gray-500" block font="mono">GT3 SPRINT</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -55,30 +55,32 @@ export function RaceHistoryMockup() {
|
|||||||
|
|
||||||
{/* Simple arrow */}
|
{/* Simple arrow */}
|
||||||
<Box as={motion.div} variants={itemVariants} display="flex" justifyContent="center">
|
<Box as={motion.div} variants={itemVariants} display="flex" justifyContent="center">
|
||||||
<Text color="text-primary-blue" size="2xl">↓</Text>
|
<Text color="text-primary-accent" size="2xl">↓</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Updates - minimal */}
|
{/* Updates - minimal */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box bg="bg-iron-gray/40" rounded="xl" p={3} border borderColor="border-charcoal-outline">
|
<Box bg="panel-gray/40" rounded="none" p={3} border borderColor="border-gray/50">
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.7}
|
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
mb={2}
|
mb={2}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
|
uppercase
|
||||||
|
className="tracking-widest"
|
||||||
>
|
>
|
||||||
Profile Updated
|
Profile Updated
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" gap={2}>
|
<Box display="flex" gap={2}>
|
||||||
<Box flexGrow={1} bg="bg-deep-graphite/50" rounded="sm" py={2} textAlign="center" border borderColor="border-primary-blue/30">
|
<Box flexGrow={1} bg="graphite-black" rounded="none" py={2} textAlign="center" border borderColor="primary-accent/30">
|
||||||
<Text size="xs" color="text-primary-blue" weight="semibold" block>Stats ↑</Text>
|
<Text size="xs" color="text-primary-accent" weight="bold" block font="mono">STATS ↑</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} bg="bg-deep-graphite/50" rounded="sm" py={2} textAlign="center" border borderColor="border-performance-green/30">
|
<Box flexGrow={1} bg="graphite-black" rounded="none" py={2} textAlign="center" border borderColor="success-green/30">
|
||||||
<Text size="xs" color="text-performance-green" weight="semibold" block>+12</Text>
|
<Text size="xs" color="text-success-green" weight="bold" block font="mono">+12</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -92,7 +94,7 @@ export function RaceHistoryMockup() {
|
|||||||
|
|
||||||
// Desktop version - richer with more updates
|
// Desktop version - richer with more updates
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 5, lg: 8 }} overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
initial="hidden"
|
initial="hidden"
|
||||||
@@ -104,60 +106,43 @@ export function RaceHistoryMockup() {
|
|||||||
<Stack gap={{ base: 2, sm: 3, md: 4, lg: 5 }}>
|
<Stack gap={{ base: 2, sm: 3, md: 4, lg: 5 }}>
|
||||||
{/* Race Result Card - Enhanced */}
|
{/* Race Result Card - Enhanced */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box bg="bg-iron-gray" rounded="lg" p={{ base: 2, sm: 3, md: 4, lg: 5 }} border borderWidth="2px" borderColor="border-primary-blue/40">
|
<Box bg="panel-gray/40" rounded="none" p={{ base: 2, sm: 3, md: 4, lg: 5 }} border borderWidth="1px" borderColor="primary-accent/40">
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
||||||
<Box position="relative" h={{ base: 12, sm: 14, md: 16, lg: 20 }} w={{ base: 12, sm: 14, md: 16, lg: 20 }} bg="bg-charcoal-outline" rounded="lg" border borderWidth="2px" borderColor="border-primary-blue/30" overflow="hidden" flexShrink={0}>
|
<Box position="relative" h={{ base: 12, sm: 14, md: 16, lg: 20 }} w={{ base: 12, sm: 14, md: 16, lg: 20 }} bg="graphite-black" rounded="none" border borderWidth="1px" borderColor="primary-accent/30" overflow="hidden" flexShrink={0}>
|
||||||
<Box position="absolute" inset="0" bg="bg-gradient-to-br from-primary-blue/20 to-performance-green/20" />
|
<Box position="absolute" inset="0" bg="gradient-to-br from-primary-accent/10 to-success-green/10" />
|
||||||
<Box position="absolute" inset="0" display="flex" alignItems="center" justifyContent="center">
|
<Box position="absolute" inset="0" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text color="text-white" size={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} weight="bold">P3</Text>
|
<Text color="text-white" size={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} weight="bold" font="mono">P3</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth="0">
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }} mb={1}>
|
<Box display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }} mb={1}>
|
||||||
<Text size={{ base: 'sm', sm: 'base', md: 'lg' }}>🏁</Text>
|
<Box w="1" h="4" bg="primary-accent" />
|
||||||
<Heading level={3} fontSize={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="semibold" color="text-white" truncate>Watkins Glen</Heading>
|
<Heading level={3} fontSize={{ base: 'sm', sm: 'base', md: 'lg', lg: 'xl' }} weight="bold" color="text-white" truncate className="uppercase tracking-widest">Watkins Glen</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} color="text-white" opacity={0.6} mb={1} block>GT3 Sprint Race</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} color="text-gray-500" mb={1} block font="mono">GT3 SPRINT RACE</Text>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 2, sm: 3, md: 4 }}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-600"
|
||||||
opacity={0.5}
|
font="mono"
|
||||||
>
|
>
|
||||||
24 drivers
|
24 DRIVERS
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-600"
|
||||||
opacity={0.5}
|
|
||||||
>
|
>
|
||||||
•
|
•
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-600"
|
||||||
opacity={0.5}
|
font="mono"
|
||||||
>
|
>
|
||||||
45 min
|
45 MIN
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-white"
|
|
||||||
opacity={0.5}
|
|
||||||
>
|
|
||||||
•
|
|
||||||
</Text>
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-white"
|
|
||||||
opacity={0.5}
|
|
||||||
>
|
|
||||||
Just finished
|
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -178,12 +163,13 @@ export function RaceHistoryMockup() {
|
|||||||
}}
|
}}
|
||||||
transition={{ duration: 2, repeat: Infinity }}
|
transition={{ duration: 2, repeat: Infinity }}
|
||||||
>
|
>
|
||||||
<Text color="text-primary-blue" size={{ base: '2xl', sm: '3xl', md: '4xl' }}>↓</Text>
|
<Text color="text-primary-accent" size={{ base: '2xl', sm: '3xl', md: '4xl' }}>↓</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-primary-blue"
|
color="text-primary-accent"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Auto-sync
|
Auto-sync
|
||||||
</Text>
|
</Text>
|
||||||
@@ -192,27 +178,28 @@ export function RaceHistoryMockup() {
|
|||||||
|
|
||||||
{/* Profile Updates Grid - More detailed */}
|
{/* Profile Updates Grid - More detailed */}
|
||||||
<Box as={motion.div} variants={itemVariants}>
|
<Box as={motion.div} variants={itemVariants}>
|
||||||
<Box bg="bg-iron-gray/80" rounded="lg" p={{ base: 2, sm: 3, md: 4, lg: 5 }} border borderColor="border-charcoal-outline">
|
<Box bg="panel-gray/20" rounded="none" p={{ base: 2, sm: 3, md: 4, lg: 5 }} border borderColor="border-gray/50">
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" opacity={0.8} textAlign="center" mb={{ base: 2, sm: 3, md: 4 }} block>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="bold" color="text-gray-500" textAlign="center" mb={{ base: 2, sm: 3, md: 4 }} block className="uppercase tracking-[0.2em]">
|
||||||
Profile Updates
|
Profile Updates
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box display="grid" gridCols={2} gap={{ base: 2, sm: 3, md: 4 }}>
|
<Box display="grid" gridCols={2} gap={{ base: 2, sm: 3, md: 4 }}>
|
||||||
{/* Career Stats Update */}
|
{/* Career Stats Update */}
|
||||||
<Box bg="bg-deep-graphite/50" rounded="lg" p={{ base: 2, sm: 3, md: 4 }} border borderColor="border-primary-blue/30">
|
<Box bg="graphite-black" rounded="none" p={{ base: 2, sm: 3, md: 4 }} border borderColor="primary-accent/30">
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Career Stats
|
Career Stats
|
||||||
</Text>
|
</Text>
|
||||||
<Box
|
<Box
|
||||||
as={motion.span}
|
as={motion.span}
|
||||||
color="text-performance-green"
|
color="text-success-green"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
animate={shouldReduceMotion ? {} : { scale: [1, 1.2, 1] }}
|
animate={shouldReduceMotion ? {} : { scale: [1, 1.2, 1] }}
|
||||||
@@ -227,26 +214,28 @@ export function RaceHistoryMockup() {
|
|||||||
color="text-white"
|
color="text-white"
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Wins: 24 → 25
|
WINS: 24 → 25
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Rating Update */}
|
{/* Rating Update */}
|
||||||
<Box bg="bg-deep-graphite/50" rounded="lg" p={{ base: 2, sm: 3, md: 4 }} border borderColor="border-performance-green/30">
|
<Box bg="graphite-black" rounded="none" p={{ base: 2, sm: 3, md: 4 }} border borderColor="success-green/30">
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Rating
|
Rating
|
||||||
</Text>
|
</Text>
|
||||||
<Box
|
<Box
|
||||||
as={motion.span}
|
as={motion.span}
|
||||||
color="text-performance-green"
|
color="text-success-green"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
font="mono"
|
font="mono"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
@@ -262,26 +251,28 @@ export function RaceHistoryMockup() {
|
|||||||
color="text-white"
|
color="text-white"
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
1342 → 1354
|
1342 → 1354
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Season Points Update */}
|
{/* Season Points Update */}
|
||||||
<Box bg="bg-deep-graphite/50" rounded="lg" p={{ base: 2, sm: 3, md: 4 }} border borderColor="border-warning-amber/30">
|
<Box bg="graphite-black" rounded="none" p={{ base: 2, sm: 3, md: 4 }} border borderColor="warning-amber/30">
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Season
|
Season
|
||||||
</Text>
|
</Text>
|
||||||
<Box
|
<Box
|
||||||
as={motion.span}
|
as={motion.span}
|
||||||
color="text-warning-amber"
|
color="text-warning-amber"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
font="mono"
|
font="mono"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
@@ -297,26 +288,28 @@ export function RaceHistoryMockup() {
|
|||||||
color="text-white"
|
color="text-white"
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
248 → 266 pts
|
248 → 266 PTS
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Team Points Update */}
|
{/* Team Points Update */}
|
||||||
<Box bg="bg-deep-graphite/50" rounded="lg" p={{ base: 2, sm: 3, md: 4 }} border borderColor="border-neon-aqua/30">
|
<Box bg="graphite-black" rounded="none" p={{ base: 2, sm: 3, md: 4 }} border borderColor="telemetry-aqua/30">
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={2}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-500"
|
||||||
opacity={0.7}
|
weight="bold"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Team
|
Team
|
||||||
</Text>
|
</Text>
|
||||||
<Box
|
<Box
|
||||||
as={motion.span}
|
as={motion.span}
|
||||||
color="text-neon-aqua"
|
color="text-telemetry-aqua"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
font="mono"
|
font="mono"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
@@ -332,8 +325,9 @@ export function RaceHistoryMockup() {
|
|||||||
color="text-white"
|
color="text-white"
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Contributing
|
CONTRIBUTING
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -18,30 +18,30 @@ export function SimPlatformMockup() {
|
|||||||
// Simple mobile version - just the essence of cross-platform
|
// Simple mobile version - just the essence of cross-platform
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite to-iron-gray" rounded="lg" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={4} overflow="hidden" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Stack gap={3} w="full">
|
<Stack gap={3} w="full">
|
||||||
{/* Active Platform - Clean */}
|
{/* Active Platform - Clean */}
|
||||||
<Box bg="bg-iron-gray/60" border borderWidth="2px" borderColor="border-primary-blue" rounded="xl" p={4} position="relative">
|
<Box bg="panel-gray/60" border borderWidth="1px" borderColor="primary-accent" rounded="none" p={4} position="relative">
|
||||||
<Box position="absolute" top="3" right="3">
|
<Box position="absolute" top="3" right="3">
|
||||||
<Box w="2" h="2" rounded="full" bg="bg-performance-green"
|
<Box w="1.5" h="1.5" rounded="full" bg="success-green"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
className="animate-pulse"
|
className="animate-pulse"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" alignItems="center" gap={3}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
<Box w="12" h="12" rounded="lg" bg="bg-primary-blue/20" display="flex" alignItems="center" justifyContent="center" flexShrink={0}>
|
<Box w="12" h="12" rounded="none" bg="primary-accent/10" display="flex" alignItems="center" justifyContent="center" flexShrink={0} border borderColor="primary-accent/30">
|
||||||
<Text size="2xl" weight="bold" color="text-primary-blue">iR</Text>
|
<Text size="2xl" weight="bold" color="text-primary-accent" font="mono">iR</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="base" weight="semibold" color="text-white" block>iRacing</Text>
|
<Text size="base" weight="bold" color="text-white" block className="uppercase tracking-widest">iRacing</Text>
|
||||||
<Text size="xs" color="text-performance-green" block>Active</Text>
|
<Text size="xs" color="text-success-green" block font="mono">ACTIVE</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Simple "more coming" indicator */}
|
{/* Simple "more coming" indicator */}
|
||||||
<Box bg="bg-iron-gray/30" rounded="xl" p={3} border borderColor="border-charcoal-outline/50">
|
<Box bg="panel-gray/20" rounded="none" p={3} border borderStyle="dashed" borderColor="border-gray/30">
|
||||||
<Text size="xs" textAlign="center" color="text-slate-500" block>More platforms coming</Text>
|
<Text size="xs" textAlign="center" color="text-gray-600" block font="mono" uppercase tracking-widest>More platforms coming</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -51,150 +51,161 @@ export function SimPlatformMockup() {
|
|||||||
// Desktop version
|
// Desktop version
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth maxWidth="3xl" mx="auto">
|
<Box position="relative" fullWidth maxWidth="3xl" mx="auto">
|
||||||
<Box bg="bg-iron-gray" border borderColor="border-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} shadow="2xl">
|
<Box bg="panel-gray/40" border borderColor="border-gray/50" rounded="none" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} shadow="2xl">
|
||||||
<Stack gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
<Stack gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" pb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} borderBottom borderColor="border-charcoal-outline">
|
<Box display="flex" alignItems="center" justifyContent="between" pb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} borderBottom borderColor="border-gray/30">
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} weight="semibold" color="text-slate-300">Platform Support</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-400" className="uppercase tracking-widest">Platform Support</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-500"
|
color="text-gray-600"
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Active: 1 | Planned: 3
|
ACTIVE: 1 | PLANNED: 3
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box display="grid" gridCols={{ base: 1, md: 2 }} gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="grid" gridCols={{ base: 1, md: 2 }} gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
{/* iRacing - Active */}
|
{/* iRacing - Active */}
|
||||||
<Box bg="bg-deep-graphite" border borderWidth="2px" borderColor="border-primary-blue" rounded="lg" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} position="relative" overflow="hidden">
|
<Box bg="graphite-black" border borderWidth="1px" borderColor="primary-accent/40" rounded="none" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} position="relative" overflow="hidden">
|
||||||
<Box position="absolute" top={{ base: 1, sm: 1.5, md: 2 }} right={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box position="absolute" top={{ base: 1, sm: 1.5, md: 2 }} right={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Box w={{ base: 1, sm: 1.5, md: 2 }} h={{ base: 1, sm: 1.5, md: 2 }} rounded="full" bg="bg-performance-green"
|
<Box w="1.5" h="1.5" rounded="full" bg="success-green"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
className="animate-pulse"
|
className="animate-pulse"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="sm" bg="bg-primary-blue/10" display="flex" alignItems="center" justifyContent="center">
|
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="none" bg="primary-accent/10" display="flex" alignItems="center" justifyContent="center" border borderColor="primary-accent/20">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-primary-blue">iR</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-primary-accent" font="mono">iR</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="semibold" color="text-white" block>iRacing</Text>
|
<Text size={{ base: 'xs', sm: 'sm', md: 'base' }} weight="bold" color="text-white" block className="uppercase tracking-widest">iRacing</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-performance-green"
|
color="text-success-green"
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Active
|
ACTIVE
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-400"
|
color="text-gray-500"
|
||||||
mt={{ base: 1.5, sm: 2, md: 3 }}
|
mt={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Full integration
|
FULL INTEGRATION
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* ACC - Future */}
|
{/* ACC - Future */}
|
||||||
<Box bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.4}>
|
<Box bg="panel-gray/20" border borderColor="border-gray/30" rounded="none" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.6}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="sm" bg="bg-slate-700/20" display="flex" alignItems="center" justifyContent="center">
|
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="none" bg="gray-800/20" display="flex" alignItems="center" justifyContent="center" border borderColor="gray-800/30">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-slate-600">AC</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-gray-700" font="mono">AC</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} weight="semibold" color="text-slate-500" block>ACC</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-600" block className="uppercase tracking-widest">ACC</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Planned
|
PLANNED
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
mt={{ base: 1.5, sm: 2, md: 3 }}
|
mt={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Coming later
|
COMING LATER
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* rFactor 2 - Future */}
|
{/* rFactor 2 - Future */}
|
||||||
<Box bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.4}>
|
<Box bg="panel-gray/20" border borderColor="border-gray/30" rounded="none" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.6}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="sm" bg="bg-slate-700/20" display="flex" alignItems="center" justifyContent="center">
|
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="none" bg="gray-800/20" display="flex" alignItems="center" justifyContent="center" border borderColor="gray-800/30">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-slate-600">rF</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-gray-700" font="mono">rF</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} weight="semibold" color="text-slate-500" block>rFactor 2</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-600" block className="uppercase tracking-widest">rFactor 2</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Planned
|
PLANNED
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
mt={{ base: 1.5, sm: 2, md: 3 }}
|
mt={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Coming later
|
COMING LATER
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* LMU - Future */}
|
{/* LMU - Future */}
|
||||||
<Box bg="bg-deep-graphite" border borderColor="border-charcoal-outline" rounded="lg" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.4}>
|
<Box bg="panel-gray/20" border borderColor="border-gray/30" rounded="none" p={{ base: 1.5, sm: 2, md: 3, lg: 4 }} opacity={0.6}>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3 }}>
|
||||||
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="sm" bg="bg-slate-700/20" display="flex" alignItems="center" justifyContent="center">
|
<Box w={{ base: 8, sm: 10, md: 12 }} h={{ base: 8, sm: 10, md: 12 }} rounded="none" bg="gray-800/20" display="flex" alignItems="center" justifyContent="center" border borderColor="gray-800/30">
|
||||||
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-slate-600">LM</Text>
|
<Text size={{ base: 'base', sm: 'xl', md: '2xl' }} weight="bold" color="text-gray-700" font="mono">LM</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }} weight="semibold" color="text-slate-500" block>Le Mans Ult.</Text>
|
<Text size={{ base: 'xs', sm: 'sm' }} weight="bold" color="text-gray-600" block className="uppercase tracking-widest">Le Mans Ult.</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Planned
|
PLANNED
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-600"
|
color="text-gray-700"
|
||||||
mt={{ base: 1.5, sm: 2, md: 3 }}
|
mt={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
>
|
>
|
||||||
Coming later
|
COMING LATER
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box pt={{ base: 1.5, sm: 2, md: 3, lg: 4 }} borderTop borderColor="border-charcoal-outline">
|
<Box pt={{ base: 1.5, sm: 2, md: 3, lg: 4 }} borderTop borderStyle="dashed" borderColor="border-gray/30">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-slate-500"
|
color="text-gray-600"
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Your identity stays with you across platforms
|
Your identity stays with you across platforms
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -17,30 +17,30 @@ export function StandingsTableMockup() {
|
|||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={3} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={3} overflow="hidden">
|
||||||
<Box mb={3}>
|
<Box mb={3}>
|
||||||
<Box display="flex" alignItems="center" gap={2} pb={2} borderBottom borderColor="border-charcoal-outline">
|
<Box display="flex" alignItems="center" gap={2} pb={2} borderBottom borderColor="border-gray/30">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '12px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
color="text-gray-400"
|
color="text-gray-600"
|
||||||
>
|
>
|
||||||
#
|
POS
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" flexGrow={1} weight="semibold" color="text-white">Driver</Text>
|
<Text size="xs" flexGrow={1} weight="bold" color="text-gray-500" className="uppercase tracking-widest">Driver</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '12px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
color="text-gray-400"
|
color="text-gray-600"
|
||||||
>
|
>
|
||||||
Pts
|
PTS
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={2}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3, 4, 5].map((i) => (
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -49,49 +49,41 @@ export function StandingsTableMockup() {
|
|||||||
gap={2}
|
gap={2}
|
||||||
py={2}
|
py={2}
|
||||||
px={2}
|
px={2}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
border
|
border
|
||||||
bg={i <= 3 ? 'bg-gradient-to-r from-performance-green/10 to-iron-gray' : 'bg-iron-gray'}
|
bg={i <= 3 ? 'panel-gray/40' : 'transparent'}
|
||||||
borderColor={i <= 3 ? 'border-performance-green/20' : 'border-charcoal-outline'}
|
borderColor={i <= 3 ? 'primary-accent/20' : 'border-gray/20'}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
h="7"
|
h="6"
|
||||||
w="7"
|
w="6"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
weight="semibold"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '12px' }}
|
style={{ fontSize: '10px' }}
|
||||||
bg={i <= 3 ? 'bg-primary-blue' : 'bg-charcoal-outline'}
|
bg={i <= 3 ? 'primary-accent' : 'panel-gray'}
|
||||||
color={i <= 3 ? 'text-white' : 'text-gray-400'}
|
color={i <= 3 ? 'text-white' : 'text-gray-500'}
|
||||||
shadow={i <= 3 ? '0_0_12px_rgba(25,140,255,0.3)' : undefined}
|
font="mono"
|
||||||
|
weight="bold"
|
||||||
>
|
>
|
||||||
{i}
|
{i}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} display="flex" alignItems="center" gap={2}>
|
<Box flexGrow={1} display="flex" alignItems="center" gap={2}>
|
||||||
<Box h="5" w="5" rounded="full" bg="bg-charcoal-outline" border borderColor="border-primary-blue/20" display="flex" alignItems="center" justifyContent="center">
|
<Box h="1.5" fullWidth maxWidth="80px" bg="white/10" rounded="none" />
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '12px' }}
|
|
||||||
>
|
|
||||||
🏎️
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
<Box h="2.5" fullWidth maxWidth="100px" bg="bg-white/10" rounded="sm" />
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box position="relative" w="16" h="5" bg="bg-charcoal-outline" rounded="sm" border borderColor="border-primary-blue/20" overflow="hidden">
|
<Box position="relative" w="12" h="4" bg="graphite-black" rounded="none" border borderColor="border-gray/30" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg={i <= 3 ? 'bg-gradient-to-r from-performance-green/40 to-performance-green/20' : 'bg-gradient-to-r from-iron-gray to-charcoal-outline'}
|
bg={i <= 3 ? 'primary-accent/40' : 'gray-700/40'}
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ width: `${100 - (i - 1) * 15}%` }}
|
style={{ width: `${100 - (i - 1) * 15}%` }}
|
||||||
/>
|
/>
|
||||||
<Box position="relative" h="full" display="flex" alignItems="center" justifyContent="center">
|
<Box position="relative" h="full" display="flex" alignItems="center" justifyContent="center">
|
||||||
<Text size="xs" font="mono" weight="semibold" color="text-white">
|
<Text size="xs" font="mono" weight="bold" color="text-white">
|
||||||
{300 - i * 20}
|
{300 - i * 20}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -104,47 +96,48 @@ export function StandingsTableMockup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getRowAnimation = (i: number) => ({
|
const getRowAnimation = (i: number) => ({
|
||||||
hidden: { opacity: 0, y: shouldReduceMotion ? 0 : 10 },
|
hidden: { opacity: 0, x: shouldReduceMotion ? 0 : -10 },
|
||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
x: 0,
|
||||||
transition: {
|
transition: {
|
||||||
delay: shouldReduceMotion ? 0 : i * 0.05,
|
delay: shouldReduceMotion ? 0 : i * 0.05,
|
||||||
type: 'spring' as const,
|
duration: 0.3
|
||||||
stiffness: 300,
|
|
||||||
damping: 24
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} overflow="hidden">
|
||||||
<Box mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
<Box mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }}>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
color="text-white"
|
color="text-gray-600"
|
||||||
opacity={0.5}
|
|
||||||
mb={{ base: 1.5, sm: 2, md: 3 }}
|
mb={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
block
|
block
|
||||||
|
font="mono"
|
||||||
|
uppercase
|
||||||
|
className="tracking-widest"
|
||||||
>
|
>
|
||||||
Real-time standings updated after every race
|
Real-time standings updated after every race
|
||||||
</Text>
|
</Text>
|
||||||
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} pb={{ base: 1.5, sm: 2, md: 3 }} borderBottom borderColor="border-charcoal-outline">
|
<Box display="flex" alignItems="center" gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }} pb={{ base: 1.5, sm: 2, md: 3 }} borderBottom borderColor="border-gray/30">
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
>
|
>
|
||||||
#
|
POS
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
color="text-white"
|
color="text-gray-400"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Driver
|
Driver
|
||||||
</Text>
|
</Text>
|
||||||
@@ -152,9 +145,8 @@ export function StandingsTableMockup() {
|
|||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
className="hidden md:block uppercase tracking-widest"
|
||||||
className="hidden md:block"
|
|
||||||
>
|
>
|
||||||
Wins
|
Wins
|
||||||
</Text>
|
</Text>
|
||||||
@@ -163,21 +155,16 @@ export function StandingsTableMockup() {
|
|||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
Points
|
Points
|
||||||
</Text>
|
</Text>
|
||||||
<Text color="text-performance-green"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '8px' }}
|
|
||||||
>
|
|
||||||
●
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={0.5}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -189,18 +176,18 @@ export function StandingsTableMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
gap={{ base: 1.5, sm: 2, md: 3, lg: 4 }}
|
||||||
py={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }}
|
py={{ base: 1.5, sm: 2, md: 2.5 }}
|
||||||
px={{ base: 1.5, sm: 2, md: 3 }}
|
px={{ base: 1.5, sm: 2, md: 3 }}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
border
|
border
|
||||||
transition
|
transition
|
||||||
bg={i <= 3 ? 'bg-gradient-to-r from-performance-green/10 to-iron-gray' : 'bg-iron-gray'}
|
bg={i <= 3 ? 'panel-gray/40' : 'transparent'}
|
||||||
borderColor={i <= 3 ? 'border-performance-green/20' : 'border-charcoal-outline'}
|
borderColor={i <= 3 ? 'primary-accent/20' : 'border-gray/20'}
|
||||||
onHoverStart={() => !shouldReduceMotion && setHoveredRow(i)}
|
onHoverStart={() => !shouldReduceMotion && setHoveredRow(i)}
|
||||||
onHoverEnd={() => setHoveredRow(null)}
|
onHoverEnd={() => setHoveredRow(null)}
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.01,
|
x: 4,
|
||||||
boxShadow: '0 0 20px rgba(25,140,255,0.3)',
|
borderColor: '#198CFF30',
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -208,30 +195,23 @@ export function StandingsTableMockup() {
|
|||||||
as={motion.div}
|
as={motion.div}
|
||||||
h={{ base: 6, sm: 7 }}
|
h={{ base: 6, sm: 7 }}
|
||||||
w={{ base: 6, sm: 7 }}
|
w={{ base: 6, sm: 7 }}
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '12px' }}
|
style={{ fontSize: '11px' }}
|
||||||
bg={i <= 3 ? 'bg-primary-blue' : 'bg-charcoal-outline'}
|
bg={i <= 3 ? 'primary-accent' : 'panel-gray'}
|
||||||
color={i <= 3 ? 'text-white' : 'text-gray-400'}
|
color={i <= 3 ? 'text-white' : 'text-gray-500'}
|
||||||
animate={
|
font="mono"
|
||||||
shouldReduceMotion ? {} : i <= 3 && hoveredRow === i
|
|
||||||
? { scale: 1.15, boxShadow: '0 0 28px rgba(25,140,255,0.5)' }
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{i}
|
{i}
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Box flexGrow={1} display="flex" alignItems="center" gap={{ base: 1, sm: 1.5, md: 2 }}>
|
||||||
<Box h={{ base: 4, sm: 5, md: 6 }} w={{ base: 4, sm: 5, md: 6 }} rounded="full" bg="bg-charcoal-outline" border borderColor="border-primary-blue/20" display="flex" alignItems="center" justifyContent="center">
|
<Box h="1.5" fullWidth maxWidth={{ base: '80px', sm: '100px', md: '140px' }} bg="white/10" rounded="none" />
|
||||||
<Text size={{ base: 'xs', sm: 'sm' }}>🏎️</Text>
|
|
||||||
</Box>
|
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} fullWidth maxWidth={{ base: '80px', sm: '100px', md: '140px' }} bg="bg-white/10" rounded="sm" />
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }} w={{ base: 10, sm: 12, md: 16 }} bg="bg-white/5" rounded="sm" font="mono"
|
<Box h="1" w={{ base: 10, sm: 12, md: 16 }} bg="white/5" rounded="none"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
className="hidden md:block"
|
className="hidden md:block"
|
||||||
/>
|
/>
|
||||||
@@ -272,13 +252,13 @@ function AnimatedPoints({
|
|||||||
const percentage = (points / 300) * 100;
|
const percentage = (points / 300) * 100;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" w={{ base: 12, sm: 16, md: 20, lg: 24 }} h={{ base: 4, sm: 5, md: 6, lg: 7 }} bg="bg-charcoal-outline" rounded="sm" border borderColor="border-primary-blue/20" overflow="hidden">
|
<Box position="relative" w={{ base: 12, sm: 16, md: 20, lg: 24 }} h={{ base: 4, sm: 5, md: 6 }} bg="graphite-black" rounded="none" border borderColor="border-gray/30" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
bg={position <= 3 ? 'bg-gradient-to-r from-performance-green/40 to-performance-green/20' : 'bg-gradient-to-r from-iron-gray to-charcoal-outline'}
|
bg={position <= 3 ? 'primary-accent/40' : 'gray-700/40'}
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: `${percentage}%` }}
|
animate={{ width: `${percentage}%` }}
|
||||||
transition={{ duration: shouldReduceMotion ? 0 : 0.8, ease: 'easeOut', delay: 0.1 + position * 0.05 }}
|
transition={{ duration: shouldReduceMotion ? 0 : 0.8, ease: 'easeOut', delay: 0.1 + position * 0.05 }}
|
||||||
@@ -288,7 +268,7 @@ function AnimatedPoints({
|
|||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
// eslint-disable-next-line gridpilot-rules/component-classification
|
||||||
style={{ fontSize: '10px' }}
|
style={{ fontSize: '10px' }}
|
||||||
font="mono"
|
font="mono"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
color="text-white"
|
color="text-white"
|
||||||
>
|
>
|
||||||
{shouldReduceMotion ? points : <Box as={motion.span}>{spring}</Box>}
|
{shouldReduceMotion ? points : <Box as={motion.span}>{spring}</Box>}
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ export function TeamCompetitionMockup() {
|
|||||||
setIsMobile(window.innerWidth < 768);
|
setIsMobile(window.innerWidth < 768);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const teamColors = ['#198CFF', '#6FE37A', '#FFC556', '#43C9E6', '#9333EA'];
|
const teamColors = ['#198CFF', '#6FE37A', '#FFBE4D', '#4ED4E0', '#9333EA'];
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={3} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={3} overflow="hidden">
|
||||||
<Stack gap={4}>
|
<Stack gap={4}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={3} block>Drivers</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-widest">Drivers</Text>
|
||||||
<Stack gap={2}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -32,11 +32,11 @@ export function TeamCompetitionMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={2}
|
gap={2}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/40"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={2}
|
p={2}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/20"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@@ -44,54 +44,40 @@ export function TeamCompetitionMockup() {
|
|||||||
left="0"
|
left="0"
|
||||||
top="0"
|
top="0"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
w="1"
|
w="0.5"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1] }}
|
style={{ backgroundColor: teamColors[i-1] }}
|
||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
h="5"
|
h="5"
|
||||||
w="5"
|
w="5"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
borderColor: teamColors[i-1],
|
borderColor: teamColors[i-1],
|
||||||
backgroundColor: `${teamColors[i-1]}20`,
|
backgroundColor: `${teamColors[i-1]}10`,
|
||||||
borderWidth: '2px'
|
borderWidth: '1px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text color="text-white">{i}</Text>
|
<Text color="text-white" font="mono">{i}</Text>
|
||||||
</Box>
|
|
||||||
<Box h="6" w="6" rounded="full" display="flex" alignItems="center" justifyContent="center"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: `${teamColors[i-1]}20`, borderWidth: '1px', borderColor: teamColors[i-1], fontSize: '14px' }}
|
|
||||||
>
|
|
||||||
<Text>🏎️</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box h="2.5" w="full" bg="bg-white/10" rounded="sm" />
|
<Box h="1.5" w="full" bg="white/10" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box h="3" w="10" bg="bg-charcoal-outline" rounded="sm" display="flex" alignItems="center" justifyContent="center" color="text-white" opacity={0.7}
|
<Box h="3" w="10" bg="graphite-black" rounded="none" border borderColor="border-gray/30" />
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box h="px"
|
<Box h="px" bg="border-gray/30" />
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="bg-gradient-to-r from-transparent via-charcoal-outline to-transparent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="semibold" color="text-white" mb={3} block>Constructors</Text>
|
<Text size="xs" weight="bold" color="text-gray-500" mb={3} block className="uppercase tracking-widest">Constructors</Text>
|
||||||
<Stack gap={2}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3].map((i) => (
|
{[1, 2, 3].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -99,11 +85,11 @@ export function TeamCompetitionMockup() {
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={2}
|
gap={2}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/40"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={2}
|
p={2}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/20"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@@ -111,37 +97,16 @@ export function TeamCompetitionMockup() {
|
|||||||
left="0"
|
left="0"
|
||||||
top="0"
|
top="0"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
w="1"
|
w="0.5"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1] }}
|
style={{ backgroundColor: teamColors[i-1] }}
|
||||||
/>
|
/>
|
||||||
<Box
|
|
||||||
h="6"
|
|
||||||
w="6"
|
|
||||||
rounded="sm"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
border
|
|
||||||
borderWidth="2px"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
|
||||||
borderColor: teamColors[i-1],
|
|
||||||
backgroundColor: `${teamColors[i-1]}20`,
|
|
||||||
fontSize: '14px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text>🏁</Text>
|
|
||||||
</Box>
|
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box h="2.5" w="full" bg="bg-white/10" rounded="sm" mb={1} />
|
<Box h="1.5" w="full" bg="white/10" rounded="none" mb={1.5} />
|
||||||
<Box position="relative" h="1.5" bg="bg-charcoal-outline" rounded="full" overflow="hidden">
|
<Box position="relative" h="1" bg="graphite-black" rounded="none" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
rounded="full"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1], width: `${100 - (i-1) * 15}%` }}
|
style={{ backgroundColor: teamColors[i-1], width: `${100 - (i-1) * 15}%` }}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -160,11 +125,7 @@ export function TeamCompetitionMockup() {
|
|||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
x: 0,
|
x: 0,
|
||||||
transition: {
|
transition: { duration: 0.4 }
|
||||||
type: 'spring' as const,
|
|
||||||
stiffness: 100,
|
|
||||||
damping: 20
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,30 +134,24 @@ export function TeamCompetitionMockup() {
|
|||||||
visible: {
|
visible: {
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
x: 0,
|
x: 0,
|
||||||
transition: {
|
transition: { duration: 0.4 }
|
||||||
type: 'spring' as const,
|
|
||||||
stiffness: 100,
|
|
||||||
damping: 20
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const rowVariants = {
|
const rowVariants = {
|
||||||
hidden: { opacity: 0, scale: shouldReduceMotion ? 1 : 0.95 },
|
hidden: { opacity: 0, x: shouldReduceMotion ? 0 : -10 },
|
||||||
visible: (i: number) => ({
|
visible: (i: number) => ({
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
scale: 1,
|
x: 0,
|
||||||
transition: {
|
transition: {
|
||||||
delay: shouldReduceMotion ? 0 : 0.3 + i * 0.05,
|
delay: shouldReduceMotion ? 0 : 0.3 + i * 0.05,
|
||||||
type: 'spring' as const,
|
duration: 0.3
|
||||||
stiffness: 300,
|
|
||||||
damping: 25
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth fullHeight bg="bg-gradient-to-br from-deep-graphite via-iron-gray to-deep-graphite" rounded="lg" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} overflow="hidden">
|
<Box position="relative" fullWidth fullHeight bg="graphite-black" rounded="none" p={{ base: 1.5, sm: 3, md: 4, lg: 6 }} overflow="hidden">
|
||||||
<Box display="grid" gridCols={2} gap={{ base: 2, sm: 3, md: 4, lg: 6 }} fullHeight>
|
<Box display="grid" gridCols={2} gap={{ base: 2, sm: 3, md: 4, lg: 6 }} fullHeight>
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
@@ -205,17 +160,10 @@ export function TeamCompetitionMockup() {
|
|||||||
animate="visible"
|
animate="visible"
|
||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
<Box h={{ base: 3, sm: 4, md: 5 }} w={{ base: 16, sm: 20, md: 24 }} bg="bg-white/10" rounded="sm" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} display="flex" alignItems="center" justifyContent="center">
|
<Box h="6" w="24" bg="panel-gray" rounded="none" mb={4} display="flex" alignItems="center" justifyContent="center" border borderColor="border-gray/30">
|
||||||
<Text
|
<Text size="xs" color="text-gray-400" weight="bold" className="uppercase tracking-widest">DRIVERS</Text>
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-white"
|
|
||||||
weight="semibold"
|
|
||||||
>
|
|
||||||
Drivers
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Stack gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3, 4, 5].map((i) => (
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -227,18 +175,18 @@ export function TeamCompetitionMockup() {
|
|||||||
position="relative"
|
position="relative"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }}
|
gap={3}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/20"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1, sm: 1.5, md: 2, lg: 2.5 }}
|
p={2}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/20"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
onHoverStart={() => !shouldReduceMotion && setHoveredDriver(i)}
|
onHoverStart={() => !shouldReduceMotion && setHoveredDriver(i)}
|
||||||
onHoverEnd={() => setHoveredDriver(null)}
|
onHoverEnd={() => setHoveredDriver(null)}
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.02,
|
x: 4,
|
||||||
boxShadow: `0 0 20px ${teamColors[i-1]}40`,
|
borderColor: `${teamColors[i-1]}40`,
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -248,65 +196,35 @@ export function TeamCompetitionMockup() {
|
|||||||
top="0"
|
top="0"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
w="0.5"
|
w="0.5"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1] }}
|
style={{ backgroundColor: teamColors[i-1] }}
|
||||||
/>
|
/>
|
||||||
<Box
|
<Box
|
||||||
h={{ base: 3.5, sm: 4, md: 5 }}
|
h="5"
|
||||||
w={{ base: 3.5, sm: 4, md: 5 }}
|
w="5"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
borderWidth="1px"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: '10px',
|
fontSize: '10px',
|
||||||
borderColor: teamColors[i-1],
|
borderColor: teamColors[i-1],
|
||||||
backgroundColor: `${teamColors[i-1]}20`
|
backgroundColor: `${teamColors[i-1]}10`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text color="text-white">{i}</Text>
|
<Text color="text-white" font="mono">{i}</Text>
|
||||||
</Box>
|
|
||||||
<Box h={{ base: 5, sm: 6, md: 7 }} w={{ base: 5, sm: 6, md: 7 }} rounded="full" display="flex" alignItems="center" justifyContent="center"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: `${teamColors[i-1]}20`, borderWidth: '1px', borderColor: teamColors[i-1], fontSize: '12px' }}
|
|
||||||
>
|
|
||||||
<Text>🏎️</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box flexGrow={1} minWidth="0">
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w="full" bg="bg-white/10" rounded="sm" />
|
<Box h="1.5" w="full" bg="white/10" rounded="none" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box h={{ base: 2, sm: 2.5, md: 3 }} w={{ base: 8, sm: 10, md: 12 }} bg="bg-charcoal-outline" rounded="sm" font="mono" display="flex" alignItems="center" justifyContent="center" color="text-white" opacity={0.7}
|
<Box h="3" w="12" bg="graphite-black" rounded="none" border borderColor="border-gray/30" />
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
/>
|
|
||||||
{hoveredDriver === i && (
|
|
||||||
<Box
|
|
||||||
as={motion.div}
|
|
||||||
position="absolute"
|
|
||||||
inset="0"
|
|
||||||
pointerEvents="none"
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
|
||||||
background: `linear-gradient(90deg, ${teamColors[i-1]}10 0%, transparent 100%)`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box position="absolute" left="1/2" top="8" bottom="8" w="px"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="bg-gradient-to-b from-transparent via-charcoal-outline to-transparent backdrop-blur-sm"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
variants={rightColumnVariants}
|
variants={rightColumnVariants}
|
||||||
@@ -314,17 +232,10 @@ export function TeamCompetitionMockup() {
|
|||||||
animate="visible"
|
animate="visible"
|
||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
<Box h={{ base: 3, sm: 4, md: 5 }} w={{ base: 20, sm: 24, md: 32 }} bg="bg-white/10" rounded="sm" mb={{ base: 1.5, sm: 2, md: 3, lg: 4 }} display="flex" alignItems="center" justifyContent="center">
|
<Box h="6" w="32" bg="panel-gray" rounded="none" mb={4} display="flex" alignItems="center" justifyContent="center" border borderColor="border-gray/30">
|
||||||
<Text
|
<Text size="xs" color="text-gray-400" weight="bold" className="uppercase tracking-widest">CONSTRUCTORS</Text>
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
color="text-white"
|
|
||||||
weight="semibold"
|
|
||||||
>
|
|
||||||
Constructors
|
|
||||||
</Text>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Stack gap={{ base: 1, sm: 1.5, md: 2 }}>
|
<Stack gap={1}>
|
||||||
{[1, 2, 3, 4, 5].map((i) => (
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -336,58 +247,37 @@ export function TeamCompetitionMockup() {
|
|||||||
position="relative"
|
position="relative"
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={{ base: 1.5, sm: 2, md: 2.5, lg: 3 }}
|
gap={3}
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/20"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
p={{ base: 1, sm: 1.5, md: 2, lg: 2.5 }}
|
p={2}
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/20"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
onHoverStart={() => !shouldReduceMotion && setHoveredTeam(i)}
|
onHoverStart={() => !shouldReduceMotion && setHoveredTeam(i)}
|
||||||
onHoverEnd={() => setHoveredTeam(null)}
|
onHoverEnd={() => setHoveredTeam(null)}
|
||||||
whileHover={shouldReduceMotion ? {} : {
|
whileHover={shouldReduceMotion ? {} : {
|
||||||
scale: 1.02,
|
x: -4,
|
||||||
boxShadow: `0 0 20px ${teamColors[i-1]}40`,
|
borderColor: `${teamColors[i-1]}40`,
|
||||||
transition: { duration: 0.15 }
|
transition: { duration: 0.15 }
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
left="0"
|
right="0"
|
||||||
top="0"
|
top="0"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
w="0.5"
|
w="0.5"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1] }}
|
style={{ backgroundColor: teamColors[i-1] }}
|
||||||
/>
|
/>
|
||||||
<Box
|
|
||||||
h={{ base: 5, sm: 6, md: 7 }}
|
|
||||||
w={{ base: 5, sm: 6, md: 7 }}
|
|
||||||
rounded="sm"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
border
|
|
||||||
borderWidth="2px"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
|
||||||
fontSize: '12px',
|
|
||||||
borderColor: teamColors[i-1],
|
|
||||||
backgroundColor: `${teamColors[i-1]}20`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text>🏁</Text>
|
|
||||||
</Box>
|
|
||||||
<Box flexGrow={1} minWidth="0">
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Box h={{ base: 1.5, sm: 2, md: 2.5 }} w="full" bg="bg-white/10" rounded="sm" mb={{ base: 0.5, sm: 1, md: 1.5 }} />
|
<Box h="1.5" w="full" bg="white/10" rounded="none" mb={1.5} />
|
||||||
<Box position="relative" h={{ base: 0.5, sm: 1, md: 1.5 }} bg="bg-charcoal-outline" rounded="full" overflow="hidden">
|
<Box position="relative" h="1" bg="graphite-black" rounded="none" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
insetY="0"
|
insetY="0"
|
||||||
left="0"
|
left="0"
|
||||||
rounded="full"
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ backgroundColor: teamColors[i-1] }}
|
style={{ backgroundColor: teamColors[i-1] }}
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: `${100 - (i-1) * 15}%` }}
|
animate={{ width: `${100 - (i-1) * 15}%` }}
|
||||||
@@ -395,30 +285,6 @@ export function TeamCompetitionMockup() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
{i === 3 && (
|
|
||||||
<Box h={{ base: 3, sm: 3.5, md: 4 }} px={{ base: 0.5, sm: 1, md: 1.5 }} bg="bg-warning-amber/20" rounded="sm" display="flex" alignItems="center" justifyContent="center" color="text-warning-amber" weight="semibold" border borderColor="border-warning-amber/30">
|
|
||||||
<Text
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{ fontSize: '10px' }}
|
|
||||||
>
|
|
||||||
=
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{hoveredTeam === i && (
|
|
||||||
<Box
|
|
||||||
as={motion.div}
|
|
||||||
position="absolute"
|
|
||||||
inset="0"
|
|
||||||
pointerEvents="none"
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
style={{
|
|
||||||
background: `linear-gradient(90deg, ${teamColors[i-1]}10 0%, transparent 100%)`
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
|
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
|
||||||
import { CheckCircle2, LucideIcon } from 'lucide-react';
|
import { CheckCircle2, LucideIcon } from 'lucide-react';
|
||||||
@@ -43,16 +43,16 @@ export function WorkflowMockup({ steps }: WorkflowMockupProps) {
|
|||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth>
|
<Box position="relative" fullWidth>
|
||||||
<Surface variant="muted" rounded="2xl" border={true} padding={6}>
|
<Surface variant="muted" rounded="none" border={true} padding={6} bg="panel-gray/40">
|
||||||
<Stack direction="row" justify="between" gap={2}>
|
<Stack direction="row" justify="between" gap={2}>
|
||||||
{steps.map((step) => (
|
{steps.map((step) => (
|
||||||
<Stack key={step.id} align="center" center direction="col">
|
<Stack key={step.id} align="center" center direction="col">
|
||||||
<Box width="10" height="10" rounded="lg" bg="bg-iron-gray" border={true} borderColor="border-charcoal-outline" display="flex" center mb={2}>
|
<Box width="10" height="10" rounded="none" bg="graphite-black" border={true} borderColor="border-gray/50" display="flex" center mb={2}>
|
||||||
<Text color={step.color}>
|
<Text color={step.color}>
|
||||||
<Icon icon={step.icon} size={4} />
|
<Icon icon={step.icon} size={4} />
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Text size="xs" weight="medium" color="text-white">{step.title}</Text>
|
<Text size="xs" weight="bold" color="text-white" className="uppercase tracking-widest">{step.title}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -63,15 +63,15 @@ export function WorkflowMockup({ steps }: WorkflowMockupProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative" fullWidth>
|
<Box position="relative" fullWidth>
|
||||||
<Surface variant="muted" rounded="2xl" border={true} padding={6} overflow="hidden">
|
<Surface variant="muted" rounded="none" border={true} padding={6} overflow="hidden" bg="panel-gray/40">
|
||||||
{/* Connection Lines */}
|
{/* Connection Lines */}
|
||||||
<Box position="absolute" top="3.5rem" left="8%" right="8%" display={{ base: 'none', sm: 'block' }}>
|
<Box position="absolute" top="3.5rem" left="8%" right="8%" display={{ base: 'none', sm: 'block' }}>
|
||||||
<Box height="0.5" bg="bg-charcoal-outline" position="relative">
|
<Box height="0.5" bg="white/5" position="relative">
|
||||||
<Box
|
<Box
|
||||||
as={motion.div}
|
as={motion.div}
|
||||||
position="absolute"
|
position="absolute"
|
||||||
fullHeight
|
fullHeight
|
||||||
bg="bg-gradient-to-r from-primary-blue to-performance-green"
|
bg="primary-accent"
|
||||||
initial={{ width: '0%' }}
|
initial={{ width: '0%' }}
|
||||||
animate={{ width: `${(activeStep / (steps.length - 1)) * 100}%` }}
|
animate={{ width: `${(activeStep / (steps.length - 1)) * 100}%` }}
|
||||||
transition={{ duration: 0.5, ease: 'easeInOut' }}
|
transition={{ duration: 0.5, ease: 'easeInOut' }}
|
||||||
@@ -104,35 +104,39 @@ export function WorkflowMockup({ steps }: WorkflowMockupProps) {
|
|||||||
as={motion.div}
|
as={motion.div}
|
||||||
w={{ base: '10', sm: '12' }}
|
w={{ base: '10', sm: '12' }}
|
||||||
h={{ base: '10', sm: '12' }}
|
h={{ base: '10', sm: '12' }}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
border={true}
|
border={true}
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
mb={2}
|
mb={2}
|
||||||
transition
|
transition
|
||||||
bg={isActive ? 'bg-primary-blue/20' : isCompleted ? 'bg-performance-green/20' : 'bg-iron-gray'}
|
bg={isActive ? 'primary-accent/10' : isCompleted ? 'success-green/10' : 'graphite-black'}
|
||||||
borderColor={isActive ? 'border-primary-blue' : isCompleted ? 'border-performance-green/50' : 'border-charcoal-outline'}
|
borderColor={isActive ? 'primary-accent' : isCompleted ? 'success-green/50' : 'border-gray/50'}
|
||||||
shadow={isActive ? 'shadow-[0_0_15px_rgba(25,140,255,0.3)]' : 'none'}
|
|
||||||
animate={isActive && !shouldReduceMotion ? {
|
animate={isActive && !shouldReduceMotion ? {
|
||||||
scale: [1, 1.08, 1],
|
opacity: [0.7, 1, 0.7],
|
||||||
transition: { duration: 1, repeat: Infinity }
|
transition: { duration: 1.5, repeat: Infinity }
|
||||||
} : {}}
|
} : {}}
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
|
{isActive && (
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
|
)}
|
||||||
{isCompleted ? (
|
{isCompleted ? (
|
||||||
<Icon icon={CheckCircle2} size={5} color="text-performance-green" />
|
<Icon icon={CheckCircle2} size={5} color="text-success-green" />
|
||||||
) : (
|
) : (
|
||||||
<Text color={isActive ? step.color : 'text-gray-500'}>
|
<Text color={isActive ? 'text-primary-accent' : 'text-gray-600'}>
|
||||||
<Icon icon={StepIcon} size={5} />
|
<Icon icon={StepIcon} size={5} />
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
weight="medium"
|
weight="bold"
|
||||||
color={isActive ? 'text-white' : 'text-gray-400'}
|
color={isActive ? 'text-white' : 'text-gray-500'}
|
||||||
display={{ base: 'none', sm: 'block' }}
|
display={{ base: 'none', sm: 'block' }}
|
||||||
transition
|
transition
|
||||||
|
className="uppercase tracking-widest"
|
||||||
>
|
>
|
||||||
{step.title}
|
{step.title}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -153,15 +157,15 @@ export function WorkflowMockup({ steps }: WorkflowMockupProps) {
|
|||||||
mt={4}
|
mt={4}
|
||||||
pt={4}
|
pt={4}
|
||||||
borderTop={true}
|
borderTop={true}
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/30"
|
||||||
display={{ base: 'block', sm: 'none' }}
|
display={{ base: 'block', sm: 'none' }}
|
||||||
>
|
>
|
||||||
<Box textAlign="center">
|
<Box textAlign="center">
|
||||||
<Text size="xs" color="text-gray-400" block mb={1}>
|
<Text size="xs" color="text-gray-400" block mb={1} font="mono" weight="bold" className="uppercase tracking-widest">
|
||||||
Step {activeStep + 1}: {steps[activeStep]?.title || ''}
|
STEP {activeStep + 1}: {steps[activeStep]?.title || ''}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="xs" color="text-gray-500" block>
|
<Text size="xs" color="text-gray-600" block font="mono">
|
||||||
{steps[activeStep]?.description || ''}
|
{steps[activeStep]?.description.toUpperCase() || ''}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -44,40 +44,40 @@ const notificationIcons: Record<string, typeof Bell> = {
|
|||||||
|
|
||||||
const notificationColors: Record<string, { bg: string; border: string; text: string; glow: string }> = {
|
const notificationColors: Record<string, { bg: string; border: string; text: string; glow: string }> = {
|
||||||
protest_filed: {
|
protest_filed: {
|
||||||
bg: 'bg-red-500/10',
|
bg: 'bg-critical-red/10',
|
||||||
border: 'border-red-500/50',
|
border: 'border-critical-red/50',
|
||||||
text: 'text-red-400',
|
text: 'text-critical-red',
|
||||||
glow: 'shadow-[0_0_60px_rgba(239,68,68,0.3)]',
|
glow: 'shadow-[0_0_60px_rgba(227,92,92,0.3)]',
|
||||||
},
|
},
|
||||||
protest_defense_requested: {
|
protest_defense_requested: {
|
||||||
bg: 'bg-warning-amber/10',
|
bg: 'bg-warning-amber/10',
|
||||||
border: 'border-warning-amber/50',
|
border: 'border-warning-amber/50',
|
||||||
text: 'text-warning-amber',
|
text: 'text-warning-amber',
|
||||||
glow: 'shadow-[0_0_60px_rgba(245,158,11,0.3)]',
|
glow: 'shadow-[0_0_60px_rgba(255,197,86,0.3)]',
|
||||||
},
|
},
|
||||||
protest_vote_required: {
|
protest_vote_required: {
|
||||||
bg: 'bg-primary-blue/10',
|
bg: 'bg-primary-accent/10',
|
||||||
border: 'border-primary-blue/50',
|
border: 'border-primary-accent/50',
|
||||||
text: 'text-primary-blue',
|
text: 'text-primary-accent',
|
||||||
glow: 'shadow-[0_0_60px_rgba(25,140,255,0.3)]',
|
glow: 'shadow-[0_0_60px_rgba(25,140,255,0.3)]',
|
||||||
},
|
},
|
||||||
penalty_issued: {
|
penalty_issued: {
|
||||||
bg: 'bg-red-500/10',
|
bg: 'bg-critical-red/10',
|
||||||
border: 'border-red-500/50',
|
border: 'border-critical-red/50',
|
||||||
text: 'text-red-400',
|
text: 'text-critical-red',
|
||||||
glow: 'shadow-[0_0_60px_rgba(239,68,68,0.3)]',
|
glow: 'shadow-[0_0_60px_rgba(227,92,92,0.3)]',
|
||||||
},
|
},
|
||||||
race_performance_summary: {
|
race_performance_summary: {
|
||||||
bg: 'bg-gradient-to-br from-yellow-400/20 via-orange-500/20 to-red-500/20',
|
bg: 'bg-panel-gray',
|
||||||
border: 'border-yellow-400/60',
|
border: 'border-warning-amber/60',
|
||||||
text: 'text-yellow-400',
|
text: 'text-warning-amber',
|
||||||
glow: 'shadow-[0_0_80px_rgba(251,191,36,0.4)]',
|
glow: 'shadow-[0_0_80px_rgba(255,197,86,0.2)]',
|
||||||
},
|
},
|
||||||
race_final_results: {
|
race_final_results: {
|
||||||
bg: 'bg-gradient-to-br from-purple-500/20 via-pink-500/20 to-indigo-500/20',
|
bg: 'bg-panel-gray',
|
||||||
border: 'border-purple-400/60',
|
border: 'border-primary-accent/60',
|
||||||
text: 'text-purple-400',
|
text: 'text-primary-accent',
|
||||||
glow: 'shadow-[0_0_80px_rgba(168,85,247,0.4)]',
|
glow: 'shadow-[0_0_80px_rgba(25,140,255,0.2)]',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,10 +123,10 @@ export function ModalNotification({
|
|||||||
|
|
||||||
const NotificationIcon = notificationIcons[notification.type] || AlertCircle;
|
const NotificationIcon = notificationIcons[notification.type] || AlertCircle;
|
||||||
const colors = notificationColors[notification.type] || {
|
const colors = notificationColors[notification.type] || {
|
||||||
bg: 'bg-warning-amber/10',
|
bg: 'bg-panel-gray',
|
||||||
border: 'border-warning-amber/50',
|
border: 'border-border-gray',
|
||||||
text: 'text-warning-amber',
|
text: 'text-gray-400',
|
||||||
glow: 'shadow-[0_0_60px_rgba(245,158,11,0.3)]',
|
glow: 'shadow-card',
|
||||||
};
|
};
|
||||||
|
|
||||||
const data: Record<string, unknown> = notification.data ?? {};
|
const data: Record<string, unknown> = notification.data ?? {};
|
||||||
@@ -160,7 +160,6 @@ export function ModalNotification({
|
|||||||
|
|
||||||
// Special celebratory styling for race notifications
|
// Special celebratory styling for race notifications
|
||||||
const isRaceNotification = notification.type.startsWith('race_');
|
const isRaceNotification = notification.type.startsWith('race_');
|
||||||
const isPerformanceSummary = notification.type === 'race_performance_summary';
|
|
||||||
|
|
||||||
const provisionalRatingChange = getNumber(data.provisionalRatingChange) ?? 0;
|
const provisionalRatingChange = getNumber(data.provisionalRatingChange) ?? 0;
|
||||||
const finalRatingChange = getNumber(data.finalRatingChange) ?? 0;
|
const finalRatingChange = getNumber(data.finalRatingChange) ?? 0;
|
||||||
@@ -177,10 +176,8 @@ export function ModalNotification({
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
p={4}
|
p={4}
|
||||||
transition
|
transition
|
||||||
bg={isVisible ? 'bg-black/70' : 'bg-transparent'}
|
bg={isVisible ? 'bg-black/80' : 'bg-transparent'}
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className={isVisible ? 'backdrop-blur-sm' : ''}
|
className={isVisible ? 'backdrop-blur-sm' : ''}
|
||||||
hoverBg={isRaceNotification ? 'bg-gradient-to-br from-black/80 via-indigo-900/10 to-black/80' : undefined}
|
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
w="full"
|
w="full"
|
||||||
@@ -188,55 +185,45 @@ export function ModalNotification({
|
|||||||
transform
|
transform
|
||||||
transition
|
transition
|
||||||
opacity={isVisible ? 1 : 0}
|
opacity={isVisible ? 1 : 0}
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className={isVisible ? 'scale-100' : 'scale-95'}
|
className={isVisible ? 'scale-100' : 'scale-95'}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
rounded="2xl"
|
rounded="sm"
|
||||||
border
|
border
|
||||||
borderWidth="2px"
|
|
||||||
borderColor={colors.border}
|
borderColor={colors.border}
|
||||||
bg={colors.bg}
|
bg="panel-gray"
|
||||||
shadow={colors.glow}
|
shadow={colors.glow}
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
position={isRaceNotification ? 'relative' : undefined}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className="backdrop-blur-md"
|
|
||||||
>
|
>
|
||||||
{/* Header with pulse animation */}
|
{/* Header */}
|
||||||
<Box
|
<Box
|
||||||
px={6}
|
px={6}
|
||||||
py={4}
|
py={4}
|
||||||
bg={colors.bg}
|
bg="graphite-black"
|
||||||
borderBottom
|
borderBottom
|
||||||
borderColor={colors.border}
|
borderColor={colors.border}
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className={isRaceNotification ? 'bg-gradient-to-r from-transparent via-yellow-500/10 to-transparent' : ''}
|
|
||||||
>
|
>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between">
|
<Box display="flex" alignItems="center" justifyContent="between">
|
||||||
<Box display="flex" alignItems="center" gap={4}>
|
<Box display="flex" alignItems="center" gap={4}>
|
||||||
<Box
|
<Box
|
||||||
p={3}
|
p={2}
|
||||||
rounded="xl"
|
rounded="sm"
|
||||||
bg={colors.bg}
|
bg="panel-gray"
|
||||||
border
|
border
|
||||||
borderColor={colors.border}
|
borderColor={colors.border}
|
||||||
shadow={isRaceNotification ? 'lg' : undefined}
|
|
||||||
>
|
>
|
||||||
<Icon icon={NotificationIcon} size={6} color={colors.text} />
|
<Icon icon={NotificationIcon} size={5} color={colors.text} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
weight="semibold"
|
weight="bold"
|
||||||
transform="uppercase"
|
className="uppercase tracking-widest"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
color="text-gray-500"
|
||||||
className="tracking-wide"
|
|
||||||
color={isRaceNotification ? 'text-yellow-400' : 'text-gray-400'}
|
|
||||||
>
|
>
|
||||||
{isRaceNotification ? (isPerformanceSummary ? '🏁 Race Complete!' : '🏆 Championship Update') : 'Action Required'}
|
{isRaceNotification ? 'Race Update' : 'Action Required'}
|
||||||
</Text>
|
</Text>
|
||||||
<Heading level={2} fontSize="xl" weight="bold" color="text-white">
|
<Heading level={3} weight="bold" color="text-white">
|
||||||
{notification.title}
|
{notification.title}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -249,7 +236,7 @@ export function ModalNotification({
|
|||||||
onClick={() => onDismiss(notification)}
|
onClick={() => onDismiss(notification)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="md"
|
size="md"
|
||||||
color="text-gray-400"
|
color="text-gray-500"
|
||||||
title="Dismiss notification"
|
title="Dismiss notification"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -257,17 +244,11 @@ export function ModalNotification({
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Body */}
|
{/* Body */}
|
||||||
<Box
|
<Box px={6} py={6}>
|
||||||
px={6}
|
|
||||||
py={5}
|
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className={isRaceNotification ? 'bg-gradient-to-b from-transparent to-yellow-500/5' : ''}
|
|
||||||
>
|
|
||||||
<Text
|
<Text
|
||||||
leading="relaxed"
|
leading="relaxed"
|
||||||
size={isRaceNotification ? 'lg' : 'base'}
|
size="base"
|
||||||
weight={isRaceNotification ? 'medium' : 'normal'}
|
color="text-gray-300"
|
||||||
color={isRaceNotification ? 'text-white' : 'text-gray-300'}
|
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
{notification.message}
|
{notification.message}
|
||||||
@@ -275,16 +256,16 @@ export function ModalNotification({
|
|||||||
|
|
||||||
{/* Race performance stats */}
|
{/* Race performance stats */}
|
||||||
{isRaceNotification && (
|
{isRaceNotification && (
|
||||||
<Box display="grid" gridCols={2} gap={3} mt={4}>
|
<Box display="grid" gridCols={2} gap={4} mt={6}>
|
||||||
<Box bg="bg-black/20" rounded="lg" p={3} border borderColor="border-yellow-400/20">
|
<Box bg="graphite-black" rounded="sm" p={4} border borderColor="border-border-gray">
|
||||||
<Text size="xs" color="text-yellow-300" weight="medium" block mb={1}>POSITION</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" block mb={1} className="uppercase tracking-widest">POSITION</Text>
|
||||||
<Text size="2xl" weight="bold" color="text-white" block>
|
<Text size="2xl" weight="bold" color="text-white" block>
|
||||||
{notification.data?.position === 'DNF' ? 'DNF' : `P${notification.data?.position || '?'}`}
|
{notification.data?.position === 'DNF' ? 'DNF' : `P${notification.data?.position || '?'}`}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box bg="bg-black/20" rounded="lg" p={3} border borderColor="border-yellow-400/20">
|
<Box bg="graphite-black" rounded="sm" p={4} border borderColor="border-border-gray">
|
||||||
<Text size="xs" color="text-yellow-300" weight="medium" block mb={1}>RATING CHANGE</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" block mb={1} className="uppercase tracking-widest">RATING</Text>
|
||||||
<Text size="2xl" weight="bold" color={ratingChange >= 0 ? 'text-green-400' : 'text-red-400'} block>
|
<Text size="2xl" weight="bold" color={ratingChange >= 0 ? 'text-success-green' : 'text-critical-red'} block>
|
||||||
{ratingChange >= 0 ? '+' : ''}
|
{ratingChange >= 0 ? '+' : ''}
|
||||||
{ratingChange}
|
{ratingChange}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -294,12 +275,12 @@ export function ModalNotification({
|
|||||||
|
|
||||||
{/* Deadline warning */}
|
{/* Deadline warning */}
|
||||||
{hasDeadline && !isRaceNotification && (
|
{hasDeadline && !isRaceNotification && (
|
||||||
<Box mt={4} display="flex" alignItems="center" gap={2} px={4} py={3} rounded="lg" bg="bg-warning-amber/10" border borderColor="border-warning-amber/30">
|
<Box mt={6} display="flex" alignItems="center" gap={3} px={4} py={3} rounded="sm" bg="warning-amber/5" border borderColor="border-warning-amber/20">
|
||||||
<Icon icon={Clock} size={5} color="text-warning-amber" />
|
<Icon icon={Clock} size={5} color="text-warning-amber" />
|
||||||
<Box>
|
<Box>
|
||||||
<Text size="sm" weight="medium" color="text-warning-amber" block>Response Required</Text>
|
<Text size="sm" weight="bold" color="text-warning-amber" block className="uppercase tracking-wider">Response Required</Text>
|
||||||
<Text size="xs" color="text-gray-400" block>
|
<Text size="xs" color="text-gray-500" block mt={0.5}>
|
||||||
Please respond by {deadline ? deadline.toLocaleDateString() : ''} at {deadline ? deadline.toLocaleTimeString() : ''}
|
By {deadline ? deadline.toLocaleDateString() : ''} {deadline ? deadline.toLocaleTimeString() : ''}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -307,9 +288,9 @@ export function ModalNotification({
|
|||||||
|
|
||||||
{/* Additional context from data */}
|
{/* Additional context from data */}
|
||||||
{protestId && (
|
{protestId && (
|
||||||
<Box mt={4} p={3} rounded="lg" bg="bg-iron-gray/50" border borderColor="border-charcoal-outline">
|
<Box mt={6} p={3} rounded="sm" bg="graphite-black" border borderColor="border-border-gray">
|
||||||
<Text size="xs" color="text-gray-500" block mb={1}>Related Protest</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" block mb={1} className="uppercase tracking-widest text-[10px]">PROTEST ID</Text>
|
||||||
<Text size="sm" color="text-gray-300" font="mono" block>
|
<Text size="xs" color="text-gray-400" font="mono" block>
|
||||||
{protestId}
|
{protestId}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -321,10 +302,8 @@ export function ModalNotification({
|
|||||||
px={6}
|
px={6}
|
||||||
py={4}
|
py={4}
|
||||||
borderTop
|
borderTop
|
||||||
borderColor={isRaceNotification ? (isPerformanceSummary ? 'border-yellow-400/60' : 'border-purple-400/60') : 'border-charcoal-outline'}
|
borderColor="border-border-gray"
|
||||||
bg={isRaceNotification ? undefined : 'bg-iron-gray/30'}
|
bg="graphite-black"
|
||||||
// eslint-disable-next-line gridpilot-rules/component-classification
|
|
||||||
className={isRaceNotification ? (isPerformanceSummary ? 'bg-gradient-to-r from-yellow-500/10 to-orange-500/10' : 'bg-gradient-to-r from-purple-500/10 to-pink-500/10') : ''}
|
|
||||||
>
|
>
|
||||||
<Box display="flex" flexWrap="wrap" gap={3} justifyContent="end">
|
<Box display="flex" flexWrap="wrap" gap={3} justifyContent="end">
|
||||||
{notification.actions && notification.actions.length > 0 ? (
|
{notification.actions && notification.actions.length > 0 ? (
|
||||||
@@ -333,9 +312,7 @@ export function ModalNotification({
|
|||||||
key={index}
|
key={index}
|
||||||
variant={action.type === 'primary' ? 'primary' : 'secondary'}
|
variant={action.type === 'primary' ? 'primary' : 'secondary'}
|
||||||
onClick={() => handleAction(action)}
|
onClick={() => handleAction(action)}
|
||||||
bg={action.type === 'danger' ? 'bg-red-500' : undefined}
|
className={action.type === 'danger' ? 'bg-critical-red hover:bg-critical-red/90' : ''}
|
||||||
color={action.type === 'danger' ? 'text-white' : undefined}
|
|
||||||
shadow={isRaceNotification ? 'lg' : undefined}
|
|
||||||
>
|
>
|
||||||
{action.label}
|
{action.label}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -346,22 +323,14 @@ export function ModalNotification({
|
|||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => (onDismiss ? onDismiss(notification) : onAction(notification, 'dismiss'))}
|
onClick={() => (onDismiss ? onDismiss(notification) : onAction(notification, 'dismiss'))}
|
||||||
shadow="lg"
|
|
||||||
>
|
>
|
||||||
Dismiss
|
Dismiss
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="primary"
|
||||||
onClick={() => handleAction({ id: 'share', label: 'Share Achievement', type: 'secondary' })}
|
|
||||||
shadow="lg"
|
|
||||||
>
|
|
||||||
🎉 Share
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={isPerformanceSummary ? 'race-performance' : 'race-final'}
|
|
||||||
onClick={handlePrimaryAction}
|
onClick={handlePrimaryAction}
|
||||||
>
|
>
|
||||||
{isPerformanceSummary ? '🏁 View Race Results' : '🏆 View Standings'}
|
{notification.type === 'race_performance_summary' ? 'View Results' : 'View Standings'}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -375,9 +344,9 @@ export function ModalNotification({
|
|||||||
|
|
||||||
{/* Cannot dismiss warning */}
|
{/* Cannot dismiss warning */}
|
||||||
{notification.requiresResponse && !isRaceNotification && (
|
{notification.requiresResponse && !isRaceNotification && (
|
||||||
<Box px={6} py={2} bg="bg-red-500/10" borderTop borderColor="border-red-500/20">
|
<Box px={6} py={2} bg="critical-red/5" borderTop borderColor="border-critical-red/10">
|
||||||
<Text size="xs" color="text-red-400" textAlign="center" block>
|
<Text size="xs" color="text-critical-red" textAlign="center" block weight="medium">
|
||||||
⚠️ This notification requires your action and cannot be dismissed
|
This action is required to continue
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -10,41 +10,39 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
backgroundImage: {
|
||||||
|
'radial-gradient': 'radial-gradient(var(--tw-gradient-stops))',
|
||||||
|
},
|
||||||
colors: {
|
colors: {
|
||||||
'deep-graphite': '#0E0F11',
|
'graphite-black': '#0C0D0F',
|
||||||
'iron-gray': '#181B1F',
|
'panel-gray': '#141619',
|
||||||
'charcoal-outline': '#22262A',
|
'border-gray': '#23272B',
|
||||||
|
'primary-accent': '#198CFF',
|
||||||
|
'telemetry-aqua': '#4ED4E0',
|
||||||
|
'warning-amber': '#FFBE4D',
|
||||||
|
'success-green': '#6FE37A',
|
||||||
|
'critical-red': '#E35C5C',
|
||||||
|
// Legacy mappings for compatibility during transition
|
||||||
|
'deep-graphite': '#0C0D0F',
|
||||||
|
'iron-gray': '#141619',
|
||||||
|
'charcoal-outline': '#23272B',
|
||||||
'primary-blue': '#198CFF',
|
'primary-blue': '#198CFF',
|
||||||
'performance-green': '#6FE37A',
|
'performance-green': '#6FE37A',
|
||||||
'warning-amber': '#FFC556',
|
'racing-red': '#E35C5C',
|
||||||
'neon-aqua': '#43C9E6',
|
|
||||||
'racing-red': '#E31E24',
|
|
||||||
'carbon-black': '#0A0A0A',
|
|
||||||
'metallic-silver': '#C0C0C8',
|
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||||
},
|
},
|
||||||
boxShadow: {
|
boxShadow: {
|
||||||
'glow': '0 0 20px rgba(25, 140, 255, 0.3)',
|
'card': '0 4px 12px rgba(0, 0, 0, 0.2)',
|
||||||
'glow-strong': '0 0 28px rgba(25, 140, 255, 0.5)',
|
},
|
||||||
'card': '0 8px 24px rgba(0, 0, 0, 0.12)',
|
transitionDuration: {
|
||||||
'racing': '0 4px 16px rgba(227, 30, 36, 0.15)',
|
'smooth': '150ms',
|
||||||
},
|
},
|
||||||
transitionTimingFunction: {
|
transitionTimingFunction: {
|
||||||
'spring': 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
'smooth': 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
'speed': 'cubic-bezier(0.22, 1, 0.36, 1)',
|
|
||||||
},
|
|
||||||
animation: {
|
|
||||||
'speed-pulse': 'speed-pulse 2s ease-in-out infinite',
|
|
||||||
},
|
|
||||||
keyframes: {
|
|
||||||
'speed-pulse': {
|
|
||||||
'0%, 100%': { opacity: '0.5' },
|
|
||||||
'50%': { opacity: '1' },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,27 +4,20 @@ import { AlternatingSection } from '@/components/landing/AlternatingSection';
|
|||||||
import { FAQ } from '@/components/landing/FAQ';
|
import { FAQ } from '@/components/landing/FAQ';
|
||||||
import { FeatureGrid } from '@/components/landing/FeatureGrid';
|
import { FeatureGrid } from '@/components/landing/FeatureGrid';
|
||||||
import { LandingHero } from '@/components/landing/LandingHero';
|
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 { CareerProgressionMockup } from '@/components/mockups/CareerProgressionMockup';
|
||||||
import { CompanionAutomationMockup } from '@/components/mockups/CompanionAutomationMockup';
|
import { CompanionAutomationMockup } from '@/components/mockups/CompanionAutomationMockup';
|
||||||
import { RaceHistoryMockup } from '@/components/mockups/RaceHistoryMockup';
|
import { RaceHistoryMockup } from '@/components/mockups/RaceHistoryMockup';
|
||||||
import { SimPlatformMockup } from '@/components/mockups/SimPlatformMockup';
|
import { SimPlatformMockup } from '@/components/mockups/SimPlatformMockup';
|
||||||
import { ModeGuard } from '@/components/shared/ModeGuard';
|
import { ModeGuard } from '@/components/shared/ModeGuard';
|
||||||
import { routes } from '@/lib/routing/RouteConfig';
|
|
||||||
import { getMediaUrl } from '@/lib/utilities/media';
|
|
||||||
import { Box } from '@/ui/Box';
|
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 { DiscordCTA } from '@/ui/DiscordCTA';
|
||||||
import { Footer } from '@/ui/Footer';
|
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 { Stack } from '@/ui/Stack';
|
||||||
import { Surface } from '@/ui/Surface';
|
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
|
import { TelemetryLine } from '@/ui/TelemetryLine';
|
||||||
|
import { Glow } from '@/ui/Glow';
|
||||||
|
|
||||||
export interface HomeViewData {
|
export interface HomeViewData {
|
||||||
isAlpha: boolean;
|
isAlpha: boolean;
|
||||||
@@ -53,220 +46,132 @@ interface HomeTemplateProps {
|
|||||||
|
|
||||||
export function HomeTemplate({ viewData }: HomeTemplateProps) {
|
export function HomeTemplate({ viewData }: HomeTemplateProps) {
|
||||||
return (
|
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 />
|
<LandingHero />
|
||||||
|
|
||||||
|
<TelemetryLine color="primary" height="1px" opacity={0.3} />
|
||||||
|
|
||||||
{/* Section 1: A Persistent Identity */}
|
{/* Section 1: A Persistent Identity */}
|
||||||
<AlternatingSection
|
<Box position="relative" bg="graphite-black">
|
||||||
heading="A Persistent Identity"
|
<Glow color="aqua" size="lg" position="bottom-left" opacity={0.03} />
|
||||||
backgroundVideo="/gameplay.mp4"
|
<AlternatingSection
|
||||||
description={
|
heading="A Persistent Identity"
|
||||||
<Stack gap={4}>
|
backgroundVideo="/gameplay.mp4"
|
||||||
<Text>
|
description={
|
||||||
Your races, your seasons, your progress — finally in one place.
|
<Stack gap={8}>
|
||||||
</Text>
|
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||||
<Stack gap={3}>
|
Your races, your seasons, your progress — finally in one place.
|
||||||
<FeatureItem text="Lifetime stats and season history across all your leagues" />
|
</Text>
|
||||||
<FeatureItem text="Track your performance, consistency, and team contributions" />
|
<Box display="grid" gridCols={1} gap={3}>
|
||||||
<FeatureItem text="Your own rating that reflects real league competition" />
|
<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>
|
</Stack>
|
||||||
<Text>
|
}
|
||||||
iRacing gives you physics. GridPilot gives you a career.
|
mockup={<CareerProgressionMockup />}
|
||||||
</Text>
|
layout="text-left"
|
||||||
</Stack>
|
/>
|
||||||
}
|
</Box>
|
||||||
mockup={<CareerProgressionMockup />}
|
|
||||||
layout="text-left"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureGrid />
|
<FeatureGrid />
|
||||||
|
|
||||||
{/* Section 2: Results That Actually Stay */}
|
{/* Section 2: Results That Actually Stay */}
|
||||||
<AlternatingSection
|
<Box position="relative" bg="graphite-black">
|
||||||
heading="Results That Actually Stay"
|
<Glow color="primary" size="lg" position="top-right" opacity={0.03} />
|
||||||
backgroundImage="/images/ff1600.jpeg"
|
<AlternatingSection
|
||||||
description={
|
heading="Results That Actually Stay"
|
||||||
<Stack gap={4}>
|
backgroundImage="/images/ff1600.jpeg"
|
||||||
<Text size="sm">
|
description={
|
||||||
Every race you run stays with you.
|
<Stack gap={8}>
|
||||||
</Text>
|
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||||
<Stack gap={3}>
|
Every race you run stays with you.
|
||||||
<ResultItem text="Your stats, your team, your story — all connected" color="#ef4444" />
|
</Text>
|
||||||
<ResultItem text="One race result updates your profile, team points, rating, and season history" color="#ef4444" />
|
<Box display="grid" gridCols={1} gap={3}>
|
||||||
<ResultItem text="No more fragmented data across spreadsheets and forums" color="#ef4444" />
|
<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>
|
</Stack>
|
||||||
<Text size="sm">
|
}
|
||||||
Your racing career, finally in one place.
|
mockup={<RaceHistoryMockup />}
|
||||||
</Text>
|
layout="text-right"
|
||||||
</Stack>
|
/>
|
||||||
}
|
</Box>
|
||||||
mockup={<RaceHistoryMockup />}
|
|
||||||
layout="text-right"
|
<TelemetryLine color="aqua" height="1px" opacity={0.2} />
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Section 3: Automatic Session Creation */}
|
{/* Section 3: Automatic Session Creation */}
|
||||||
<AlternatingSection
|
<Box position="relative" bg="graphite-black">
|
||||||
heading="Automatic Session Creation"
|
<Glow color="amber" size="lg" position="bottom-right" opacity={0.02} />
|
||||||
description={
|
<AlternatingSection
|
||||||
<Stack gap={4}>
|
heading="Automatic Session Creation"
|
||||||
<Text size="sm">
|
description={
|
||||||
Setting up league races used to mean clicking through iRacing's wizard 20 times.
|
<Stack gap={8}>
|
||||||
</Text>
|
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||||
<Stack gap={3}>
|
Setting up league races used to mean clicking through iRacing's wizard 20 times.
|
||||||
<StepItem step={1} text="Our companion app syncs with your league schedule" />
|
</Text>
|
||||||
<StepItem step={2} text="When it's race time, it creates the iRacing session automatically" />
|
<Box display="grid" gridCols={1} gap={3}>
|
||||||
<StepItem step={3} text="No clicking through wizards. No manual setup" />
|
<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>
|
</Stack>
|
||||||
<Text size="sm">
|
}
|
||||||
Automation instead of repetition.
|
mockup={<CompanionAutomationMockup />}
|
||||||
</Text>
|
layout="text-left"
|
||||||
</Stack>
|
/>
|
||||||
}
|
</Box>
|
||||||
mockup={<CompanionAutomationMockup />}
|
|
||||||
layout="text-left"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Section 4: Game-Agnostic Platform */}
|
{/* Section 4: Game-Agnostic Platform */}
|
||||||
<AlternatingSection
|
<Box position="relative" bg="graphite-black">
|
||||||
heading="Built for iRacing. Ready for the future."
|
<Glow color="primary" size="xl" position="center" opacity={0.03} />
|
||||||
backgroundImage="/images/lmp3.jpeg"
|
<AlternatingSection
|
||||||
description={
|
heading="Built for iRacing. Ready for the future."
|
||||||
<Stack gap={4}>
|
backgroundImage="/images/lmp3.jpeg"
|
||||||
<Text size="sm">
|
description={
|
||||||
Right now, we're focused on making iRacing league racing better.
|
<Stack gap={8}>
|
||||||
</Text>
|
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||||
<Text size="sm">
|
Right now, we're focused on making iRacing league racing better.
|
||||||
But sims come and go. Your leagues, your teams, your rating — those stay.
|
</Text>
|
||||||
</Text>
|
<Text color="text-gray-400" leading="relaxed">
|
||||||
<Text size="sm">
|
But sims come and go. Your leagues, your teams, your rating — those stay.
|
||||||
GridPilot is built to outlast any single platform.
|
</Text>
|
||||||
</Text>
|
<Box borderLeft borderStyle="solid" borderColor="border-gray" pl={4} py={1} bg="white/5">
|
||||||
<Text size="sm">
|
<Text color="text-gray-500" font="mono" size="xs" uppercase letterSpacing="widest" leading="relaxed">
|
||||||
When the next sim arrives, your competitive identity moves with you.
|
GridPilot is built to outlast any single platform. When the next sim arrives, your competitive identity moves with you.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Box>
|
||||||
}
|
</Stack>
|
||||||
mockup={<SimPlatformMockup />}
|
}
|
||||||
layout="text-right"
|
mockup={<SimPlatformMockup />}
|
||||||
/>
|
layout="text-right"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{/* Alpha-only discovery section */}
|
{/* Alpha-only discovery section */}
|
||||||
<ModeGuard feature="alpha_discovery">
|
<ModeGuard feature="alpha_discovery">
|
||||||
<Container size="lg" py={12}>
|
<Box bg="panel-gray/20" py={20} borderTop borderBottom borderColor="border-gray/50" position="relative">
|
||||||
<Stack gap={8}>
|
<Glow color="aqua" size="xl" position="center" opacity={0.02} />
|
||||||
<Box>
|
<DiscoverySection viewData={viewData} />
|
||||||
<Heading level={2}>Discover the grid</Heading>
|
</Box>
|
||||||
<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>
|
|
||||||
</ModeGuard>
|
</ModeGuard>
|
||||||
|
|
||||||
<DiscordCTA />
|
<DiscordCTA />
|
||||||
|
|||||||
@@ -16,21 +16,21 @@ interface BadgeProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Badge({ children, className = '', variant = 'default', size = 'sm', icon, style, bg, color, borderColor }: BadgeProps) {
|
export function Badge({ children, className = '', variant = 'default', size = 'sm', icon, style, bg, color, borderColor }: BadgeProps) {
|
||||||
const baseClasses = 'flex items-center gap-1.5 rounded-full border font-medium';
|
const baseClasses = 'flex items-center gap-1.5 rounded-none border font-bold uppercase tracking-widest';
|
||||||
|
|
||||||
const sizeClasses = {
|
const sizeClasses = {
|
||||||
xs: 'px-1.5 py-0.5 text-[10px]',
|
xs: 'px-1.5 py-0.5 text-[9px]',
|
||||||
sm: 'px-2.5 py-1 text-xs',
|
sm: 'px-2 py-0.5 text-[10px]',
|
||||||
md: 'px-3 py-1.5 text-sm'
|
md: 'px-3 py-1 text-xs'
|
||||||
};
|
};
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
default: 'bg-gray-500/10 border-gray-500/30 text-gray-400',
|
default: 'bg-gray-500/10 border-gray-500/30 text-gray-400',
|
||||||
primary: 'bg-primary-blue/10 border-primary-blue/30 text-primary-blue',
|
primary: 'bg-primary-accent/10 border-primary-accent/30 text-primary-accent',
|
||||||
success: 'bg-performance-green/10 border-performance-green/30 text-performance-green',
|
success: 'bg-success-green/10 border-success-green/30 text-success-green',
|
||||||
warning: 'bg-warning-amber/10 border-warning-amber/30 text-warning-amber',
|
warning: 'bg-warning-amber/10 border-warning-amber/30 text-warning-amber',
|
||||||
danger: 'bg-red-600/10 border-red-600/30 text-red-500',
|
danger: 'bg-critical-red/10 border-critical-red/30 text-critical-red',
|
||||||
info: 'bg-neon-aqua/10 border-neon-aqua/30 text-neon-aqua'
|
info: 'bg-telemetry-aqua/10 border-telemetry-aqua/30 text-telemetry-aqua'
|
||||||
};
|
};
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ interface ButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'as'
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
className?: string;
|
className?: string;
|
||||||
variant?: 'primary' | 'secondary' | 'danger' | 'ghost' | 'race-performance' | 'race-final' | 'discord';
|
variant?: 'primary' | 'secondary' | 'danger' | 'ghost' | 'race-final' | 'discord';
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: 'sm' | 'md' | 'lg';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
type?: 'button' | 'submit' | 'reset';
|
type?: 'button' | 'submit' | 'reset';
|
||||||
@@ -34,25 +34,24 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
|||||||
rel,
|
rel,
|
||||||
...props
|
...props
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
const baseClasses = 'inline-flex items-center rounded-lg transition-all duration-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 hover:scale-[1.02] active:scale-95';
|
const baseClasses = 'inline-flex items-center justify-center rounded-none transition-all duration-150 ease-smooth focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 active:opacity-80 uppercase tracking-widest font-bold';
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
primary: 'bg-primary-blue text-white hover:bg-primary-blue/80 focus-visible:outline-primary-blue shadow-[0_0_15px_rgba(25,140,255,0.4)]',
|
primary: 'bg-primary-accent text-white hover:bg-primary-accent/90 focus-visible:outline-primary-accent shadow-[0_0_15px_rgba(25,140,255,0.3)] hover:shadow-[0_0_25px_rgba(25,140,255,0.5)]',
|
||||||
secondary: 'bg-iron-gray text-white border border-charcoal-outline hover:bg-iron-gray/80 focus-visible:outline-primary-blue',
|
secondary: 'bg-panel-gray text-white border border-border-gray hover:bg-border-gray/50 focus-visible:outline-primary-accent',
|
||||||
danger: 'bg-red-600 text-white hover:bg-red-700 focus-visible:outline-red-600',
|
danger: 'bg-critical-red text-white hover:bg-critical-red/90 focus-visible:outline-critical-red',
|
||||||
ghost: 'bg-transparent text-gray-400 hover:bg-gray-800 focus-visible:outline-gray-400',
|
ghost: 'bg-transparent text-gray-400 hover:text-white hover:bg-white/5 focus-visible:outline-gray-400',
|
||||||
'race-performance': 'bg-gradient-to-r from-yellow-400 to-orange-500 text-white shadow-[0_0_15px_rgba(251,191,36,0.4)] hover:from-yellow-500 hover:to-orange-600 focus-visible:outline-yellow-400',
|
'race-final': 'bg-success-green text-graphite-black hover:bg-success-green/90 focus-visible:outline-success-green',
|
||||||
'race-final': 'bg-gradient-to-r from-purple-400 to-pink-500 text-white shadow-[0_0_15px_rgba(168,85,247,0.4)] hover:from-purple-500 hover:to-pink-600 focus-visible:outline-purple-400',
|
discord: 'bg-[#5865F2] text-white hover:bg-[#4752C4] focus-visible:outline-[#5865F2]',
|
||||||
discord: 'bg-[#5865F2] text-white hover:bg-[#4752C4] shadow-[0_0_20px_rgba(88,101,242,0.3)] hover:shadow-[0_0_30px_rgba(88,101,242,0.6)] focus-visible:outline-[#5865F2]'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sizeClasses = {
|
const sizeClasses = {
|
||||||
sm: 'min-h-[36px] px-3 py-1.5 text-xs',
|
sm: 'min-h-[32px] px-3 py-1 text-xs font-medium',
|
||||||
md: 'min-h-[44px] px-4 py-2 text-sm',
|
md: 'min-h-[40px] px-4 py-2 text-sm font-medium',
|
||||||
lg: 'min-h-[52px] px-6 py-3 text-base'
|
lg: 'min-h-[48px] px-6 py-3 text-base font-medium'
|
||||||
};
|
};
|
||||||
|
|
||||||
const disabledClasses = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer';
|
const disabledClasses = disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer';
|
||||||
const widthClasses = fullWidth ? 'w-full' : '';
|
const widthClasses = fullWidth ? 'w-full' : '';
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface CardProps extends Omit<BoxProps<'div'>, 'children' | 'className' | 'on
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
variant?: 'default' | 'highlight';
|
variant?: 'default' | 'outline' | 'ghost';
|
||||||
p?: Spacing | ResponsiveSpacing;
|
p?: Spacing | ResponsiveSpacing;
|
||||||
px?: Spacing | ResponsiveSpacing;
|
px?: Spacing | ResponsiveSpacing;
|
||||||
py?: Spacing | ResponsiveSpacing;
|
py?: Spacing | ResponsiveSpacing;
|
||||||
@@ -30,17 +30,18 @@ export function Card({
|
|||||||
variant = 'default',
|
variant = 'default',
|
||||||
...props
|
...props
|
||||||
}: CardProps) {
|
}: CardProps) {
|
||||||
const baseClasses = 'rounded-lg shadow-card border duration-200';
|
const baseClasses = 'rounded-none transition-all duration-150 ease-smooth';
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
default: 'bg-iron-gray border-charcoal-outline',
|
default: 'bg-panel-gray border border-border-gray shadow-card',
|
||||||
highlight: 'bg-gradient-to-r from-blue-900/20 to-blue-700/10 border-blue-500/30'
|
outline: 'bg-transparent border border-border-gray',
|
||||||
|
ghost: 'bg-transparent border-none'
|
||||||
};
|
};
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
baseClasses,
|
baseClasses,
|
||||||
variantClasses[variant],
|
variantClasses[variant],
|
||||||
onClick ? 'cursor-pointer hover:scale-[1.02]' : '',
|
onClick ? 'cursor-pointer hover:bg-border-gray/30' : '',
|
||||||
className
|
className
|
||||||
].filter(Boolean).join(' ');
|
].filter(Boolean).join(' ');
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ export function Card({
|
|||||||
<Box
|
<Box
|
||||||
className={classes}
|
className={classes}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
p={hasPadding ? undefined : 6}
|
p={hasPadding ? undefined : 4}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ export function DecorativeBlur({
|
|||||||
opacity = 10
|
opacity = 10
|
||||||
}: DecorativeBlurProps) {
|
}: DecorativeBlurProps) {
|
||||||
const colorClasses = {
|
const colorClasses = {
|
||||||
blue: 'bg-primary-blue',
|
blue: 'bg-primary-accent',
|
||||||
green: 'bg-performance-green',
|
green: 'bg-success-green',
|
||||||
purple: 'bg-purple-600',
|
purple: 'bg-purple-600',
|
||||||
yellow: 'bg-yellow-400',
|
yellow: 'bg-warning-amber',
|
||||||
red: 'bg-racing-red'
|
red: 'bg-critical-red'
|
||||||
};
|
};
|
||||||
|
|
||||||
const sizeClasses = {
|
const sizeClasses = {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Button } from '@/ui/Button';
|
import { Button } from '@/ui/Button';
|
||||||
|
import { Container } from '@/ui/Container';
|
||||||
|
import { Heading } from '@/ui/Heading';
|
||||||
|
import { Glow } from '@/ui/Glow';
|
||||||
import { Icon } from '@/ui/Icon';
|
import { Icon } from '@/ui/Icon';
|
||||||
import { DiscordIcon } from '@/ui/icons/DiscordIcon';
|
import { DiscordIcon } from '@/ui/icons/DiscordIcon';
|
||||||
import { Stack } from '@/ui/Stack';
|
import { Stack } from '@/ui/Stack';
|
||||||
@@ -14,21 +17,25 @@ export function DiscordCTA() {
|
|||||||
const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#';
|
const discordUrl = process.env.NEXT_PUBLIC_DISCORD_URL || '#';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Surface
|
<Box
|
||||||
as="section"
|
as="section"
|
||||||
variant="discord"
|
bg="graphite-black"
|
||||||
padding={4}
|
|
||||||
position="relative"
|
position="relative"
|
||||||
py={{ base: 4, md: 12, lg: 16 }}
|
py={{ base: 20, md: 32 }}
|
||||||
|
borderBottom
|
||||||
|
borderColor="border-gray/50"
|
||||||
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<Box maxWidth="896px" mx="auto" px={2}>
|
<Glow color="primary" size="xl" position="center" opacity={0.05} />
|
||||||
|
|
||||||
|
<Container size="lg" position="relative" zIndex={10}>
|
||||||
<Surface
|
<Surface
|
||||||
variant="discord-inner"
|
variant="default"
|
||||||
padding={3}
|
padding={12}
|
||||||
border
|
border
|
||||||
rounded="xl"
|
rounded="none"
|
||||||
position="relative"
|
position="relative"
|
||||||
shadow="discord"
|
className="overflow-hidden bg-panel-gray/40"
|
||||||
>
|
>
|
||||||
{/* Discord brand accent */}
|
{/* Discord brand accent */}
|
||||||
<Box
|
<Box
|
||||||
@@ -37,120 +44,107 @@ export function DiscordCTA() {
|
|||||||
left={0}
|
left={0}
|
||||||
right={0}
|
right={0}
|
||||||
height="1"
|
height="1"
|
||||||
backgroundColor="[#5865F2]"
|
bg="primary-accent"
|
||||||
opacity={0.6}
|
|
||||||
className="bg-gradient-to-r from-transparent via-[#5865F2]/60 to-transparent"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack align="center" gap={6} center>
|
<Stack align="center" gap={12} center>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Stack align="center" gap={4}>
|
<Stack align="center" gap={6}>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
center
|
center
|
||||||
rounded="full"
|
rounded="none"
|
||||||
w={{ base: "10", md: "14", lg: "18" }}
|
w={{ base: "16", md: "20" }}
|
||||||
h={{ base: "10", md: "14", lg: "18" }}
|
h={{ base: "16", md: "20" }}
|
||||||
backgroundColor="[#5865F2]"
|
bg="primary-accent/10"
|
||||||
opacity={0.2}
|
|
||||||
border
|
border
|
||||||
borderColor="[#5865F2]"
|
borderColor="primary-accent/30"
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
<DiscordIcon color="text-[#5865F2]" size={32} />
|
<DiscordIcon color="text-primary-accent" size={40} />
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent" />
|
||||||
|
<Box position="absolute" bottom="-1px" right="-1px" w="2" h="2" borderBottom borderRight borderColor="primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Stack gap={2}>
|
<Stack gap={4} align="center">
|
||||||
<Text as="h2" size="2xl" weight="semibold" color="text-white">
|
<Heading level={2} weight="bold" color="text-white" fontSize={{ base: '2xl', md: '4xl' }} className="tracking-tight">
|
||||||
Join us on Discord
|
Join the Grid on Discord
|
||||||
</Text>
|
</Heading>
|
||||||
<Box
|
<Box
|
||||||
mx="auto"
|
w="16"
|
||||||
rounded="full"
|
h="1"
|
||||||
w={{ base: "16", md: "24", lg: "32" }}
|
bg="primary-accent"
|
||||||
h={{ base: "0.5", md: "1" }}
|
|
||||||
backgroundColor="[#5865F2]"
|
|
||||||
className="bg-gradient-to-r from-[#5865F2] to-[#7289DA]"
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* Personal message */}
|
{/* Personal message */}
|
||||||
<Box maxWidth="672px" mx="auto">
|
<Box maxWidth="2xl" mx="auto" textAlign="center">
|
||||||
<Stack gap={3}>
|
<Stack gap={6}>
|
||||||
<Text size="sm" color="text-gray-300" weight="normal" leading="relaxed">
|
<Text size="lg" color="text-gray-300" weight="medium" leading="relaxed">
|
||||||
GridPilot is a <Text weight="bold" color="text-white">solo developer project</Text>, and I'm building it because I got tired of the chaos in league racing.
|
GridPilot is a <span className="text-white font-bold">solo developer project</span> built for the community.
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="sm" color="text-gray-400" weight="normal" leading="relaxed">
|
<Text size="base" color="text-gray-400" weight="normal" leading="relaxed">
|
||||||
This is <Text weight="bold" color="text-gray-300">early days</Text>, and I need your help. Join the Discord to:
|
We are in early alpha. Join us to help shape the future of motorsport infrastructure. Your feedback directly influences the roadmap.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Benefits grid */}
|
{/* Benefits grid */}
|
||||||
<Box
|
<Box
|
||||||
maxWidth="2xl"
|
maxWidth="4xl"
|
||||||
mx="auto"
|
mx="auto"
|
||||||
mt={4}
|
fullWidth
|
||||||
>
|
>
|
||||||
<Grid cols={2} gap={3} className="md:grid-cols-2">
|
<Grid cols={1} mdCols={2} gap={6}>
|
||||||
<BenefitItem
|
<BenefitItem
|
||||||
icon={MessageSquare}
|
icon={MessageSquare}
|
||||||
title="Share your pain points"
|
title="Share your pain points"
|
||||||
description="Tell me what frustrates you about league racing today"
|
description="Tell us what frustrates you about league racing today."
|
||||||
/>
|
/>
|
||||||
<BenefitItem
|
<BenefitItem
|
||||||
icon={Lightbulb}
|
icon={Lightbulb}
|
||||||
title="Shape the product"
|
title="Shape the product"
|
||||||
description="Your ideas will directly influence what gets built"
|
description="Your ideas directly influence our roadmap."
|
||||||
/>
|
/>
|
||||||
<BenefitItem
|
<BenefitItem
|
||||||
icon={Users}
|
icon={Users}
|
||||||
title="Be part of the community"
|
title="Connect with racers"
|
||||||
description="Connect with other league racers who get it"
|
description="Join a community of like-minded competitive drivers."
|
||||||
/>
|
/>
|
||||||
<BenefitItem
|
<BenefitItem
|
||||||
icon={Code}
|
icon={Code}
|
||||||
title="Get early access"
|
title="Early Access"
|
||||||
description="Test features first and help iron out the rough edges"
|
description="Test new features before they go public."
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* CTA Button */}
|
{/* CTA Button */}
|
||||||
<Stack gap={3} pt={4}>
|
<Stack gap={6} pt={4} align="center">
|
||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
href={discordUrl}
|
href={discordUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
variant="discord"
|
variant="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
icon={<DiscordIcon size={28} />}
|
className="px-16 py-4"
|
||||||
|
icon={<DiscordIcon size={24} />}
|
||||||
>
|
>
|
||||||
Join us on Discord
|
Join Discord
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Text size="xs" color="text-primary-blue" weight="light">
|
<Box border borderStyle="dashed" borderColor="primary-accent/50" px={4} py={1}>
|
||||||
💡 Get a link to our early alpha view in the Discord
|
<Text size="xs" color="text-primary-accent" weight="bold" font="mono" uppercase letterSpacing="widest">
|
||||||
</Text>
|
Early Alpha Access Available
|
||||||
|
|
||||||
{!process.env.NEXT_PUBLIC_DISCORD_URL && (
|
|
||||||
<Text size="xs" color="text-gray-500">
|
|
||||||
Note: Configure NEXT_PUBLIC_DISCORD_URL in your environment variables
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* Footer note */}
|
|
||||||
<Box maxWidth="xl" mx="auto" pt={4}>
|
|
||||||
<Text size="xs" color="text-gray-500" weight="light" leading="relaxed" align="center" block>
|
|
||||||
This is a community effort. Every voice matters. Let's build something that actually works for league racing.
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Surface>
|
</Surface>
|
||||||
</Box>
|
</Container>
|
||||||
</Surface>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,30 +153,29 @@ function BenefitItem({ icon, title, description }: { icon: LucideIcon, title: st
|
|||||||
<Surface
|
<Surface
|
||||||
variant="muted"
|
variant="muted"
|
||||||
border
|
border
|
||||||
padding={3}
|
padding={6}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
display="flex"
|
display="flex"
|
||||||
gap={3}
|
gap={5}
|
||||||
className="items-start hover:border-[#5865F2]/30 transition-all"
|
className="items-start hover:border-primary-accent/30 transition-all bg-panel-gray/20 group"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
center
|
center
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
w="6"
|
w="10"
|
||||||
h="6"
|
h="10"
|
||||||
backgroundColor="[#5865F2]"
|
bg="primary-accent/5"
|
||||||
opacity={0.2}
|
|
||||||
border
|
border
|
||||||
borderColor="[#5865F2]"
|
borderColor="border-gray/50"
|
||||||
mt={0.5}
|
className="group-hover:border-primary-accent/30 transition-colors"
|
||||||
>
|
>
|
||||||
<Icon icon={icon} size={4} color="text-[#5865F2]" />
|
<Icon icon={icon} size={5} color="text-primary-accent" />
|
||||||
</Box>
|
</Box>
|
||||||
<Stack gap={0.5}>
|
<Stack gap={2}>
|
||||||
<Text size="xs" weight="medium" color="text-white">{title}</Text>
|
<Text size="base" weight="bold" color="text-white" className="tracking-wide">{title}</Text>
|
||||||
<Text size="xs" color="text-gray-400" leading="relaxed">{description}</Text>
|
<Text size="sm" color="text-gray-400" leading="relaxed">{description}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Surface>
|
</Surface>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,41 +8,31 @@ const xUrl = process.env.NEXT_PUBLIC_X_URL || '#';
|
|||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<Box as="footer" position="relative" bg="bg-deep-graphite">
|
<Box as="footer" position="relative" bg="graphite-black" borderTop borderColor="border-gray/50">
|
||||||
<Box position="absolute" top="0" left="0" right="0" h="px" bg="linear-gradient(to right, transparent, var(--primary-blue), transparent)" />
|
<Box position="absolute" top="0" left="0" right="0" h="px" bg="linear-gradient(to right, transparent, #198CFF, transparent)" opacity={0.3} />
|
||||||
|
|
||||||
<Box maxWidth="4xl" mx="auto" px={{ base: 'calc(1.5rem+var(--sal))', lg: 8 }} py={{ base: 2, md: 8, lg: 12 }} pb={{ base: 'calc(0.5rem+var(--sab))', md: 'calc(1.5rem+var(--sab))' }}>
|
<Box maxWidth="7xl" mx="auto" px={{ base: 6, lg: 8 }} py={{ base: 12, md: 16 }}>
|
||||||
{/* Racing stripe accent */}
|
{/* Racing stripe accent */}
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
gap={1}
|
gap={2}
|
||||||
mb={{ base: 2, md: 4, lg: 6 }}
|
mb={8}
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
<Box w={{ base: "12", md: "20", lg: "28" }} h={{ base: "0.5", md: "0.5", lg: "1" }} bg="bg-white" rounded="full" />
|
<Box w="12" h="1" bg="white" opacity={0.1} />
|
||||||
<Box w={{ base: "12", md: "20", lg: "28" }} h={{ base: "0.5", md: "0.5", lg: "1" }} bg="bg-primary-blue" rounded="full" />
|
<Box w="12" h="1" bg="primary-accent" />
|
||||||
<Box w={{ base: "12", md: "20", lg: "28" }} h={{ base: "0.5", md: "0.5", lg: "1" }} bg="bg-white" rounded="full" />
|
<Box w="12" h="1" bg="white" opacity={0.1} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Personal message */}
|
{/* Personal message */}
|
||||||
<Box
|
<Box
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
mb={{ base: 3, md: 6, lg: 8 }}
|
mb={12}
|
||||||
>
|
>
|
||||||
<Box mb={2} display="flex" justifyContent="center">
|
<Text size="sm" color="text-gray-300" block mb={2} weight="bold" className="tracking-wide">
|
||||||
<Image
|
|
||||||
src="/images/logos/icon-square-dark.svg"
|
|
||||||
alt="GridPilot"
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
fullHeight
|
|
||||||
style={{ width: 'auto' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Text size={{ base: 'xs', lg: 'sm' }} color="text-gray-300" block mb={{ base: 1, md: 2 }}>
|
|
||||||
🏁 Built by a sim racer, for sim racers
|
🏁 Built by a sim racer, for sim racers
|
||||||
</Text>
|
</Text>
|
||||||
<Text size={{ base: 'xs', md: 'xs' }} color="text-gray-400" weight="light" maxWidth="2xl" mx="auto" block>
|
<Text size="xs" color="text-gray-500" weight="normal" maxWidth="2xl" mx="auto" block leading="relaxed">
|
||||||
Just a fellow racer tired of spreadsheets and chaos. GridPilot is my passion project to make league racing actually fun again.
|
Just a fellow racer tired of spreadsheets and chaos. GridPilot is my passion project to make league racing actually fun again.
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -51,50 +41,39 @@ export function Footer() {
|
|||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
gap={{ base: 4, md: 6, lg: 8 }}
|
gap={8}
|
||||||
mb={{ base: 3, md: 6, lg: 8 }}
|
mb={12}
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={discordUrl}
|
href={discordUrl}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="sm"
|
||||||
hoverTextColor="text-neon-aqua"
|
className="text-gray-400 hover:text-primary-accent transition-colors font-bold uppercase tracking-widest"
|
||||||
transition
|
|
||||||
px={3}
|
|
||||||
py={2}
|
|
||||||
minHeight="44px"
|
|
||||||
minWidth="44px"
|
|
||||||
>
|
>
|
||||||
💬 Join Discord
|
💬 Discord
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={xUrl}
|
href={xUrl}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="xs"
|
size="sm"
|
||||||
color="text-gray-300"
|
className="text-gray-400 hover:text-primary-accent transition-colors font-bold uppercase tracking-widest"
|
||||||
hoverTextColor="text-neon-aqua"
|
|
||||||
transition
|
|
||||||
px={3}
|
|
||||||
py={2}
|
|
||||||
minHeight="44px"
|
|
||||||
minWidth="44px"
|
|
||||||
>
|
>
|
||||||
𝕏 Follow on X
|
𝕏 Twitter
|
||||||
</Link>
|
</Link>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Development status */}
|
{/* Development status */}
|
||||||
<Box
|
<Box
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
pt={{ base: 2, md: 4, lg: 6 }}
|
pt={8}
|
||||||
borderTop
|
borderTop
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/30"
|
||||||
>
|
>
|
||||||
<Text size={{ base: 'xs', lg: 'sm' }} color="text-gray-500" block mb={{ base: 1, md: 2 }}>
|
<Text size="xs" color="text-gray-600" block mb={1} font="mono" uppercase letterSpacing="widest">
|
||||||
⚡ Early development • Feedback welcome
|
⚡ Early development • Feedback welcome
|
||||||
</Text>
|
</Text>
|
||||||
<Text size={{ base: 'xs', md: 'xs' }} color="text-gray-600" block>
|
<Text size="xs" color="text-gray-700" block font="mono">
|
||||||
Questions? Find me on Discord
|
© {new Date().getFullYear()} GridPilot
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
49
apps/website/ui/Glow.tsx
Normal file
49
apps/website/ui/Glow.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box } from './Box';
|
||||||
|
|
||||||
|
interface GlowProps {
|
||||||
|
color?: 'primary' | 'aqua' | 'purple' | 'amber';
|
||||||
|
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||||
|
opacity?: number;
|
||||||
|
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center';
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Glow({
|
||||||
|
color = 'primary',
|
||||||
|
size = 'md',
|
||||||
|
opacity = 0.1,
|
||||||
|
position = 'center',
|
||||||
|
className = ''
|
||||||
|
}: GlowProps) {
|
||||||
|
const colorMap = {
|
||||||
|
primary: 'from-primary-accent/20',
|
||||||
|
aqua: 'from-telemetry-aqua/20',
|
||||||
|
purple: 'from-purple-500/20',
|
||||||
|
amber: 'from-warning-amber/20',
|
||||||
|
};
|
||||||
|
|
||||||
|
const sizeMap = {
|
||||||
|
sm: 'w-64 h-64',
|
||||||
|
md: 'w-96 h-96',
|
||||||
|
lg: 'w-[32rem] h-[32rem]',
|
||||||
|
xl: 'w-[48rem] h-[48rem]',
|
||||||
|
};
|
||||||
|
|
||||||
|
const positionMap = {
|
||||||
|
'top-left': '-top-32 -left-32',
|
||||||
|
'top-right': '-top-32 -right-32',
|
||||||
|
'bottom-left': '-bottom-32 -left-32',
|
||||||
|
'bottom-right': '-bottom-32 -right-32',
|
||||||
|
'center': 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
className={`${sizeMap[size]} ${positionMap[position]} bg-radial-gradient ${colorMap[color]} to-transparent blur-[100px] pointer-events-none ${className}`}
|
||||||
|
style={{ opacity }}
|
||||||
|
zIndex={0}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ interface HeaderProps {
|
|||||||
|
|
||||||
export function Header({ children }: HeaderProps) {
|
export function Header({ children }: HeaderProps) {
|
||||||
return (
|
return (
|
||||||
<header className="fixed top-0 left-0 right-0 z-50 bg-deep-graphite/80 backdrop-blur-sm border-b border-white/5">
|
<header className="fixed top-0 left-0 right-0 z-50 bg-graphite-black/80 backdrop-blur-md border-b border-border-gray/50">
|
||||||
<Container>
|
<Container>
|
||||||
{children}
|
{children}
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ export function Heading({ level, children, icon, groupHoverColor, truncate, font
|
|||||||
const Tag = `h${level}` as ElementType;
|
const Tag = `h${level}` as ElementType;
|
||||||
|
|
||||||
const levelClasses = {
|
const levelClasses = {
|
||||||
1: 'text-3xl md:text-4xl font-bold text-white',
|
1: 'text-3xl md:text-4xl font-bold text-white tracking-tight',
|
||||||
2: 'text-xl font-semibold text-white',
|
2: 'text-xl md:text-2xl font-bold text-white tracking-tight',
|
||||||
3: 'text-lg font-semibold text-white',
|
3: 'text-lg font-bold text-white tracking-tight',
|
||||||
4: 'text-base font-semibold text-white',
|
4: 'text-base font-bold text-white tracking-tight',
|
||||||
5: 'text-sm font-semibold text-white',
|
5: 'text-sm font-bold text-white tracking-tight uppercase tracking-wider',
|
||||||
6: 'text-xs font-semibold text-white',
|
6: 'text-xs font-bold text-white tracking-tight uppercase tracking-widest',
|
||||||
};
|
};
|
||||||
|
|
||||||
const weightClasses = {
|
const weightClasses = {
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|||||||
|
|
||||||
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||||
({ className = '', variant = 'default', errorMessage, icon, label, ...props }, ref) => {
|
({ className = '', variant = 'default', errorMessage, icon, label, ...props }, ref) => {
|
||||||
const baseClasses = 'px-3 py-2 border rounded-lg text-white bg-deep-graphite focus:outline-none focus:border-primary-blue transition-colors w-full';
|
const baseClasses = 'px-3 py-2 border rounded-sm text-white bg-graphite-black focus:outline-none focus:border-primary-accent transition-all duration-150 ease-smooth w-full text-sm placeholder:text-gray-600';
|
||||||
const variantClasses = (variant === 'error' || errorMessage) ? 'border-racing-red' : 'border-charcoal-outline';
|
const variantClasses = (variant === 'error' || errorMessage) ? 'border-critical-red' : 'border-border-gray';
|
||||||
const iconClasses = icon ? 'pl-10' : '';
|
const iconClasses = icon ? 'pl-10' : '';
|
||||||
const classes = `${baseClasses} ${variantClasses} ${iconClasses} ${className}`;
|
const classes = `${baseClasses} ${variantClasses} ${iconClasses} ${className}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={1.5} fullWidth>
|
<Stack gap={1.5} fullWidth>
|
||||||
{label && (
|
{label && (
|
||||||
<Text as="label" size="sm" weight="medium" color="text-gray-300">
|
<Text as="label" size="xs" weight="bold" color="text-gray-500" className="uppercase tracking-wider">
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -34,13 +34,14 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
zIndex={10}
|
zIndex={10}
|
||||||
display="flex"
|
display="flex"
|
||||||
center
|
center
|
||||||
|
className="text-gray-500"
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<input ref={ref} className={classes} {...props} />
|
<input ref={ref} className={classes} {...props} />
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<Text size="xs" color="text-error-red" block mt={1}>
|
<Text size="xs" color="text-critical-red" block mt={1}>
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -50,4 +51,4 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Input.displayName = 'Input';
|
Input.displayName = 'Input';
|
||||||
|
|||||||
46
apps/website/ui/LandingItems.tsx
Normal file
46
apps/website/ui/LandingItems.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Stack } from '@/ui/Stack';
|
||||||
|
import { Text } from '@/ui/Text';
|
||||||
|
import { Surface } from '@/ui/Surface';
|
||||||
|
import { Box } from '@/ui/Box';
|
||||||
|
|
||||||
|
export function FeatureItem({ text }: { text: string }) {
|
||||||
|
return (
|
||||||
|
<Surface variant="muted" rounded="none" border padding={4} bg="panel-gray/40" borderColor="border-gray/50" className="group hover:border-primary-accent/30 transition-colors">
|
||||||
|
<Stack direction="row" align="center" gap={4}>
|
||||||
|
<Box w="1" h="4" bg="primary-accent" className="group-hover:h-6 transition-all" />
|
||||||
|
<Text color="text-gray-300" leading="relaxed" weight="normal" size="sm" className="tracking-wide">
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Surface>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ResultItem({ text, color }: { text: string, color: string }) {
|
||||||
|
return (
|
||||||
|
<Surface variant="muted" rounded="none" border padding={4} bg="panel-gray/40" borderColor="border-gray/50" className="group hover:border-primary-accent/30 transition-colors">
|
||||||
|
<Stack direction="row" align="center" gap={4}>
|
||||||
|
<Box w="1" h="4" style={{ backgroundColor: color }} className="group-hover:h-6 transition-all" />
|
||||||
|
<Text color="text-gray-300" leading="relaxed" weight="normal" size="sm" className="tracking-wide">
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Surface>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StepItem({ step, text }: { step: number, text: string }) {
|
||||||
|
return (
|
||||||
|
<Surface variant="muted" rounded="none" border padding={4} bg="panel-gray/40" borderColor="border-gray/50" className="group hover:border-primary-accent/30 transition-colors">
|
||||||
|
<Stack direction="row" align="center" gap={4}>
|
||||||
|
<Box w="8" h="8" display="flex" center border borderColor="border-gray" className="group-hover:border-primary-accent/50 transition-colors">
|
||||||
|
<Text weight="bold" size="xs" color="text-primary-accent" font="mono">{step.toString().padStart(2, '0')}</Text>
|
||||||
|
</Box>
|
||||||
|
<Text color="text-gray-300" leading="relaxed" weight="normal" size="sm" className="tracking-wide">
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Surface>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { Box } from './Box';
|
|||||||
import { Heading } from './Heading';
|
import { Heading } from './Heading';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { Text } from './Text';
|
import { Text } from './Text';
|
||||||
|
import { Stack } from './Stack';
|
||||||
import { Image } from './Image';
|
import { Image } from './Image';
|
||||||
import { PlaceholderImage } from './PlaceholderImage';
|
import { PlaceholderImage } from './PlaceholderImage';
|
||||||
import { Calendar as LucideCalendar, ChevronRight as LucideChevronRight, Users as LucideUsers } from 'lucide-react';
|
import { Calendar as LucideCalendar, ChevronRight as LucideChevronRight, Users as LucideUsers } from 'lucide-react';
|
||||||
@@ -60,13 +61,13 @@ export function LeagueCard({
|
|||||||
<Box
|
<Box
|
||||||
position="relative"
|
position="relative"
|
||||||
h="full"
|
h="full"
|
||||||
rounded="xl"
|
rounded="none"
|
||||||
bg="bg-iron-gray"
|
bg="panel-gray/40"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
transition
|
transition
|
||||||
className="hover:border-primary-blue/50 hover:shadow-[0_0_30px_rgba(25,140,255,0.15)] hover:bg-iron-gray/80"
|
className="hover:border-primary-accent/30 hover:bg-panel-gray/60 transition-all duration-300"
|
||||||
>
|
>
|
||||||
{/* Cover Image */}
|
{/* Cover Image */}
|
||||||
<Box position="relative" h="32" overflow="hidden">
|
<Box position="relative" h="32" overflow="hidden">
|
||||||
@@ -76,10 +77,10 @@ export function LeagueCard({
|
|||||||
fullWidth
|
fullWidth
|
||||||
fullHeight
|
fullHeight
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
className="transition-transform duration-300 group-hover:scale-105"
|
className="transition-transform duration-500 group-hover:scale-105 opacity-60"
|
||||||
/>
|
/>
|
||||||
{/* Gradient Overlay */}
|
{/* Gradient Overlay */}
|
||||||
<Box position="absolute" inset="0" bg="bg-gradient-to-t from-iron-gray via-iron-gray/60 to-transparent" />
|
<Box position="absolute" inset="0" bg="linear-gradient(to top, #0C0D0F, transparent)" />
|
||||||
|
|
||||||
{/* Badges - Top Left */}
|
{/* Badges - Top Left */}
|
||||||
<Box position="absolute" top="3" left="3" display="flex" alignItems="center" gap={2}>
|
<Box position="absolute" top="3" left="3" display="flex" alignItems="center" gap={2}>
|
||||||
@@ -93,7 +94,7 @@ export function LeagueCard({
|
|||||||
|
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Box position="absolute" left="4" bottom="-6" zIndex={10}>
|
<Box position="absolute" left="4" bottom="-6" zIndex={10}>
|
||||||
<Box w="12" h="12" rounded="lg" overflow="hidden" border borderColor="border-iron-gray" bg="bg-deep-graphite" shadow="xl">
|
<Box w="12" h="12" rounded="none" overflow="hidden" border borderColor="border-gray/50" bg="graphite-black" shadow="xl">
|
||||||
{logoUrl ? (
|
{logoUrl ? (
|
||||||
<Image
|
<Image
|
||||||
src={logoUrl}
|
src={logoUrl}
|
||||||
@@ -114,34 +115,37 @@ export function LeagueCard({
|
|||||||
{/* Content */}
|
{/* Content */}
|
||||||
<Box pt={8} px={4} pb={4} display="flex" flexDirection="col" fullHeight>
|
<Box pt={8} px={4} pb={4} display="flex" flexDirection="col" fullHeight>
|
||||||
{/* Title & Description */}
|
{/* Title & Description */}
|
||||||
<Heading level={3} className="line-clamp-1 group-hover:text-primary-blue transition-colors" mb={1}>
|
<Stack direction="row" align="center" gap={2} mb={1}>
|
||||||
{name}
|
<Box w="1" h="4" bg="primary-accent" />
|
||||||
</Heading>
|
<Heading level={3} fontSize="lg" weight="bold" className="line-clamp-1 group-hover:text-primary-accent transition-colors tracking-tight">
|
||||||
<Text size="xs" color="text-gray-500" lineClamp={2} mb={3} style={{ height: '2rem' }} block>
|
{name}
|
||||||
|
</Heading>
|
||||||
|
</Stack>
|
||||||
|
<Text size="xs" color="text-gray-500" lineClamp={2} mb={4} style={{ height: '2.5rem' }} block leading="relaxed">
|
||||||
{description || 'No description available'}
|
{description || 'No description available'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Stats Row */}
|
{/* Stats Row */}
|
||||||
<Box display="flex" alignItems="center" gap={3} mb={3}>
|
<Box display="flex" alignItems="center" gap={3} mb={4}>
|
||||||
{/* Primary Slots (Drivers/Teams/Nations) */}
|
{/* Primary Slots (Drivers/Teams/Nations) */}
|
||||||
<Box flexGrow={1}>
|
<Box flexGrow={1}>
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" mb={1}>
|
<Box display="flex" alignItems="center" justifyContent="between" mb={1.5}>
|
||||||
<Text size="xs" color="text-gray-500">{slotLabel}</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest">{slotLabel}</Text>
|
||||||
<Text size="xs" color="text-gray-400">
|
<Text size="xs" color="text-gray-400" font="mono">
|
||||||
{usedSlots}/{maxSlots || '∞'}
|
{usedSlots}/{maxSlots || '∞'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Box h="1.5" rounded="full" bg="bg-charcoal-outline" overflow="hidden">
|
<Box h="1" rounded="none" bg="border-gray/30" overflow="hidden">
|
||||||
<Box
|
<Box
|
||||||
h="full"
|
h="full"
|
||||||
rounded="full"
|
rounded="none"
|
||||||
transition
|
transition
|
||||||
bg={
|
bg={
|
||||||
fillPercentage >= 90
|
fillPercentage >= 90
|
||||||
? 'bg-warning-amber'
|
? 'warning-amber'
|
||||||
: fillPercentage >= 70
|
: fillPercentage >= 70
|
||||||
? 'bg-primary-blue'
|
? 'primary-accent'
|
||||||
: 'bg-performance-green'
|
: 'success-green'
|
||||||
}
|
}
|
||||||
style={{ width: `${Math.min(fillPercentage, 100)}%` }}
|
style={{ width: `${Math.min(fillPercentage, 100)}%` }}
|
||||||
/>
|
/>
|
||||||
@@ -150,35 +154,25 @@ export function LeagueCard({
|
|||||||
|
|
||||||
{/* Open Slots Badge */}
|
{/* Open Slots Badge */}
|
||||||
{hasOpenSlots && (
|
{hasOpenSlots && (
|
||||||
<Box display="flex" alignItems="center" gap={1} px={2} py={1} rounded="lg" bg="bg-neon-aqua/10" border borderColor="border-neon-aqua/20">
|
<Box display="flex" alignItems="center" gap={1.5} px={2} py={1} rounded="none" bg="primary-accent/5" border borderColor="primary-accent/20">
|
||||||
<Box w="1.5" h="1.5" rounded="full" bg="bg-neon-aqua" animate="pulse" />
|
<Box w="1.5" h="1.5" rounded="full" bg="primary-accent" className="animate-pulse" />
|
||||||
<Text size="xs" color="text-neon-aqua" weight="medium">
|
<Text size="xs" color="text-primary-accent" weight="bold" className="uppercase tracking-tighter">
|
||||||
{openSlotsCount} open
|
{openSlotsCount} OPEN
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Driver count for team leagues */}
|
|
||||||
{isTeamLeague && (
|
|
||||||
<Box display="flex" alignItems="center" gap={2} mb={3}>
|
|
||||||
<Icon icon={LucideUsers} size={3} color="text-gray-500" />
|
|
||||||
<Text size="xs" color="text-gray-500">
|
|
||||||
{usedDriverSlots ?? 0}/{maxDrivers ?? '∞'} drivers
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Spacer to push footer to bottom */}
|
{/* Spacer to push footer to bottom */}
|
||||||
<Box flexGrow={1} />
|
<Box flexGrow={1} />
|
||||||
|
|
||||||
{/* Footer Info */}
|
{/* Footer Info */}
|
||||||
<Box display="flex" alignItems="center" justifyContent="between" pt={3} borderTop style={{ borderColor: 'rgba(38, 38, 38, 0.5)' }} mt="auto">
|
<Box display="flex" alignItems="center" justifyContent="between" pt={3} borderTop borderColor="border-gray/30" mt="auto">
|
||||||
<Box display="flex" alignItems="center" gap={3}>
|
<Box display="flex" alignItems="center" gap={3}>
|
||||||
{timingSummary && (
|
{timingSummary && (
|
||||||
<Box display="flex" alignItems="center" gap={1}>
|
<Box display="flex" alignItems="center" gap={2}>
|
||||||
<Icon icon={LucideCalendar} size={3} color="text-gray-500" />
|
<Icon icon={LucideCalendar} size={3} color="text-gray-500" />
|
||||||
<Text size="xs" color="text-gray-500">
|
<Text size="xs" color="text-gray-500" font="mono">
|
||||||
{timingSummary.split('•')[1]?.trim() || timingSummary}
|
{timingSummary.split('•')[1]?.trim() || timingSummary}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -186,8 +180,8 @@ export function LeagueCard({
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* View Arrow */}
|
{/* View Arrow */}
|
||||||
<Box display="flex" alignItems="center" gap={1} className="group-hover:text-primary-blue transition-colors">
|
<Box display="flex" alignItems="center" gap={1} className="group-hover:text-primary-accent transition-colors">
|
||||||
<Text size="xs" color="text-gray-500">View</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest">VIEW</Text>
|
||||||
<Icon icon={LucideChevronRight} size={3} color="text-gray-500" className="transition-transform group-hover:translate-x-0.5" />
|
<Icon icon={LucideChevronRight} size={3} color="text-gray-500" className="transition-transform group-hover:translate-x-0.5" />
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ export function Link({
|
|||||||
const baseClasses = 'inline-flex items-center transition-colors';
|
const baseClasses = 'inline-flex items-center transition-colors';
|
||||||
|
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
primary: 'text-primary-blue hover:text-primary-blue/80',
|
primary: 'text-primary-accent hover:text-primary-accent/80',
|
||||||
secondary: 'text-purple-300 hover:text-purple-400',
|
secondary: 'text-telemetry-aqua hover:text-telemetry-aqua/80',
|
||||||
ghost: 'text-gray-400 hover:text-gray-300'
|
ghost: 'text-gray-400 hover:text-gray-300'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ interface MainContentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function MainContent({ children }: MainContentProps) {
|
export function MainContent({ children }: MainContentProps) {
|
||||||
return <div className="pt-16">{children}</div>;
|
return <div className="pt-16 md:pt-20">{children}</div>;
|
||||||
}
|
}
|
||||||
@@ -28,10 +28,10 @@ export function Section({
|
|||||||
}: SectionProps) {
|
}: SectionProps) {
|
||||||
const variantClasses = {
|
const variantClasses = {
|
||||||
default: '',
|
default: '',
|
||||||
card: 'bg-iron-gray rounded-lg p-6 border border-charcoal-outline',
|
card: 'bg-panel-gray rounded-none p-6 border border-border-gray',
|
||||||
highlight: 'bg-gradient-to-r from-blue-900/20 to-blue-700/10 rounded-lg p-6 border border-blue-500/30',
|
highlight: 'bg-gradient-to-r from-primary-accent/10 to-transparent rounded-none p-6 border border-primary-accent/30',
|
||||||
dark: 'bg-iron-gray',
|
dark: 'bg-graphite-black',
|
||||||
light: 'bg-charcoal-outline'
|
light: 'bg-panel-gray'
|
||||||
};
|
};
|
||||||
|
|
||||||
const classes = [
|
const classes = [
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function Surface<T extends ElementType = 'div'>({
|
|||||||
as,
|
as,
|
||||||
children,
|
children,
|
||||||
variant = 'default',
|
variant = 'default',
|
||||||
rounded = 'lg',
|
rounded = 'none',
|
||||||
border = false,
|
border = false,
|
||||||
padding = 0,
|
padding = 0,
|
||||||
className = '',
|
className = '',
|
||||||
@@ -26,16 +26,16 @@ export function Surface<T extends ElementType = 'div'>({
|
|||||||
...props
|
...props
|
||||||
}: SurfaceProps<T> & ComponentPropsWithoutRef<T>) {
|
}: SurfaceProps<T> & ComponentPropsWithoutRef<T>) {
|
||||||
const variantClasses: Record<string, string> = {
|
const variantClasses: Record<string, string> = {
|
||||||
default: 'bg-iron-gray',
|
default: 'bg-panel-gray',
|
||||||
muted: 'bg-iron-gray/50',
|
muted: 'bg-panel-gray/40',
|
||||||
dark: 'bg-deep-graphite',
|
dark: 'bg-graphite-black',
|
||||||
glass: 'bg-deep-graphite/60 backdrop-blur-md',
|
glass: 'bg-graphite-black/60 backdrop-blur-md',
|
||||||
'gradient-blue': 'bg-gradient-to-br from-primary-blue/20 via-iron-gray/80 to-deep-graphite',
|
'gradient-blue': 'bg-gradient-to-br from-primary-accent/10 via-panel-gray/80 to-graphite-black',
|
||||||
'gradient-gold': 'bg-gradient-to-br from-yellow-600/20 via-iron-gray/80 to-deep-graphite',
|
'gradient-gold': 'bg-gradient-to-br from-warning-amber/10 via-panel-gray/80 to-graphite-black',
|
||||||
'gradient-purple': 'bg-gradient-to-br from-purple-600/20 via-iron-gray/80 to-deep-graphite',
|
'gradient-purple': 'bg-gradient-to-br from-purple-600/10 via-panel-gray/80 to-graphite-black',
|
||||||
'gradient-green': 'bg-gradient-to-br from-green-600/20 via-iron-gray/80 to-deep-graphite',
|
'gradient-green': 'bg-gradient-to-br from-success-green/10 via-panel-gray/80 to-graphite-black',
|
||||||
'discord': 'bg-gradient-to-b from-deep-graphite to-iron-gray',
|
'discord': 'bg-gradient-to-b from-graphite-black to-panel-gray',
|
||||||
'discord-inner': 'bg-gradient-to-br from-iron-gray via-deep-graphite to-iron-gray'
|
'discord-inner': 'bg-gradient-to-br from-panel-gray via-graphite-black to-panel-gray'
|
||||||
};
|
};
|
||||||
|
|
||||||
const shadowClasses: Record<string, string> = {
|
const shadowClasses: Record<string, string> = {
|
||||||
@@ -72,7 +72,7 @@ export function Surface<T extends ElementType = 'div'>({
|
|||||||
const classes = [
|
const classes = [
|
||||||
variantClasses[variant],
|
variantClasses[variant],
|
||||||
roundedClasses[rounded],
|
roundedClasses[rounded],
|
||||||
border ? 'border border-charcoal-outline' : '',
|
border ? 'border border-border-gray' : '',
|
||||||
paddingClasses[padding] || 'p-0',
|
paddingClasses[padding] || 'p-0',
|
||||||
shadowClasses[shadow],
|
shadowClasses[shadow],
|
||||||
display ? display : '',
|
display ? display : '',
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ interface TableProps extends HTMLAttributes<HTMLTableElement> {
|
|||||||
|
|
||||||
export function Table({ children, className = '', ...props }: TableProps) {
|
export function Table({ children, className = '', ...props }: TableProps) {
|
||||||
return (
|
return (
|
||||||
<Box overflow="auto">
|
<Box overflow="auto" className="border border-border-gray rounded-sm">
|
||||||
<table className={`w-full ${className}`} {...props}>
|
<table className={`w-full border-collapse text-left ${className}`} {...props}>
|
||||||
{children}
|
{children}
|
||||||
</table>
|
</table>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -22,7 +22,7 @@ interface TableHeadProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|||||||
|
|
||||||
export function TableHead({ children, ...props }: TableHeadProps) {
|
export function TableHead({ children, ...props }: TableHeadProps) {
|
||||||
return (
|
return (
|
||||||
<thead {...props}>
|
<thead className="bg-graphite-black border-b border-border-gray" {...props}>
|
||||||
{children}
|
{children}
|
||||||
</thead>
|
</thead>
|
||||||
);
|
);
|
||||||
@@ -34,7 +34,7 @@ interface TableBodyProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|||||||
|
|
||||||
export function TableBody({ children, ...props }: TableBodyProps) {
|
export function TableBody({ children, ...props }: TableBodyProps) {
|
||||||
return (
|
return (
|
||||||
<tbody {...props}>
|
<tbody className="divide-y divide-border-gray/50" {...props}>
|
||||||
{children}
|
{children}
|
||||||
</tbody>
|
</tbody>
|
||||||
);
|
);
|
||||||
@@ -47,8 +47,8 @@ interface TableRowProps extends BoxProps<'tr'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TableRow({ children, className = '', clickable = false, variant = 'default', ...props }: TableRowProps) {
|
export function TableRow({ children, className = '', clickable = false, variant = 'default', ...props }: TableRowProps) {
|
||||||
const baseClasses = 'border-b border-charcoal-outline/50 transition-colors';
|
const baseClasses = 'transition-colors duration-150 ease-smooth';
|
||||||
const variantClasses = variant === 'highlight' ? 'bg-primary-blue/5' : '';
|
const variantClasses = variant === 'highlight' ? 'bg-primary-accent/5' : 'hover:bg-white/[0.02]';
|
||||||
const classes = [
|
const classes = [
|
||||||
baseClasses,
|
baseClasses,
|
||||||
variantClasses,
|
variantClasses,
|
||||||
@@ -68,7 +68,7 @@ interface TableHeaderProps extends BoxProps<'th'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TableHeader({ children, className = '', ...props }: TableHeaderProps) {
|
export function TableHeader({ children, className = '', ...props }: TableHeaderProps) {
|
||||||
const baseClasses = 'py-3 px-4 text-xs font-medium text-gray-400 uppercase';
|
const baseClasses = 'py-2.5 px-4 text-[11px] font-bold text-gray-500 uppercase tracking-wider';
|
||||||
const classes = [baseClasses, className].filter(Boolean).join(' ');
|
const classes = [baseClasses, className].filter(Boolean).join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -83,7 +83,7 @@ interface TableCellProps extends BoxProps<'td'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TableCell({ children, className = '', ...props }: TableCellProps) {
|
export function TableCell({ children, className = '', ...props }: TableCellProps) {
|
||||||
const baseClasses = 'py-3 px-4';
|
const baseClasses = 'py-3 px-4 text-sm text-gray-300';
|
||||||
const classes = [baseClasses, className].filter(Boolean).join(' ');
|
const classes = [baseClasses, className].filter(Boolean).join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -47,53 +47,55 @@ export function TeamCard({
|
|||||||
onClick,
|
onClick,
|
||||||
}: TeamCardProps) {
|
}: TeamCardProps) {
|
||||||
return (
|
return (
|
||||||
<Box onClick={onClick} h="full" cursor={onClick ? 'pointer' : 'default'}>
|
<Box onClick={onClick} h="full" cursor={onClick ? 'pointer' : 'default'} className="group">
|
||||||
<Card h="full" p={0} display="flex" flexDirection="col" overflow="hidden">
|
<Card h="full" p={0} display="flex" flexDirection="col" overflow="hidden" className="bg-panel-gray/40 border-border-gray/50 hover:border-primary-accent/30 hover:bg-panel-gray/60 transition-all duration-300">
|
||||||
{/* Header with Logo */}
|
{/* Header with Logo */}
|
||||||
<Box p={4} pb={0}>
|
<Box p={5} pb={0}>
|
||||||
<Stack direction="row" align="start" gap={4}>
|
<Stack direction="row" align="start" gap={4}>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Box
|
<Box
|
||||||
w="14"
|
w="16"
|
||||||
h="14"
|
h="16"
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
bg="bg-deep-graphite"
|
bg="graphite-black"
|
||||||
display="flex"
|
display="flex"
|
||||||
center
|
center
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
border
|
border
|
||||||
borderColor="border-charcoal-outline"
|
borderColor="border-gray/50"
|
||||||
|
className="relative"
|
||||||
>
|
>
|
||||||
{logo ? (
|
{logo ? (
|
||||||
<Image
|
<Image
|
||||||
src={logo}
|
src={logo}
|
||||||
alt={name}
|
alt={name}
|
||||||
width={56}
|
width={64}
|
||||||
height={56}
|
height={64}
|
||||||
fullWidth
|
fullWidth
|
||||||
fullHeight
|
fullHeight
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PlaceholderImage size={56} />
|
<PlaceholderImage size={64} />
|
||||||
)}
|
)}
|
||||||
|
<Box position="absolute" top="-1px" left="-1px" w="2" h="2" borderTop borderLeft borderColor="primary-accent/30" />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Title & Badges */}
|
{/* Title & Badges */}
|
||||||
<Box flexGrow={1} minWidth="0">
|
<Box flexGrow={1} minWidth="0">
|
||||||
<Stack direction="row" align="start" justify="between" gap={2}>
|
<Stack direction="row" align="start" justify="between" gap={2}>
|
||||||
<Heading level={4}>
|
<Heading level={4} weight="bold" fontSize="lg" className="tracking-tight group-hover:text-primary-accent transition-colors">
|
||||||
{name}
|
{name}
|
||||||
</Heading>
|
</Heading>
|
||||||
{isRecruiting && (
|
{isRecruiting && (
|
||||||
<Badge variant="success" icon={UserPlus}>
|
<Badge variant="success" size="xs">
|
||||||
Recruiting
|
RECRUITING
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{/* Performance Level & Category */}
|
{/* Performance Level & Category */}
|
||||||
<Stack direction="row" align="center" gap={2} wrap mt={1.5}>
|
<Stack direction="row" align="center" gap={2} wrap mt={2}>
|
||||||
{performanceBadge}
|
{performanceBadge}
|
||||||
{specializationContent}
|
{specializationContent}
|
||||||
{categoryBadge}
|
{categoryBadge}
|
||||||
@@ -103,48 +105,43 @@ export function TeamCard({
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<Box p={4} display="flex" flexDirection="col" flexGrow={1}>
|
<Box p={5} display="flex" flexDirection="col" flexGrow={1}>
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<Text
|
<Text
|
||||||
size="xs"
|
size="xs"
|
||||||
color="text-gray-500"
|
color="text-gray-500"
|
||||||
mb={3}
|
mb={4}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
block
|
block
|
||||||
|
leading="relaxed"
|
||||||
|
style={{ height: '2.5rem' }}
|
||||||
>
|
>
|
||||||
{description || 'No description available'}
|
{description || 'No description available'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* Region & Languages */}
|
{/* Region & Languages */}
|
||||||
{(region || languagesContent) && (
|
{(region || languagesContent) && (
|
||||||
<Stack direction="row" align="center" gap={2} wrap mb={3}>
|
<Stack direction="row" align="center" gap={2} wrap mb={4}>
|
||||||
{region && (
|
{region && (
|
||||||
<Box
|
<Box
|
||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gap={1.5}
|
gap={2}
|
||||||
px={2}
|
px={2}
|
||||||
py={1}
|
py={1}
|
||||||
rounded="md"
|
rounded="none"
|
||||||
bg="bg-iron-gray/50"
|
bg="panel-gray/20"
|
||||||
border
|
border
|
||||||
style={{ borderColor: 'rgba(38, 38, 38, 0.3)' }}
|
borderColor="border-gray/30"
|
||||||
>
|
>
|
||||||
<Icon icon={Globe} size={3} color="var(--neon-aqua)" />
|
<Icon icon={Globe} size={3} color="text-primary-accent" />
|
||||||
<Text size="xs" color="text-gray-400">{region}</Text>
|
<Text size="xs" color="text-gray-400" weight="bold" className="uppercase tracking-widest">{region}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{languagesContent}
|
{languagesContent}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Stats Grid */}
|
|
||||||
{statsContent && (
|
|
||||||
<Box display="grid" gridCols={3} gap={2} mb={4}>
|
|
||||||
{statsContent}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Spacer */}
|
{/* Spacer */}
|
||||||
<Box flexGrow={1} />
|
<Box flexGrow={1} />
|
||||||
|
|
||||||
@@ -153,21 +150,21 @@ export function TeamCard({
|
|||||||
display="flex"
|
display="flex"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="between"
|
justifyContent="between"
|
||||||
pt={3}
|
pt={4}
|
||||||
borderTop
|
borderTop
|
||||||
style={{ borderColor: 'rgba(38, 38, 38, 0.5)' }}
|
borderColor="border-gray/30"
|
||||||
mt="auto"
|
mt="auto"
|
||||||
>
|
>
|
||||||
<Stack direction="row" align="center" gap={2}>
|
<Stack direction="row" align="center" gap={2}>
|
||||||
<Icon icon={Users} size={3} color="var(--text-gray-600)" />
|
<Icon icon={Users} size={3} color="text-gray-500" />
|
||||||
<Text size="xs" color="text-gray-500">
|
<Text size="xs" color="text-gray-500" font="mono">
|
||||||
{memberCount} {memberCount === 1 ? 'member' : 'members'}
|
{memberCount} {memberCount === 1 ? 'MEMBER' : 'MEMBERS'}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack direction="row" align="center" gap={1}>
|
<Stack direction="row" align="center" gap={1} className="group-hover:text-primary-accent transition-colors">
|
||||||
<Text size="xs" color="text-gray-500">View</Text>
|
<Text size="xs" color="text-gray-500" weight="bold" className="uppercase tracking-widest">VIEW</Text>
|
||||||
<Icon icon={ChevronRight} size={3} color="var(--text-gray-600)" />
|
<Icon icon={ChevronRight} size={3} color="text-gray-500" className="transition-transform group-hover:translate-x-0.5" />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
43
apps/website/ui/TelemetryLine.tsx
Normal file
43
apps/website/ui/TelemetryLine.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box } from './Box';
|
||||||
|
|
||||||
|
interface TelemetryLineProps {
|
||||||
|
color?: 'primary' | 'aqua' | 'amber' | 'green' | 'red';
|
||||||
|
height?: number | string;
|
||||||
|
animate?: boolean;
|
||||||
|
opacity?: number;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TelemetryLine({
|
||||||
|
color = 'primary',
|
||||||
|
height = '2px',
|
||||||
|
animate = false,
|
||||||
|
opacity = 1,
|
||||||
|
className = ''
|
||||||
|
}: TelemetryLineProps) {
|
||||||
|
const colorMap = {
|
||||||
|
primary: 'bg-primary-accent',
|
||||||
|
aqua: 'bg-telemetry-aqua',
|
||||||
|
amber: 'bg-warning-amber',
|
||||||
|
green: 'bg-success-green',
|
||||||
|
red: 'bg-critical-red',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
height={height}
|
||||||
|
fullWidth
|
||||||
|
className={`${colorMap[color]} ${animate ? 'animate-pulse' : ''} ${className}`}
|
||||||
|
style={{
|
||||||
|
opacity,
|
||||||
|
boxShadow: `0 0 8px ${
|
||||||
|
color === 'primary' ? '#198CFF' :
|
||||||
|
color === 'aqua' ? '#4ED4E0' :
|
||||||
|
color === 'amber' ? '#FFBE4D' :
|
||||||
|
color === 'green' ? '#6FE37A' : '#E35C5C'
|
||||||
|
}4D`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -24,30 +24,35 @@ export function UpcomingRaceItem({
|
|||||||
return (
|
return (
|
||||||
<Surface
|
<Surface
|
||||||
variant="muted"
|
variant="muted"
|
||||||
padding={3}
|
padding={4}
|
||||||
rounded="lg"
|
rounded="none"
|
||||||
|
border
|
||||||
|
borderColor="border-gray/30"
|
||||||
|
className="hover:border-primary-accent/30 transition-colors bg-panel-gray/20 group"
|
||||||
>
|
>
|
||||||
<Text color="text-white" weight="medium" block>
|
<Stack direction="row" align="center" gap={4}>
|
||||||
{track}
|
<Box w="1" h="8" bg="primary-accent" opacity={0.3} className="group-hover:opacity-100 transition-opacity" />
|
||||||
</Text>
|
<Box flexGrow={1}>
|
||||||
<Text size="sm" color="text-gray-400" block>
|
<Text color="text-white" weight="bold" block className="tracking-tight">
|
||||||
{car}
|
{track}
|
||||||
</Text>
|
</Text>
|
||||||
<Stack direction="row" align="center" gap={2} mt={1}>
|
<Text size="xs" color="text-gray-500" block weight="medium" className="uppercase tracking-widest mt-0.5">
|
||||||
<Text size="xs" color="text-gray-500">
|
{car}
|
||||||
{formattedDate}
|
</Text>
|
||||||
</Text>
|
</Box>
|
||||||
<Text size="xs" color="text-gray-500">
|
<Stack align="end" gap={1}>
|
||||||
•
|
<Text size="xs" color="text-gray-400" font="mono" weight="bold">
|
||||||
</Text>
|
{formattedDate}
|
||||||
<Text size="xs" color="text-gray-500">
|
</Text>
|
||||||
{formattedTime}
|
<Text size="xs" color="text-gray-600" font="mono">
|
||||||
</Text>
|
{formattedTime}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
{isMyLeague && (
|
{isMyLeague && (
|
||||||
<Box mt={1}>
|
<Box mt={3} display="flex" justifyContent="end">
|
||||||
<Badge variant="success">
|
<Badge variant="success" size="xs">
|
||||||
Your League
|
YOUR LEAGUE
|
||||||
</Badge>
|
</Badge>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
199
docs/THEME.md
199
docs/THEME.md
@@ -1,141 +1,124 @@
|
|||||||
# GridPilot Theme — “Motorsport Infrastructure, Smoothly Engineered”
|
🎨 GridPilot Dev Theme — “Precision Racing Minimal”
|
||||||
|
|
||||||
*A precise, professional motorsport interface with premium smoothness — engineered for trust, control, and long-term use.*
|
The core idea:
|
||||||
|
A UI that feels like a race engineer’s dashboard — not a game launcher, not corporate SaaS.
|
||||||
|
|
||||||
---
|
⸻
|
||||||
|
|
||||||
## 1. Design Philosophy
|
1. Identity & Mood
|
||||||
|
|
||||||
GridPilot should feel like:
|
The theme should communicate:
|
||||||
- **race control software**, not a game UI
|
• Precision (like telemetry screens)
|
||||||
- **infrastructure**, not a startup product
|
• Calm intensity (the feeling before a qualifying lap)
|
||||||
- **engineered**, not styled
|
• Authority without ego (used by serious racers)
|
||||||
- **stable and authoritative**, yet pleasant to use
|
• Modern minimalism (dev-friendly, clean structure)
|
||||||
- **built for years**, not trends
|
• Soft gaming hints (subtle neon touches, not RGB vomit)
|
||||||
|
|
||||||
The goal is not excitement.
|
It appeals equally to:
|
||||||
The goal is **confidence**.
|
• Devs (clean, modular, readable components)
|
||||||
|
• Gamers (immersive, familiar atmosphere)
|
||||||
|
• Simracers (motorsport seriousness)
|
||||||
|
|
||||||
Think:
|
⸻
|
||||||
**“FIA race control x timing screens x modern tooling — with smooth interaction.”**
|
|
||||||
|
|
||||||
---
|
2. Visual Language
|
||||||
|
|
||||||
## 2. Visual Style
|
Primary Look
|
||||||
|
• Dark, matte surfaces
|
||||||
|
• Thin, crisp separators
|
||||||
|
• Soft glow accents in blue / cyan / purple
|
||||||
|
• Avoid aggressive neon or gamer-overload
|
||||||
|
• Subtle gradient tints (barely visible)
|
||||||
|
|
||||||
### Core Aesthetic:
|
Color System
|
||||||
- dark, neutral background
|
• Base: Near-black graphite (#0C0D0F)
|
||||||
- minimal gradients (almost invisible)
|
• Surface: Deep charcoal (#141619)
|
||||||
- restrained highlights only for meaning
|
• Outline: Soft steel grey (#23272B)
|
||||||
- strict hierarchy
|
• Primary Accent: Electric blue (#198CFF)
|
||||||
- dense, readable layouts
|
• Telemetry Accent: Aqua (#4ED4E0)
|
||||||
- smooth transitions only where state changes
|
• Warning: Motorsport amber (#FFBE4D)
|
||||||
|
|
||||||
Everything should look:
|
Everything should feel instrument-grade, not decorative.
|
||||||
**intentional, measured, and calm.**
|
|
||||||
|
|
||||||
---
|
⸻
|
||||||
|
|
||||||
### Color Palette (refined)
|
3. Component Philosophy
|
||||||
|
|
||||||
- **Graphite Black:** `#0E0F11`
|
Cards
|
||||||
- **Panel Gray:** `#171A1E`
|
• Functional over flashy
|
||||||
- **Border Gray:** `#22262A`
|
• Slight bevel or inset shadow (hint of cockpit panels)
|
||||||
- **Primary Accent:** `#198CFF` (used sparingly)
|
• Dense info, clean hierarchy
|
||||||
- **Success Green:** `#6FE37A`
|
|
||||||
- **Warning Amber:** `#FFC556`
|
|
||||||
- **Critical Red:** `#E35C5C`
|
|
||||||
|
|
||||||
No neon.
|
Tables
|
||||||
No playful colors.
|
• High-density
|
||||||
Color = meaning, not decoration.
|
• Thin row dividers
|
||||||
|
• Highlight on hover only
|
||||||
|
• Telemetry-coded status colors
|
||||||
|
|
||||||
---
|
Buttons
|
||||||
|
• Flat by default
|
||||||
|
• Glow + gradient only on hover
|
||||||
|
• Snappy micro-animation (motorsport feedback)
|
||||||
|
|
||||||
## 3. Motion & Interaction
|
Modals
|
||||||
|
• Soft frosted blur
|
||||||
|
• Fast open/close animation
|
||||||
|
• Dimmed pit-lane-lighting vibe
|
||||||
|
|
||||||
### Animation Philosophy
|
⸻
|
||||||
Motion exists only to:
|
|
||||||
- confirm an action
|
|
||||||
- show hierarchy
|
|
||||||
- indicate state change
|
|
||||||
|
|
||||||
Never to impress.
|
4. Motion & Feedback
|
||||||
|
|
||||||
### Characteristics:
|
Racing-inspired, but minimal:
|
||||||
- short durations (120–200ms)
|
• Short accelerations (fast ease-out)
|
||||||
- low amplitude
|
• No bounces — this isn’t a game store
|
||||||
- no exaggerated easing
|
• Hover = slight lift + color pulse
|
||||||
- no elastic bounces
|
• Loading = progress line (like pit limiter light)
|
||||||
- no decorative movement
|
• Switching tabs = sliding underline (chicane motion)
|
||||||
|
|
||||||
**Motion should feel like a well-damped suspension — not a show car.**
|
Devs should feel the UI is responsive, not playful.
|
||||||
|
|
||||||
---
|
⸻
|
||||||
|
|
||||||
## 4. Where Motion Is Allowed
|
5. Layout Structure
|
||||||
|
|
||||||
- button press feedback
|
Think “Telemetry Workspace”:
|
||||||
- panel open / close
|
• Sidebar = dashboard rail
|
||||||
- table row expansion
|
• Header = control bar
|
||||||
- state changes (pending → approved → completed)
|
• Content = track map / data table zone
|
||||||
- subtle loading indicators
|
• Right panel = session or context info
|
||||||
|
|
||||||
If motion does not improve clarity → remove it.
|
Everything modular, easily replaceable, easy to reason about.
|
||||||
|
|
||||||
---
|
⸻
|
||||||
|
|
||||||
## 5. What We Explicitly Avoid
|
6. Tone for Dev-Facing Text
|
||||||
|
• Short
|
||||||
|
• Neutral
|
||||||
|
• Calm
|
||||||
|
• Technical
|
||||||
|
• Real-world language (not corporate slang)
|
||||||
|
|
||||||
- hero animations
|
Examples:
|
||||||
- animated backgrounds
|
• “Add race”
|
||||||
- glowing UI chrome
|
• “Review protest”
|
||||||
- playful hover gimmicks
|
• “Session updated”
|
||||||
- “app store” aesthetics
|
• “Export standings”
|
||||||
- anything that reduces trust
|
• “Sponsor payout queued”
|
||||||
|
|
||||||
GridPilot must feel:
|
No hype. No marketing tone inside the app.
|
||||||
**reliable before it feels beautiful.**
|
|
||||||
|
|
||||||
---
|
⸻
|
||||||
|
|
||||||
## 6. Components
|
7. Emotional Target
|
||||||
|
|
||||||
### Tables
|
Users should feel:
|
||||||
- primary UI element
|
• In control
|
||||||
- dense, readable
|
• Supported
|
||||||
- fixed column logic
|
• Efficient
|
||||||
- no playful effects
|
• Connected to motorsport culture
|
||||||
|
• Trusting (nothing looks cheap or gimmicky)
|
||||||
|
|
||||||
### Cards
|
It’s basically:
|
||||||
- functional grouping
|
|
||||||
- no visual dominance
|
|
||||||
- secondary to tables
|
|
||||||
|
|
||||||
### Modals
|
“A premium cockpit dashboard… built by people who race and code.”
|
||||||
- simple
|
|
||||||
- fast
|
|
||||||
- decisive
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Typography
|
|
||||||
|
|
||||||
- neutral sans-serif
|
|
||||||
- excellent numeric readability
|
|
||||||
- no personality fonts
|
|
||||||
|
|
||||||
Primary goal:
|
|
||||||
**information clarity, not brand expression.**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Design Principle Summary
|
|
||||||
|
|
||||||
GridPilot is:
|
|
||||||
- not gamer UI
|
|
||||||
- not esports branding
|
|
||||||
- not corporate SaaS
|
|
||||||
|
|
||||||
It is:
|
|
||||||
**modern motorsport infrastructure software.**
|
|
||||||
Reference in New Issue
Block a user