SkateSpots/src/pages/Index.tsx
SkateSpots b2a6108eeb refactor: split non-component exports (react-refresh) into their own modules
Removes the last react-refresh disables by splitting exports instead:
- contexts/language.ts: Language/Translations types + LanguageContext +
  useTranslation/useT (LanguageProvider stays a component-only file); 12
  importers repointed.
- lib/sortOptions.ts: calculatePopularity + calculateDistance (out of SortDropdown)
- lib/trustFilter.ts: TrustFilterLevel + filterByTrustLevel (out of TrustFilter)
- EventCountdown: deleted the unused useEventStatus hook
- NewsletterPage: dropped the NEWSLETTER_ISSUES re-export shim
- AGENTS.md updated (lint stays clean with no blanket disables)

Gate: 101 tests, 0 errors/warnings, boot smoke green.
2026-09-26 23:29:42 +07:00

650 lines
27 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useMemo } from 'react';
import { useSeoMeta } from '@unhead/react';
import { Link } from 'react-router-dom';
import { Layout } from '@/components/Layout';
import { SpotCard, SpotCardSkeleton } from '@/components/SpotCard';
import { EventCard, EventCardSkeleton } from '@/components/EventCard';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { LoginArea } from '@/components/auth/LoginArea';
import { useSkateSpots } from '@/hooks/useSkateSpots';
import { useUpcomingSkateEvents } from '@/hooks/useSkateEvents';
import { useLabProfiles, type LabProfile } from '@/hooks/useLabProfiles';
import { useZapLeaderboard } from '@/hooks/useZapLeaderboard';
import { SKATE_SPOT_KIND, type SkateSpot } from '@/lib/types';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useAuthor } from '@/hooks/useAuthor';
import { useTranslation } from '@/contexts/language';
import { genUserName } from '@/lib/genUserName';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import {
MapPin,
Calendar,
Plus,
ChevronRight,
Sparkles,
CheckCircle2,
GraduationCap,
Video,
Crown,
Zap,
} from 'lucide-react';
import { FounderFollow } from '@/components/FounderFollow';
import { SponsorSlot } from '@/components/SponsorSlot';
function HeroSection() {
const { user } = useCurrentUser();
const { t } = useTranslation();
return (
<section className="relative overflow-hidden bg-black">
{/* Neon background glows */}
<div className="absolute top-0 right-0 w-[600px] h-[600px] bg-[#ff0040] opacity-20 rounded-full blur-[150px]" />
<div className="absolute bottom-0 left-0 w-[500px] h-[500px] bg-[#ccff00] opacity-15 rounded-full blur-[120px]" />
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] h-[400px] bg-[#ff6600] opacity-10 rounded-full blur-[100px]" />
<div className="relative container px-3 xs:px-4 sm:px-6 md:px-8 py-10 xs:py-14 sm:py-20 md:py-28">
<div className="max-w-4xl mx-auto text-center">
<div className="mb-6 xs:mb-8">
<div className="inline-flex items-center px-3 xs:px-6 py-1.5 xs:py-2 rounded-full border-2 border-[#ff0040]/50 bg-black/50 backdrop-blur-sm mb-4 xs:mb-6">
<span className="font-condensed text-[#ff0040] uppercase tracking-[0.2em] text-sm xs:text-base" style={{ fontWeight: 600 }}>
<Sparkles className="inline h-3 w-3 xs:h-4 xs:w-4 mr-1 xs:mr-2" />
{t('home.tagline')}
</span>
</div>
{/* Main headline — Bebas Neue split into two lines for visual impact */}
<h1 className="font-display mb-4 xs:mb-5 leading-[0.88] tracking-wide">
<span className="block text-[clamp(4rem,18vw,10rem)] gradient-text drop-shadow-[0_2px_24px_rgba(255,0,64,0.4)]">
SKATE
</span>
<span className="block text-[clamp(4rem,18vw,10rem)] text-white drop-shadow-[0_2px_24px_rgba(204,255,0,0.25)]">
SPOTS
</span>
</h1>
{/* Subtitle — Barlow Condensed, bold and spaced */}
<p className="font-condensed font-700 text-[clamp(0.85rem,3.5vw,1.35rem)] text-gray-100 mb-2 md:mb-3 max-w-2xl mx-auto uppercase tracking-[0.18em] px-2 xs:px-4"
style={{ fontWeight: 700 }}>
{t('home.subtitle')}
</p>
<p className="font-condensed text-[clamp(0.75rem,2.5vw,1rem)] text-gray-400 max-w-lg mx-auto px-2 xs:px-4 mb-6 xs:mb-8 tracking-wide italic">
{t('home.description')}
</p>
<div className="flex flex-wrap items-center justify-center gap-2 mb-6 xs:mb-8 px-2">
<div className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-yellow-500/20 border border-yellow-500/50">
<span className="text-yellow-400 font-bold text-xs uppercase">
⚠️ {t('home.betaWarning')}
</span>
</div>
<div className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-blue-500/20 border border-blue-500/50">
<span className="text-blue-400 font-bold text-xs uppercase">
📱 {t('home.pwaHint')}
</span>
</div>
</div>
</div>
<div className="flex flex-col xs:flex-row items-stretch xs:items-center justify-center gap-2 xs:gap-3 sm:gap-4 mb-6 px-2 xs:px-4">
<Link to="/map" className="w-full xs:w-auto">
<Button
size="lg"
className="w-full xs:w-auto gap-2 bg-[#ff0040] hover:bg-[#ff0040]/90 text-white border-2 border-[#ff0040] shadow-[0_0_20px_rgba(255,0,64,0.5)] font-bold uppercase px-5 sm:px-8 py-3 xs:py-4 sm:py-6 text-sm sm:text-base h-auto"
>
<MapPin className="h-4 w-4 sm:h-5 sm:w-5 flex-shrink-0" />
{t('home.exploreMap')}
</Button>
</Link>
{user && (
<Link to="/create-spot" className="w-full xs:w-auto">
<Button
size="lg"
variant="outline"
className="w-full xs:w-auto gap-2 border-2 border-[#ccff00] bg-transparent hover:bg-[#ccff00]/10 text-[#ccff00] shadow-[0_0_15px_rgba(204,255,0,0.3)] font-bold uppercase px-5 sm:px-8 py-3 xs:py-4 sm:py-6 text-sm sm:text-base h-auto"
>
<Plus className="h-4 w-4 sm:h-5 sm:w-5 flex-shrink-0" />
{t('home.addSpot')}
</Button>
</Link>
)}
</div>
{/* Login/Signup Area - Prominent for logged out users (desktop only, mobile has it in header) */}
{!user && (
<div className="px-4 mt-6 hidden md:block">
<div className="max-w-md mx-auto p-5 sm:p-6 rounded-2xl bg-black/80 backdrop-blur-lg border-2 border-[#ccff00]/50 shadow-[0_0_30px_rgba(204,255,0,0.15)]">
<p className="text-center text-gray-200 mb-5 text-sm sm:text-base font-medium">
Join the community to add spots, create events & more
</p>
<LoginArea className="flex flex-row gap-3 w-full justify-center" />
</div>
</div>
)}
</div>
</div>
</section>
);
}
function FeaturedSpots() {
const { data: spots, isLoading } = useSkateSpots(8);
const { t } = useTranslation();
return (
<section className="bg-black py-16 border-t border-[#ff0040]/30">
<div className="container px-4">
<div className="flex items-center justify-between mb-10">
<div>
<h2 className="font-display text-4xl md:text-5xl neon-red mb-1 tracking-wide">
{t('home.featuredSpots')}
</h2>
<p className="font-condensed text-gray-400 uppercase text-sm tracking-[0.15em]" style={{ fontWeight: 600 }}>
{t('home.featuredSpotsDesc')}
</p>
</div>
<Link to="/map">
<Button variant="ghost" className="gap-2 text-[#ff0040] hover:text-[#ff0040]/80 hover:bg-[#ff0040]/10">
{t('common.viewAll')}
<ChevronRight className="h-4 w-4" />
</Button>
</Link>
</div>
{isLoading ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 stagger-children">
{Array.from({ length: 4 }).map((_, i) => (
<SpotCardSkeleton key={i} />
))}
</div>
) : spots && spots.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 stagger-children">
{spots.slice(0, 4).map((spot) => (
<SpotCard key={spot.id} spot={spot} />
))}
</div>
) : (
<Card className="border-2 border-dashed border-[#ff0040]/30 bg-black/50">
<CardContent className="py-12 text-center">
<MapPin className="h-12 w-12 mx-auto mb-4 text-[#ff0040]/50" />
<p className="text-gray-400 mb-4 uppercase font-bold tracking-wide">
{t('home.noSpotsYet')}
</p>
<Link to="/create-spot">
<Button className="gap-2 bg-[#ff0040] hover:bg-[#ff0040]/90 font-bold uppercase">
<Plus className="h-4 w-4" />
{t('home.addSpot')}
</Button>
</Link>
</CardContent>
</Card>
)}
</div>
</section>
);
}
function UpcomingEvents() {
const { data: events, isLoading } = useUpcomingSkateEvents(4);
const { t } = useTranslation();
return (
<section className="bg-black py-16 border-t border-[#ccff00]/30">
<div className="container px-4">
<div className="flex items-center justify-between mb-10">
<div>
<h2 className="font-display text-4xl md:text-5xl neon-lime mb-1 tracking-wide">
{t('home.upcomingEvents')}
</h2>
<p className="font-condensed text-gray-400 uppercase text-sm tracking-[0.15em]" style={{ fontWeight: 600 }}>
{t('home.upcomingEventsDesc')}
</p>
</div>
<Link to="/events">
<Button variant="ghost" className="gap-2 text-[#ccff00] hover:text-[#ccff00]/80 hover:bg-[#ccff00]/10">
{t('common.viewAll')}
<ChevronRight className="h-4 w-4" />
</Button>
</Link>
</div>
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
{Array.from({ length: 4 }).map((_, i) => (
<EventCardSkeleton key={i} compact />
))}
</div>
) : events && events.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
{events.map((event) => (
<EventCard key={event.id} event={event} compact />
))}
</div>
) : (
<Card className="border-2 border-dashed border-[#ccff00]/30 bg-black/50">
<CardContent className="py-12 text-center">
<Calendar className="h-12 w-12 mx-auto mb-4 text-[#ccff00]/50" />
<p className="text-gray-400 mb-4 uppercase font-bold tracking-wide">
{t('home.noEventsYet')}
</p>
<Link to="/create-event">
<Button className="gap-2 bg-[#ccff00] text-black hover:bg-[#ccff00]/90 font-bold uppercase">
<Plus className="h-4 w-4" />
{t('home.createEvent')}
</Button>
</Link>
</CardContent>
</Card>
)}
</div>
</section>
);
}
function TopZappedSection() {
// Last 30 days of zap activity, aggregated once.
const since = useMemo(() => Math.floor(Date.now() / 1000) - 30 * 24 * 60 * 60, []);
const { data } = useZapLeaderboard(since);
const { data: spots } = useSkateSpots(200);
const spotByCoord = useMemo(() => {
const map = new Map<string, SkateSpot>();
for (const s of spots ?? []) {
map.set(`${SKATE_SPOT_KIND}:${s.pubkey}:${s.identifier}`, s);
}
return map;
}, [spots]);
// Only spots we can actually link to, so every row lands somewhere real.
const top = useMemo(
() =>
(data?.targets ?? [])
.filter((t) => t.key.startsWith(`${SKATE_SPOT_KIND}:`) && spotByCoord.has(t.key))
.slice(0, 4),
[data, spotByCoord],
);
// Never render an empty section on the home page.
if (top.length === 0) return null;
return (
<section className="bg-black py-16 border-t border-[#ccff00]/30">
<div className="container px-4">
<div className="flex items-center justify-between mb-8">
<div>
<h2 className="font-display text-4xl md:text-5xl neon-lime mb-1 tracking-wide">
TOP ZAPPED
</h2>
<p className="font-condensed text-gray-400 uppercase text-sm tracking-[0.15em]" style={{ fontWeight: 600 }}>
Most supported spots this month
</p>
</div>
<Link to="/leaderboards">
<Button variant="ghost" className="gap-2 text-[#ccff00] hover:text-[#ccff00]/80 hover:bg-[#ccff00]/10">
Leaderboard
<ChevronRight className="h-4 w-4" />
</Button>
</Link>
</div>
<div className="space-y-2">
{top.map((t, i) => {
const spot = spotByCoord.get(t.key)!;
const isShop = spot.types.includes('shop');
return (
<Link
key={t.key}
to={isShop ? `/shop/${spot.pubkey}/${spot.identifier}` : `/spot/${spot.pubkey}/${spot.identifier}`}
className="block"
>
<div className="flex items-center gap-3 rounded-xl border-2 border-white/10 bg-black/60 px-3 py-2.5 transition-colors hover:border-[#ccff00]/60">
<span className="flex w-6 items-center justify-center text-sm font-bold text-gray-500">
{i === 0 ? <Crown className="h-5 w-5 text-yellow-400" /> : `#${i + 1}`}
</span>
<div className="min-w-0 flex-1">
<p className="truncate font-bold uppercase tracking-wide text-white">{spot.title}</p>
{spot.location && <p className="truncate text-xs text-gray-500">{spot.location}</p>}
</div>
<div className="flex items-center gap-1 font-bold text-[#ccff00]">
<Zap className="h-4 w-4" />
{t.sats.toLocaleString()}
</div>
</div>
</Link>
);
})}
</div>
</div>
</section>
);
}
function TheLabSection() {
const { data: profiles, isLoading } = useLabProfiles(100);
const { t } = useTranslation();
// Get 2 coaches and 2 filmers
const coaches = profiles?.filter(p => p.profileType === 'coach').slice(0, 2) || [];
const filmers = profiles?.filter(p => p.profileType === 'filmer').slice(0, 2) || [];
const displayProfiles = [...coaches, ...filmers];
return (
<section className="bg-black py-16 border-t border-[#ff6600]/30">
<div className="container px-4">
<div className="flex items-center justify-between mb-10">
<div>
<h2 className="font-display text-4xl md:text-5xl mb-1 tracking-wide">
<span className="neon-red">THE</span> <span className="neon-orange">LAB</span>
</h2>
<p className="font-condensed text-gray-400 uppercase text-sm tracking-[0.15em]" style={{ fontWeight: 600 }}>
{t('home.theLabDesc')}
</p>
</div>
<Link to="/lab">
<Button variant="ghost" className="gap-2 text-[#ff6600] hover:text-[#ff6600]/80 hover:bg-[#ff6600]/10">
{t('common.viewAll')}
<ChevronRight className="h-4 w-4" />
</Button>
</Link>
</div>
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
{Array.from({ length: 4 }).map((_, i) => (
<Card key={i} className="bg-black/90 border-2 border-[#ff6600]/20">
<CardContent className="p-6">
<div className="flex items-start gap-4">
{/* Avatar skeleton */}
<div className="h-16 w-16 rounded-full bg-gray-800 animate-pulse flex-shrink-0" />
<div className="flex-1 min-w-0 space-y-3">
{/* Name skeleton */}
<div className="flex items-center gap-2">
<div className="h-5 w-28 bg-gray-800 animate-pulse rounded" />
<div className="h-5 w-5 bg-gray-800 animate-pulse rounded" />
</div>
{/* Location skeleton */}
<div className="h-3 w-24 bg-gray-800 animate-pulse rounded" />
{/* Bio skeleton */}
<div className="space-y-1.5">
<div className="h-3 w-full bg-gray-800 animate-pulse rounded" />
<div className="h-3 w-3/4 bg-gray-800 animate-pulse rounded" />
</div>
{/* Skills skeleton */}
<div className="flex gap-1.5">
<div className="h-5 w-14 bg-gray-800 animate-pulse rounded-full" />
<div className="h-5 w-16 bg-gray-800 animate-pulse rounded-full" />
<div className="h-5 w-12 bg-gray-800 animate-pulse rounded-full" />
</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
) : displayProfiles.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
{displayProfiles.map((profile) => (
<LabProfileCard key={profile.id} profile={profile} />
))}
</div>
) : (
<Card className="border-2 border-dashed border-[#ff6600]/30 bg-black/50">
<CardContent className="py-12 text-center">
<GraduationCap className="h-12 w-12 mx-auto mb-4 text-[#ff6600]/50" />
<p className="text-gray-400 mb-4 uppercase font-bold tracking-wide">
{t('lab.noCoaches')}
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<Link to="/become/coach">
<Button className="gap-2 bg-red-500 hover:bg-red-600 font-bold uppercase">
<GraduationCap className="h-4 w-4" />
{t('home.becomeCoach')}
</Button>
</Link>
<Link to="/become/filmer">
<Button className="gap-2 bg-orange-500 hover:bg-orange-600 font-bold uppercase">
<Video className="h-4 w-4" />
{t('home.becomeFilmer')}
</Button>
</Link>
</div>
</CardContent>
</Card>
)}
</div>
</section>
);
}
function LabProfileCard({ profile }: { profile: LabProfile }) {
const author = useAuthor(profile.pubkey);
const metadata = author.data?.metadata;
const displayName = metadata?.name ?? genUserName(profile.pubkey);
const avatar = profile.image || metadata?.picture;
const isCoach = profile.profileType === 'coach';
const accentColor = isCoach ? '#ff0040' : '#ff6600';
return (
<Link to={`/coach/${profile.pubkey}/${profile.identifier}`}>
<Card className="bg-black/90 border-2 hover:shadow-2xl transition-all h-full backdrop-blur" style={{
borderColor: `${accentColor}40`,
}}>
<CardContent className="p-6">
<div className="flex items-start gap-4">
<Avatar className="h-16 w-16 border-2" style={{ borderColor: `${accentColor}50` }}>
<AvatarImage src={avatar} alt={displayName} />
<AvatarFallback className="text-lg font-bold" style={{
backgroundColor: `${accentColor}20`,
color: accentColor
}}>
{displayName.slice(0, 2).toUpperCase()}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h3 className="font-black text-lg uppercase truncate" style={{ color: accentColor }}>
{displayName}
</h3>
{isCoach ? (
<GraduationCap className="h-5 w-5 flex-shrink-0" style={{ color: accentColor }} />
) : (
<Video className="h-5 w-5 flex-shrink-0" style={{ color: accentColor }} />
)}
</div>
{profile.location && (
<p className="text-sm text-gray-400 flex items-center gap-1 mb-2">
<MapPin className="h-3 w-3" style={{ color: accentColor }} />
{profile.location}
</p>
)}
{profile.bio && (
<p className="text-sm text-gray-300 line-clamp-2 mb-3">
{profile.bio}
</p>
)}
{profile.skills.length > 0 && (
<div className="flex flex-wrap gap-1">
{profile.skills.slice(0, 3).map((skill: string) => (
<Badge key={skill} variant="secondary" className="text-xs bg-gray-800 border-gray-700">
{skill}
</Badge>
))}
{profile.skills.length > 3 && (
<Badge variant="secondary" className="text-xs bg-gray-800 border-gray-700">
+{profile.skills.length - 3}
</Badge>
)}
</div>
)}
</div>
</div>
</CardContent>
</Card>
</Link>
);
}
function NewToNostrSection() {
const { user } = useCurrentUser();
// Don't show this section if user is already logged in
if (user) return null;
return (
<section className="bg-black py-12 md:py-20 border-t border-[#ccff00]/30">
<div className="container px-4">
<div className="relative overflow-hidden rounded-2xl border-2 border-[#ccff00]/50 bg-black/50 backdrop-blur">
{/* Neon glow */}
<div className="absolute inset-0 bg-[#ccff00]/5 blur-xl" />
<div className="relative py-12 md:py-16 px-4 md:px-12 text-center">
<h2 className="font-display text-5xl md:text-7xl neon-lime mb-4 tracking-wide">
NEW HERE?
</h2>
<p className="text-gray-300 max-w-2xl mx-auto mb-6 md:mb-8 text-base md:text-lg px-4">
Create a free account to add spots, join events, connect with skaters, and become part of the community. No email required.
</p>
{/* Founder Follow - prominent placement */}
<div className="flex items-center justify-center gap-6 mb-8">
<div className="flex flex-col items-center">
<FounderFollow size="lg" showBadge={true} />
</div>
<div className="text-left max-w-xs hidden sm:block">
<div className="flex items-center gap-2 text-[#ff6600] font-bold text-sm mb-1">
<Crown className="h-4 w-4" />
Follow the Founder
</div>
<p className="text-gray-400 text-xs">
Hold the avatar for 3 seconds to follow and stay updated on new features and events!
</p>
</div>
</div>
<Link to="/onboarding">
<Button size="lg" className="gap-2 bg-[#ccff00] text-black hover:bg-[#ccff00]/90 border-2 border-[#ccff00] shadow-[0_0_20px_rgba(204,255,0,0.5)] font-bold uppercase px-10 py-6 text-lg">
<CheckCircle2 className="h-6 w-6" />
Create Account
</Button>
</Link>
<p className="text-gray-500 text-xs mt-4">
Your account works across all apps in the network
</p>
</div>
</div>
</div>
</section>
);
}
function CTASection() {
const { t } = useTranslation();
return (
<section className="bg-black py-20 border-t border-[#ff6600]/30">
<div className="container px-4">
<div className="relative overflow-hidden rounded-2xl border-2 border-[#ff6600]/50">
{/* Neon glows */}
<div className="absolute top-0 right-0 w-96 h-96 bg-[#ff0040] opacity-20 rounded-full blur-[120px]" />
<div className="absolute bottom-0 left-0 w-96 h-96 bg-[#ccff00] opacity-15 rounded-full blur-[100px]" />
<div className="relative py-16 px-6 md:px-12 text-center bg-black/50 backdrop-blur">
<h2 className="font-display text-5xl md:text-7xl neon-orange mb-4 tracking-wide">
{t('home.readyToShare')}
</h2>
<p className="text-gray-300 max-w-2xl mx-auto mb-8 text-lg">
{t('home.readyToShareDesc')}
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<Link to="/create-spot">
<Button size="lg" className="gap-2 bg-[#ff0040] hover:bg-[#ff0040]/90 text-white border-2 border-[#ff0040] shadow-[0_0_20px_rgba(255,0,64,0.5)] font-bold uppercase px-8">
<Plus className="h-5 w-5" />
{t('home.addSpot')}
</Button>
</Link>
<Link to="/create-event">
<Button size="lg" variant="outline" className="gap-2 border-2 border-[#ccff00] bg-transparent hover:bg-[#ccff00]/10 text-[#ccff00] shadow-[0_0_15px_rgba(204,255,0,0.3)] font-bold uppercase px-8">
<Calendar className="h-5 w-5" />
{t('home.createEvent')}
</Button>
</Link>
</div>
</div>
</div>
</div>
</section>
);
}
function Footer() {
const { t } = useTranslation();
return (
<footer className="border-t border-[#ff0040]/30 bg-black">
<div className="container px-4 py-10">
<div className="flex flex-col md:flex-row items-center justify-between gap-6">
<div className="flex items-center gap-3">
<img
src="/images/icon-1024.png"
alt="SkateSpots"
className="w-10 h-10 rounded-lg shadow-[0_0_20px_rgba(255,0,64,0.5)]"
/>
<span className="font-display text-2xl neon-red tracking-wide" style={{
textShadow: '0 0 10px #ff0040'
}}>SkateSpots</span>
</div>
<p className="text-sm text-gray-400 text-center uppercase tracking-wide">
Open Source • Community Owned • No Ads
</p>
<div className="flex items-center gap-6 text-sm">
<Link to="/faq" className="text-gray-400 hover:text-[#ff0040] transition-colors uppercase font-bold tracking-wide">
FAQ
</Link>
<Link to="/about" className="text-gray-400 hover:text-[#ff0040] transition-colors uppercase font-bold tracking-wide">
{t('nav.about')}
</Link>
</div>
</div>
</div>
</footer>
);
}
export default function Index() {
useSeoMeta({
title: 'SkateSpots - Discover Skateboarding Locations Worldwide',
description:
'Find the best skateboarding spots, connect with fellow skaters, and discover events near you. Built on Nostr for true decentralization.',
});
// Platform sponsors - currently empty (invisible placeholder)
// When sponsors are added, pass them here: sponsors={[{name: "Vans", logo: "...", tier: "title"}]}
const platformSponsors = undefined;
return (
<Layout>
<HeroSection />
<FeaturedSpots />
<TopZappedSection />
<UpcomingEvents />
<TheLabSection />
<NewToNostrSection />
<CTASection />
{/* Sponsor slot - invisible until sponsors are added */}
<SponsorSlot
size="banner"
sponsors={platformSponsors}
title="Platform Partners"
/>
<Footer />
</Layout>
);
}