website landing page

This commit is contained in:
2025-12-02 19:44:18 +01:00
parent fd3b4171aa
commit 895318ac40
33 changed files with 2226 additions and 842 deletions

View File

@@ -1,147 +0,0 @@
import { NextRequest, NextResponse } from 'next/server';
import { validateEmail, isDisposableEmail } from '@/lib/email-validation';
import { checkRateLimit, getClientIp } from '@/lib/rate-limit';
const SIGNUP_LIST_KEY = 'signups:emails';
const isDev = !process.env.KV_REST_API_URL;
// In-memory fallback for development
const devSignups = new Map<string, { email: string; timestamp: string; ip: string }>();
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { email } = body;
if (!email || typeof email !== 'string') {
return NextResponse.json(
{ error: "That email doesn't look right." },
{ status: 400 }
);
}
const validation = validateEmail(email);
if (!validation.success) {
return NextResponse.json(
{ error: "That email doesn't look right." },
{ status: 400 }
);
}
const sanitizedEmail = validation.email!;
if (isDisposableEmail(sanitizedEmail)) {
return NextResponse.json(
{ error: "That email doesn't look right." },
{ status: 400 }
);
}
const clientIp = getClientIp(request);
const rateLimitResult = await checkRateLimit(clientIp);
if (!rateLimitResult.allowed) {
const retrySeconds = Math.ceil((rateLimitResult.resetAt - Date.now()) / 1000);
return NextResponse.json(
{
error: 'Too fast. Try again in a minute.',
resetAt: rateLimitResult.resetAt,
retryAfter: retrySeconds,
},
{
status: 429,
headers: {
'Retry-After': retrySeconds.toString(),
},
}
);
}
if (isDev) {
console.warn('[DEV MODE] Using in-memory signup storage - data will not persist');
if (devSignups.has(sanitizedEmail)) {
return NextResponse.json(
{ error: "Already got you. I'll keep you posted." },
{ status: 409 }
);
}
const signupData = {
email: sanitizedEmail,
timestamp: new Date().toISOString(),
ip: clientIp,
};
devSignups.set(sanitizedEmail, signupData);
return NextResponse.json(
{
success: true,
message: 'Thanks. That means a lot.',
},
{
status: 200,
headers: {
'X-RateLimit-Remaining': rateLimitResult.remaining.toString(),
'X-RateLimit-Reset': rateLimitResult.resetAt.toString(),
},
}
);
}
// Production: Use Vercel KV
const { kv } = await import('@vercel/kv');
const existingSignup = await kv.hget(SIGNUP_LIST_KEY, sanitizedEmail);
if (existingSignup) {
return NextResponse.json(
{ error: "Already got you. I'll keep you posted." },
{ status: 409 }
);
}
const signupData = {
email: sanitizedEmail,
timestamp: new Date().toISOString(),
ip: clientIp,
};
await kv.hset(SIGNUP_LIST_KEY, {
[sanitizedEmail]: JSON.stringify(signupData),
});
return NextResponse.json(
{
success: true,
message: 'Thanks. That means a lot.',
},
{
status: 200,
headers: {
'X-RateLimit-Remaining': rateLimitResult.remaining.toString(),
'X-RateLimit-Reset': rateLimitResult.resetAt.toString(),
},
}
);
} catch (error) {
console.error('Signup error:', error);
return NextResponse.json(
{ error: 'Something broke. Try again?' },
{ status: 500 }
);
}
}
/**
* GET /api/signup
* Return 405 Method Not Allowed
*/
export async function GET() {
return NextResponse.json(
{ error: 'Method not allowed' },
{ status: 405 }
);
}

View File

@@ -13,10 +13,61 @@
--color-performance-green: #6FE37A;
--color-warning-amber: #FFC556;
--color-neon-aqua: #43C9E6;
--sat: env(safe-area-inset-top);
--sar: env(safe-area-inset-right);
--sab: env(safe-area-inset-bottom);
--sal: env(safe-area-inset-left);
}
* {
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
html {
overscroll-behavior: none;
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
}
body {
@apply bg-deep-graphite text-white antialiased;
overscroll-behavior: none;
}
button, a {
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
.scroll-container {
-webkit-overflow-scrolling: touch;
scroll-behavior: smooth;
}
/* Mobile typography optimization - lighter and more spacious */
@media (max-width: 640px) {
h1 {
font-size: clamp(1.5rem, 6vw, 2rem);
font-weight: 600;
line-height: 1.2;
}
h2 {
font-size: clamp(1.125rem, 4.5vw, 1.5rem);
font-weight: 600;
line-height: 1.3;
}
h3 {
font-size: 1rem;
font-weight: 500;
}
p {
font-size: 0.8125rem; /* 13px */
line-height: 1.6;
}
}
}
@@ -24,4 +75,159 @@
.animate-spring {
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Racing stripe patterns */
.racing-stripes {
background: linear-gradient(
45deg,
transparent 25%,
rgba(25, 140, 255, 0.03) 25%,
rgba(25, 140, 255, 0.03) 50%,
transparent 50%,
transparent 75%,
rgba(25, 140, 255, 0.03) 75%
);
background-size: 60px 60px;
}
/* Checkered flag pattern */
.checkered-pattern {
background-image:
linear-gradient(45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.02) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.02) 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
/* Speed lines animation */
@keyframes speed-lines {
0% {
transform: translateX(0) scaleX(0);
opacity: 0;
}
50% {
opacity: 0.3;
}
100% {
transform: translateX(100px) scaleX(1);
opacity: 0;
}
}
.animate-speed-lines {
animation: speed-lines 1.5s ease-out infinite;
}
/* Racing accent line */
.racing-accent {
position: relative;
}
.racing-accent::before {
content: '';
position: absolute;
left: -16px;
top: 0;
bottom: 0;
width: 3px;
background: linear-gradient(to bottom, #FF0000, #198CFF);
border-radius: 2px;
}
/* Carbon fiber texture */
.carbon-fiber {
background-image:
linear-gradient(27deg, rgba(255,255,255,0.02) 5%, transparent 5%),
linear-gradient(207deg, rgba(255,255,255,0.02) 5%, transparent 5%),
linear-gradient(27deg, rgba(0,0,0,0.05) 5%, transparent 5%),
linear-gradient(207deg, rgba(0,0,0,0.05) 5%, transparent 5%);
background-size: 10px 10px;
}
/* Racing red-white-blue animated gradient */
@keyframes racing-gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.animate-racing-gradient {
background: linear-gradient(
90deg,
#DC0000 0%,
#FFFFFF 25%,
#0066FF 50%,
#DC0000 75%,
#FFFFFF 100%
);
background-size: 300% 100%;
animation: racing-gradient 12s linear infinite;
-webkit-background-clip: text;
background-clip: text;
}
/* Static red-white-blue gradient (no animation) */
.static-racing-gradient {
background: linear-gradient(
90deg,
#DC0000 0%,
#FFFFFF 50%,
#2563eb 100%
);
-webkit-background-clip: text;
background-clip: text;
}
@media (prefers-reduced-motion: reduce) {
.animate-racing-gradient {
animation: none;
}
}
/* Entrance animations */
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fade-in-up 0.6s ease-out forwards;
}
.animate-fade-in {
animation: fade-in 0.4s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) {
.animate-fade-in-up,
.animate-fade-in {
animation: none;
opacity: 1;
transform: none;
}
}
}

View File

@@ -4,6 +4,18 @@ import './globals.css';
export const metadata: Metadata = {
title: 'GridPilot - iRacing League Racing Platform',
description: 'The dedicated home for serious iRacing leagues. Automatic results, standings, team racing, and professional race control.',
viewport: {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
viewportFit: 'cover',
},
themeColor: '#0a0a0a',
appleWebApp: {
capable: true,
statusBarStyle: 'black-translucent',
},
openGraph: {
title: 'GridPilot - iRacing League Racing Platform',
description: 'Structure over chaos. The professional platform for iRacing league racing.',
@@ -22,8 +34,11 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="en" className="scroll-smooth">
<body className="antialiased">
<html lang="en" className="scroll-smooth overflow-x-hidden">
<head>
<meta name="mobile-web-app-capable" content="yes" />
</head>
<body className="antialiased overflow-x-hidden">
<header className="fixed top-0 left-0 right-0 z-50 bg-deep-graphite/80 backdrop-blur-sm border-b border-white/5">
<div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex items-baseline space-x-3">

View File

@@ -2,13 +2,14 @@ import { ModeGuard } from '@/components/shared/ModeGuard';
import Hero from '@/components/landing/Hero';
import AlternatingSection from '@/components/landing/AlternatingSection';
import FeatureGrid from '@/components/landing/FeatureGrid';
import EmailCapture from '@/components/landing/EmailCapture';
import DiscordCTA from '@/components/landing/DiscordCTA';
import FAQ from '@/components/landing/FAQ';
import Footer from '@/components/landing/Footer';
import CareerProgressionMockup from '@/components/mockups/CareerProgressionMockup';
import RaceHistoryMockup from '@/components/mockups/RaceHistoryMockup';
import CompanionAutomationMockup from '@/components/mockups/CompanionAutomationMockup';
import SimPlatformMockup from '@/components/mockups/SimPlatformMockup';
import MockupStack from '@/components/ui/MockupStack';
export default function HomePage() {
return (
@@ -19,25 +20,47 @@ export default function HomePage() {
{/* Section 1: A Persistent Identity */}
<AlternatingSection
heading="A Persistent Identity"
backgroundVideo="/gameplay.mp4"
description={
<>
<p>
Your races, your seasons, your progress finally in one place.
</p>
<ul className="space-y-2 mt-4">
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>Lifetime stats and season history across all your leagues</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>Track your performance, consistency, and team contributions</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>Your own rating that reflects real league competition</span>
</li>
</ul>
<div className="space-y-3 mt-4">
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-4 border border-slate-700/40 hover:border-primary-blue/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(59,130,246,0.15)]">
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-transparent via-primary-blue/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-9 h-9 rounded-lg bg-gradient-to-br from-primary-blue/20 to-blue-900/20 border border-primary-blue/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-5 h-5 text-primary-blue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-slate-200 leading-relaxed font-light">Lifetime stats and season history across all your leagues</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-4 border border-slate-700/40 hover:border-primary-blue/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(59,130,246,0.15)]">
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-transparent via-primary-blue/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-9 h-9 rounded-lg bg-gradient-to-br from-primary-blue/20 to-blue-900/20 border border-primary-blue/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-5 h-5 text-primary-blue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-slate-200 leading-relaxed font-light">Track your performance, consistency, and team contributions</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-4 border border-slate-700/40 hover:border-primary-blue/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(59,130,246,0.15)]">
<div className="absolute top-0 left-0 w-full h-0.5 bg-gradient-to-r from-transparent via-primary-blue/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-9 h-9 rounded-lg bg-gradient-to-br from-primary-blue/20 to-blue-900/20 border border-primary-blue/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-5 h-5 text-primary-blue" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M5 13l4 4L19 7" />
</svg>
</div>
<span className="text-slate-200 leading-relaxed font-light">Your own rating that reflects real league competition</span>
</div>
</div>
</div>
<p className="mt-4">
iRacing gives you physics. GridPilot gives you a career.
</p>
@@ -55,29 +78,50 @@ export default function HomePage() {
backgroundImage="/images/ff1600.jpeg"
description={
<>
<p>
<p className="text-sm md:text-base leading-relaxed">
Every race you run stays with you.
</p>
<ul className="space-y-2 mt-4">
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>Your stats, your team, your story all connected</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>One race result updates your profile, team points, rating, and season history</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>No more fragmented data across spreadsheets and forums</span>
</li>
</ul>
<p className="mt-4">
<div className="space-y-3 mt-4 md:mt-6">
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-3.5 md:p-4 border border-slate-700/40 hover:border-red-600/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(220,38,38,0.15)]">
<div className="absolute top-0 right-0 w-12 h-12 bg-gradient-to-bl from-red-600/10 to-transparent rounded-bl-3xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3">
<div className="flex-shrink-0 w-8 h-8 md:w-9 md:h-9 rounded-lg bg-gradient-to-br from-red-600/20 to-red-900/20 border border-red-600/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-4 h-4 md:w-5 md:h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
</svg>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">Your stats, your team, your story all connected</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-3.5 md:p-4 border border-slate-700/40 hover:border-red-600/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(220,38,38,0.15)]">
<div className="absolute top-0 right-0 w-12 h-12 bg-gradient-to-bl from-red-600/10 to-transparent rounded-bl-3xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3">
<div className="flex-shrink-0 w-8 h-8 md:w-9 md:h-9 rounded-lg bg-gradient-to-br from-red-600/20 to-red-900/20 border border-red-600/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-4 h-4 md:w-5 md:h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">One race result updates your profile, team points, rating, and season history</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-r from-slate-900/60 via-slate-800/40 to-slate-900/60 p-3.5 md:p-4 border border-slate-700/40 hover:border-red-600/50 transition-all duration-300 hover:shadow-[0_0_25px_rgba(220,38,38,0.15)]">
<div className="absolute top-0 right-0 w-12 h-12 bg-gradient-to-bl from-red-600/10 to-transparent rounded-bl-3xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3">
<div className="flex-shrink-0 w-8 h-8 md:w-9 md:h-9 rounded-lg bg-gradient-to-br from-red-600/20 to-red-900/20 border border-red-600/30 flex items-center justify-center shadow-lg group-hover:scale-105 transition-transform">
<svg className="w-4 h-4 md:w-5 md:h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">No more fragmented data across spreadsheets and forums</span>
</div>
</div>
</div>
<p className="mt-4 md:mt-6 text-sm md:text-base leading-relaxed">
Your racing career, finally in one place.
</p>
</>
}
mockup={<RaceHistoryMockup />}
mockup={<MockupStack index={1}><RaceHistoryMockup /></MockupStack>}
layout="text-right"
/>
@@ -86,28 +130,50 @@ export default function HomePage() {
heading="Automatic Session Creation"
description={
<>
<p>
<p className="text-sm md:text-base leading-relaxed">
Setting up league races used to mean clicking through iRacing's wizard 20 times.
</p>
<ul className="space-y-2 mt-4">
<li className="flex items-center gap-2">
<span className="text-primary-blue">•</span>
<span>Our companion app syncs with your league schedule</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue">•</span>
<span>When it's race time, it creates the iRacing session automatically</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>No clicking through wizards. No manual setup</span>
</li>
<li className="flex items-center gap-2">
<span className="text-primary-blue"></span>
<span>Runs on your machine, totally transparent, completely safe</span>
</li>
</ul>
<p className="mt-4">
<div className="space-y-3 mt-4 md:mt-6">
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-br from-slate-900/70 to-slate-800/50 p-3.5 md:p-4 border border-slate-700/50 hover:border-primary-blue/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(59,130,246,0.2)]">
<div className="absolute -top-12 -right-12 w-24 h-24 bg-gradient-to-br from-primary-blue/10 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3 relative">
<div className="flex-shrink-0 w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-primary-blue/25 to-blue-900/25 border border-primary-blue/40 flex items-center justify-center shadow-lg group-hover:shadow-primary-blue/20 group-hover:scale-110 transition-all">
<span className="text-primary-blue font-bold text-sm">1</span>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">Our companion app syncs with your league schedule</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-br from-slate-900/70 to-slate-800/50 p-3.5 md:p-4 border border-slate-700/50 hover:border-primary-blue/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(59,130,246,0.2)]">
<div className="absolute -top-12 -right-12 w-24 h-24 bg-gradient-to-br from-primary-blue/10 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3 relative">
<div className="flex-shrink-0 w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-primary-blue/25 to-blue-900/25 border border-primary-blue/40 flex items-center justify-center shadow-lg group-hover:shadow-primary-blue/20 group-hover:scale-110 transition-all">
<span className="text-primary-blue font-bold text-sm">2</span>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">When it's race time, it creates the iRacing session automatically</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-br from-slate-900/70 to-slate-800/50 p-3.5 md:p-4 border border-slate-700/50 hover:border-primary-blue/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(59,130,246,0.2)]">
<div className="absolute -top-12 -right-12 w-24 h-24 bg-gradient-to-br from-primary-blue/10 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3 relative">
<div className="flex-shrink-0 w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-primary-blue/25 to-blue-900/25 border border-primary-blue/40 flex items-center justify-center shadow-lg group-hover:shadow-primary-blue/20 group-hover:scale-110 transition-all">
<span className="text-primary-blue font-bold text-sm">3</span>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">No clicking through wizards. No manual setup</span>
</div>
</div>
<div className="group relative overflow-hidden rounded-lg bg-gradient-to-br from-slate-900/70 to-slate-800/50 p-3.5 md:p-4 border border-slate-700/50 hover:border-green-600/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(34,197,94,0.2)]">
<div className="absolute -top-12 -right-12 w-24 h-24 bg-gradient-to-br from-green-600/10 to-transparent rounded-full blur-2xl opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="flex items-start gap-2.5 md:gap-3 relative">
<div className="flex-shrink-0 w-9 h-9 md:w-10 md:h-10 rounded-xl bg-gradient-to-br from-green-600/25 to-green-900/25 border border-green-600/40 flex items-center justify-center shadow-lg group-hover:shadow-green-600/20 group-hover:scale-110 transition-all">
<svg className="w-4 h-4 md:w-5 md:h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
<span className="text-slate-200 text-sm md:text-base leading-relaxed font-light">Runs on your machine, totally transparent, completely safe</span>
</div>
</div>
</div>
<p className="mt-4 md:mt-6 text-sm md:text-base leading-relaxed">
Automation instead of repetition.
</p>
</>
@@ -122,16 +188,16 @@ export default function HomePage() {
backgroundImage="/images/lmp3.jpeg"
description={
<>
<p>
<p className="text-sm md:text-base leading-relaxed">
Right now, we're focused on making iRacing league racing better.
</p>
<p className="mt-4">
<p className="mt-4 md:mt-6 text-sm md:text-base leading-relaxed">
But sims come and go. Your leagues, your teams, your rating those stay.
</p>
<p className="mt-4">
<p className="mt-4 md:mt-6 text-sm md:text-base leading-relaxed">
GridPilot is built to outlast any single platform.
</p>
<p className="mt-4">
<p className="mt-4 md:mt-6 text-sm md:text-base leading-relaxed">
When the next sim arrives, your competitive identity moves with you.
</p>
</>
@@ -140,7 +206,7 @@ export default function HomePage() {
layout="text-right"
/>
<EmailCapture />
<DiscordCTA />
<FAQ />
<Footer />
</main>