feat: redesign /supporters with 4 sections + footer label update

- Supporters: existing Geyser leaderboard (unchanged)
- Collaborators: new section, empty placeholder with GitHub CTA
- Partners: Geyser, Angor, Zapstore, ContextVM, MKStack
- Friends: placeholder grid (5 slots) + logo spec instructions
- Footer: "Supporters" → "Collaborators & Friends"
- Static COLLABORATORS/PARTNERS/FRIENDS arrays at top of file for easy editing

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-06-27 17:51:11 +07:00
parent 5dffa5cac3
commit baf4c5e10d
2 changed files with 336 additions and 99 deletions

View file

@ -796,7 +796,7 @@ export function Layout({ children }: LayoutProps) {
to="/supporters" to="/supporters"
className="hover:text-[#ccff00] transition-colors font-medium" className="hover:text-[#ccff00] transition-colors font-medium"
> >
Supporters Collaborators &amp; Friends
</Link> </Link>
<span className="flex items-center gap-1.5"> <span className="flex items-center gap-1.5">
<span>Not sponsored by</span> <span>Not sponsored by</span>

View file

@ -2,12 +2,84 @@ import { useSeoMeta } from '@unhead/react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { Layout } from '@/components/Layout'; import { Layout } from '@/components/Layout';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { ExternalLink, Zap, Heart, Users } from 'lucide-react'; import { ExternalLink, Zap, Heart, Users, Handshake, Building2, Star } from 'lucide-react';
const GEYSER_PROJECT_URL = 'https://geyser.fund/project/skatespots?hero=satsnfacts'; const GEYSER_PROJECT_URL = 'https://geyser.fund/project/skatespots?hero=satsnfacts';
const GEYSER_LEADERBOARD_URL = 'https://geyser.fund/project/skatespots/leaderboard'; const GEYSER_LEADERBOARD_URL = 'https://geyser.fund/project/skatespots/leaderboard';
const GEYSER_API_URL = 'https://api.geyser.fund/graphql'; const GEYSER_API_URL = 'https://api.geyser.fund/graphql';
// ─── Static data — edit these arrays to add/remove entries ──────────────────
interface Collaborator {
name: string;
role: string;
avatar?: string; // URL or leave blank for placeholder
npub?: string; // Nostr npub — links to their profile
url?: string; // fallback link
}
interface Partner {
name: string;
logo?: string; // URL to logo (SVG/PNG transparent preferred)
url: string;
description: string;
}
interface Friend {
name: string;
logo?: string; // URL to logo (SVG/PNG transparent preferred, min 200×200px)
url: string;
tagline?: string;
}
const COLLABORATORS: Collaborator[] = [
// Add collaborators here — mix of Nostr users and external people
// Example:
// { name: 'Alice', role: 'Developer', npub: 'npub1...', avatar: 'https://...' },
// { name: 'Bob', role: 'Relay Operator', url: 'https://...' },
];
const PARTNERS: Partner[] = [
{
name: 'Geyser',
logo: 'https://geyser.fund/logo-brand.svg',
url: 'https://geyser.fund/project/skatespots',
description: 'Bitcoin Lightning crowdfunding',
},
{
name: 'Angor',
logo: '',
url: 'https://angor.io/',
description: 'Bitcoin crowdfunding platform',
},
{
name: 'Zapstore',
logo: '',
url: 'https://zapstore.dev/apps/org.skatespots.app',
description: 'Open-source app distribution',
},
{
name: 'ContextVM',
logo: '',
url: 'https://contextvm.org/s/e3564bb877adb0c99a7e4973ccb22e516d7d9eca0027d0bd62a04174ed670530',
description: 'Nostr AI infrastructure',
},
{
name: 'MKStack',
logo: '',
url: 'https://soapbox.pub/mkstack',
description: 'Nostr client framework',
},
];
const FRIENDS: Friend[] = [
// Add friends here once logos are ready
// Format: { name: 'Example', logo: 'https://example.com/logo.png', url: 'https://example.com', tagline: 'Short description' }
// Logo requirements: PNG/WebP with transparent background, min 200×200px square preferred
];
// ─── Helpers ─────────────────────────────────────────────────────────────────
interface GeyserContributor { interface GeyserContributor {
user: { user: {
id: string; id: string;
@ -15,8 +87,7 @@ interface GeyserContributor {
imageUrl?: string; imageUrl?: string;
heroLink?: string; heroLink?: string;
}; };
amount: number; // sats amount: number;
funder?: boolean;
} }
function formatSats(sats: number): string { function formatSats(sats: number): string {
@ -38,12 +109,7 @@ function useGeyserLeaderboard() {
projectGet(where: { name: "skatespots" }) { projectGet(where: { name: "skatespots" }) {
funders(input: { orderBy: { amountFunded: desc }, pagination: { take: 100 } }) { funders(input: { orderBy: { amountFunded: desc }, pagination: { take: 100 } }) {
amountFunded amountFunded
user { user { id username imageUrl heroLink }
id
username
imageUrl
heroLink
}
} }
} }
}`, }`,
@ -59,34 +125,63 @@ function useGeyserLeaderboard() {
return []; return [];
} }
}, },
staleTime: 5 * 60 * 1000, // 5 min cache staleTime: 5 * 60 * 1000,
retry: false, retry: false,
}); });
} }
// ─── Section header ───────────────────────────────────────────────────────────
function SectionHeader({
icon: Icon,
title,
subtitle,
color = '#ccff00',
}: {
icon: React.ComponentType<{ className?: string }>;
title: string;
subtitle?: string;
color?: string;
}) {
return (
<div className="mb-6 md:mb-8">
<div className="flex items-center gap-3 mb-2">
<Icon className="h-5 w-5 flex-shrink-0" style={{ color }} />
<h2 className="text-2xl md:text-3xl font-black uppercase tracking-wide" style={{ color }}>
{title}
</h2>
<div className="flex-1 h-px" style={{ background: `linear-gradient(to right, ${color}40, transparent)` }} />
</div>
{subtitle && <p className="text-gray-400 text-sm ml-8">{subtitle}</p>}
</div>
);
}
// ─── Supporter card (Geyser contributors) ────────────────────────────────────
function SupporterCard({ contributor }: { contributor: GeyserContributor }) { function SupporterCard({ contributor }: { contributor: GeyserContributor }) {
const isAnon = !contributor.user.username || contributor.user.username.toLowerCase().startsWith('anon'); const isAnon = !contributor.user.username || contributor.user.username.toLowerCase().startsWith('anon');
const profileUrl = contributor.user.heroLink || `https://geyser.fund/user/${contributor.user.id}`; const profileUrl = contributor.user.heroLink || `https://geyser.fund/user/${contributor.user.id}`;
const card = ( const card = (
<div className="flex flex-col items-center gap-2 p-4 rounded-xl bg-black/60 border border-white/10 hover:border-[#ccff00]/40 transition-all group min-w-0"> <div className="flex flex-col items-center gap-2 p-4 rounded-xl bg-black/60 border border-white/10 hover:border-[#ccff00]/40 transition-all group">
<div className="relative"> <div className="relative">
{contributor.user.imageUrl ? ( {contributor.user.imageUrl ? (
<img <img
src={contributor.user.imageUrl} src={contributor.user.imageUrl}
alt={contributor.user.username || 'Anon'} alt={contributor.user.username || 'Anon'}
className="w-14 h-14 rounded-full object-cover border-2 border-white/10 group-hover:border-[#ccff00]/50 transition-all" className="w-12 h-12 rounded-full object-cover border-2 border-white/10 group-hover:border-[#ccff00]/50 transition-all"
/> />
) : ( ) : (
<div className="w-14 h-14 rounded-full bg-gradient-to-br from-[#ff0040]/20 to-[#ccff00]/20 border-2 border-white/10 flex items-center justify-center text-2xl"> <div className="w-12 h-12 rounded-full bg-gradient-to-br from-[#ff0040]/20 to-[#ccff00]/20 border-2 border-white/10 flex items-center justify-center text-xl">
🛹 🛹
</div> </div>
)} )}
<div className="absolute -bottom-1 -right-1 bg-[#f7931a] rounded-full w-5 h-5 flex items-center justify-center"> <div className="absolute -bottom-1 -right-1 bg-[#f7931a] rounded-full w-4 h-4 flex items-center justify-center">
<Zap className="h-2.5 w-2.5 text-white" /> <Zap className="h-2 w-2 text-white" />
</div> </div>
</div> </div>
<p className="text-xs font-bold text-center text-white truncate max-w-full"> <p className="text-xs font-bold text-center text-white truncate max-w-full w-full text-center">
{isAnon ? 'Anon 🛹' : contributor.user.username} {isAnon ? 'Anon 🛹' : contributor.user.username}
</p> </p>
<p className="text-[10px] font-bold text-[#f7931a]"> <p className="text-[10px] font-bold text-[#f7931a]">
@ -96,35 +191,141 @@ function SupporterCard({ contributor }: { contributor: GeyserContributor }) {
); );
if (isAnon) return card; if (isAnon) return card;
return <a href={profileUrl} target="_blank" rel="noopener noreferrer">{card}</a>;
}
// ─── Collaborator card ────────────────────────────────────────────────────────
function CollaboratorCard({ collab }: { collab: Collaborator }) {
const href = collab.npub
? `/${collab.npub}`
: collab.url;
const inner = (
<div className="flex items-center gap-3 p-4 rounded-xl bg-black/60 border border-white/10 hover:border-[#ff0040]/40 transition-all group">
<div className="relative flex-shrink-0">
{collab.avatar ? (
<img
src={collab.avatar}
alt={collab.name}
className="w-12 h-12 rounded-full object-cover border-2 border-white/10 group-hover:border-[#ff0040]/50 transition-all"
/>
) : (
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-[#ff0040]/20 to-[#ff6600]/20 border-2 border-white/10 flex items-center justify-center text-xl">
🛹
</div>
)}
</div>
<div className="flex-1 min-w-0">
<p className="font-bold text-sm text-white truncate">{collab.name}</p>
<p className="text-xs text-[#ff0040]/80 font-medium">{collab.role}</p>
</div>
{href && <ExternalLink className="h-3.5 w-3.5 text-gray-600 group-hover:text-[#ff0040] flex-shrink-0 transition-colors" />}
</div>
);
if (!href) return inner;
if (collab.npub) return <Link to={`/${collab.npub}`}>{inner}</Link>;
return <a href={href} target="_blank" rel="noopener noreferrer">{inner}</a>;
}
// ─── Partner card ─────────────────────────────────────────────────────────────
function PartnerCard({ partner }: { partner: Partner }) {
return ( return (
<a href={profileUrl} target="_blank" rel="noopener noreferrer"> <a
{card} href={partner.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 p-4 rounded-xl bg-black/60 border border-white/10 hover:border-[#ff6600]/50 hover:bg-[#ff6600]/5 transition-all group"
>
<div className="w-10 h-10 rounded-lg bg-white/5 border border-white/10 flex items-center justify-center flex-shrink-0 overflow-hidden">
{partner.logo ? (
<img
src={partner.logo}
alt={partner.name}
className="w-7 h-7 object-contain brightness-0 invert opacity-70 group-hover:opacity-100 transition-opacity"
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }}
/>
) : (
<Building2 className="h-5 w-5 text-gray-600 group-hover:text-[#ff6600] transition-colors" />
)}
</div>
<div className="flex-1 min-w-0">
<p className="font-bold text-sm text-white group-hover:text-[#ff6600] transition-colors">{partner.name}</p>
<p className="text-xs text-gray-500 truncate">{partner.description}</p>
</div>
<ExternalLink className="h-3.5 w-3.5 text-gray-600 group-hover:text-[#ff6600] flex-shrink-0 transition-colors" />
</a> </a>
); );
} }
// ─── Friend card ──────────────────────────────────────────────────────────────
function FriendCard({ friend }: { friend: Friend }) {
return (
<a
href={friend.url}
target="_blank"
rel="noopener noreferrer"
className="flex flex-col items-center gap-3 p-5 rounded-xl bg-black/60 border border-white/10 hover:border-[#ccff00]/40 hover:bg-[#ccff00]/5 transition-all group text-center"
>
<div className="w-16 h-16 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center overflow-hidden group-hover:border-[#ccff00]/40 transition-all">
{friend.logo ? (
<img
src={friend.logo}
alt={friend.name}
className="w-12 h-12 object-contain"
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }}
/>
) : (
<span className="text-2xl">🛹</span>
)}
</div>
<div>
<p className="font-bold text-sm text-white group-hover:text-[#ccff00] transition-colors">{friend.name}</p>
{friend.tagline && <p className="text-xs text-gray-500 mt-0.5">{friend.tagline}</p>}
</div>
</a>
);
}
// ─── Placeholder card (empty slot) ───────────────────────────────────────────
function PlaceholderCard({ label }: { label: string }) {
return (
<div className="flex flex-col items-center gap-3 p-5 rounded-xl border border-dashed border-white/10 text-center opacity-40">
<div className="w-16 h-16 rounded-2xl border border-dashed border-white/20 flex items-center justify-center">
<span className="text-2xl">?</span>
</div>
<p className="text-xs text-gray-600 font-medium">{label}</p>
</div>
);
}
// ─── Main page ────────────────────────────────────────────────────────────────
export default function SupportersPage() { export default function SupportersPage() {
useSeoMeta({ useSeoMeta({
title: 'Supporters - SkateSpots', title: 'Supporters - SkateSpots',
description: 'The community keeping SkateSpots alive. Powered by Geyser and Bitcoin Lightning.', description: 'The community keeping SkateSpots alive — supporters, collaborators, partners and friends.',
}); });
const { data: contributors = [], isLoading } = useGeyserLeaderboard(); const { data: contributors = [], isLoading } = useGeyserLeaderboard();
return ( return (
<Layout> <Layout>
<div className="container max-w-4xl px-4 py-12 md:py-16"> <div className="container max-w-4xl px-4 py-12 md:py-16 space-y-16">
{/* Hero */} {/* ── Hero ── */}
<div className="text-center mb-14"> <div className="text-center">
<div className="text-6xl mb-4">⚡</div> <div className="text-6xl mb-4">⚡</div>
<h1 className="text-5xl md:text-6xl font-black uppercase mb-3 leading-none"> <h1 className="text-5xl md:text-6xl font-black uppercase mb-3 leading-none">
<span className="neon-red">OUR</span>{' '} <span className="neon-red">OUR</span>{' '}
<span className="neon-lime">SUPPORTERS</span> <span className="neon-lime">COMMUNITY</span>
</h1> </h1>
<p className="text-gray-400 max-w-lg mx-auto text-base mb-6"> <p className="text-gray-400 max-w-lg mx-auto text-base mb-8">
SkateSpots is community-powered. These skaters and Bitcoiners help keep the project free, decentralized, and built for the streets. SkateSpots is built by and for the community. Supporters, collaborators, partners and friends keeping this free and decentralized.
</p> </p>
<a <a
href={GEYSER_PROJECT_URL} href={GEYSER_PROJECT_URL}
@ -138,26 +339,22 @@ export default function SupportersPage() {
</a> </a>
</div> </div>
{/* Individuals section */} {/* ── 1. Supporters ── */}
<section className="mb-16"> <section>
<div className="flex items-center gap-3 mb-6"> <SectionHeader
<Users className="h-5 w-5 text-[#ccff00]" /> icon={Zap}
<h2 className="text-2xl font-black uppercase"> title="Supporters"
<span className="neon-lime">Plebs</span> subtitle="Bitcoin contributors via Geyser — ordered by cumulative sats."
</h2> color="#f7931a"
<div className="flex-1 h-px bg-gradient-to-r from-[#ccff00]/30 to-transparent" /> />
</div>
<p className="text-gray-400 text-sm mb-6">
Every sat counts — ordered by cumulative contributions.
</p>
{isLoading ? ( {isLoading ? (
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-3"> <div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-3">
{Array.from({ length: 12 }).map((_, i) => ( {Array.from({ length: 12 }).map((_, i) => (
<div key={i} className="flex flex-col items-center gap-2 p-4 rounded-xl bg-black/40 border border-white/5 animate-pulse"> <div key={i} className="flex flex-col items-center gap-2 p-4 rounded-xl bg-black/40 border border-white/5 animate-pulse">
<div className="w-14 h-14 rounded-full bg-white/10" /> <div className="w-12 h-12 rounded-full bg-white/10" />
<div className="h-3 w-16 bg-white/10 rounded" /> <div className="h-3 w-14 bg-white/10 rounded" />
<div className="h-3 w-12 bg-white/5 rounded" /> <div className="h-3 w-10 bg-white/5 rounded" />
</div> </div>
))} ))}
</div> </div>
@ -171,53 +368,116 @@ export default function SupportersPage() {
<div className="text-center py-12 rounded-2xl border border-dashed border-white/10"> <div className="text-center py-12 rounded-2xl border border-dashed border-white/10">
<div className="text-4xl mb-3">🛹</div> <div className="text-4xl mb-3">🛹</div>
<p className="text-gray-500 mb-2">Be the first supporter!</p> <p className="text-gray-500 mb-2">Be the first supporter!</p>
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">The leaderboard updates once contributions come in.</p>
The leaderboard will update once contributions come in.
</p>
</div> </div>
)} )}
{/* Geyser leaderboard link */} <div className="mt-6 flex flex-wrap items-center justify-between gap-4">
<div className="mt-6 text-center">
<a <a
href={GEYSER_LEADERBOARD_URL} href={GEYSER_LEADERBOARD_URL}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-full border-2 border-[#ccff00]/30 text-[#ccff00] font-bold text-sm hover:bg-[#ccff00]/10 transition-all" className="inline-flex items-center gap-2 px-5 py-2.5 rounded-full border-2 border-[#f7931a]/30 text-[#f7931a] font-bold text-sm hover:bg-[#f7931a]/10 transition-all"
> >
<ExternalLink className="h-4 w-4" /> <ExternalLink className="h-4 w-4" />
Full Leaderboard on Geyser Full Leaderboard on Geyser
</a> </a>
</div> <div className="flex items-center gap-2 text-xs text-gray-500">
<span>Powered by</span>
{/* Powered by Geyser */} <a href={GEYSER_PROJECT_URL} target="_blank" rel="noopener noreferrer"
<div className="mt-8 flex items-center justify-center gap-3"> className="font-bold text-gray-400 hover:text-white transition-colors">
<span className="text-xs text-gray-500">Powered by</span> Geyser ⚡
<a </a>
href={GEYSER_PROJECT_URL} </div>
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 hover:opacity-80 transition-opacity"
>
<img
src="https://geyser.fund/logo-brand.svg"
alt="Geyser"
className="h-5 brightness-0 invert opacity-60 hover:opacity-90 transition-opacity"
onError={(e) => {
(e.target as HTMLImageElement).style.display = 'none';
}}
/>
<span className="text-sm font-bold text-gray-400 hover:text-white transition-colors">Geyser</span>
</a>
</div> </div>
</section> </section>
{/* Mission section */} {/* ── 2. Collaborators ── */}
<section className="mb-16"> <section>
<SectionHeader
icon={Users}
title="Collaborators"
subtitle="People actively building SkateSpots — developers, designers, relay operators."
color="#ff0040"
/>
{COLLABORATORS.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{COLLABORATORS.map((c) => (
<CollaboratorCard key={c.name} collab={c} />
))}
</div>
) : (
<div className="rounded-xl border border-dashed border-[#ff0040]/20 p-8 text-center">
<Users className="h-10 w-10 mx-auto mb-3 text-[#ff0040]/30" />
<p className="text-gray-500 text-sm mb-1">Want to collaborate?</p>
<p className="text-xs text-gray-600 mb-4">Developers, designers, skaters with skills — reach out.</p>
<a
href="https://github.com/soapbox-pub/mkstack"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-xs font-bold text-[#ff0040]/70 hover:text-[#ff0040] transition-colors"
>
<ExternalLink className="h-3 w-3" />
View on GitHub
</a>
</div>
)}
</section>
{/* ── 3. Partners ── */}
<section>
<SectionHeader
icon={Handshake}
title="Partners"
subtitle="Organizations and projects we work closely with."
color="#ff6600"
/>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{PARTNERS.map((p) => (
<PartnerCard key={p.name} partner={p} />
))}
</div>
</section>
{/* ── 4. Friends ── */}
<section>
<SectionHeader
icon={Star}
title="Friends"
subtitle="Aligned projects and people who share our values."
color="#ccff00"
/>
{FRIENDS.length > 0 ? (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3">
{FRIENDS.map((f) => (
<FriendCard key={f.name} friend={f} />
))}
</div>
) : (
<>
{/* Placeholder grid — remove once real entries are added */}
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3 mb-4">
{Array.from({ length: 5 }).map((_, i) => (
<PlaceholderCard key={i} label="Coming soon" />
))}
</div>
<div className="rounded-xl border border-dashed border-[#ccff00]/20 p-6 text-center">
<p className="text-gray-500 text-sm mb-1">Want to be listed here?</p>
<p className="text-xs text-gray-600">
Send a logo (PNG/WebP, transparent background, min 200×200px) and your URL via DM on Nostr.
</p>
</div>
</>
)}
</section>
{/* ── Mission ── */}
<section>
<div className="relative rounded-2xl overflow-hidden border-2 border-[#ff0040]/30 bg-black/80 p-8 md:p-10"> <div className="relative rounded-2xl overflow-hidden border-2 border-[#ff0040]/30 bg-black/80 p-8 md:p-10">
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#ff0040] via-[#ff6600] to-[#ccff00]" /> <div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#ff0040] via-[#ff6600] to-[#ccff00]" />
<div className="absolute -top-16 -right-16 w-48 h-48 bg-[#ff0040] rounded-full opacity-5 blur-3xl pointer-events-none" />
<div className="flex justify-center mb-5"> <div className="flex justify-center mb-5">
<Heart className="h-10 w-10 text-[#ff0040]" /> <Heart className="h-10 w-10 text-[#ff0040]" />
</div> </div>
@ -226,11 +486,9 @@ export default function SupportersPage() {
</h2> </h2>
<p className="text-gray-400 text-sm text-center max-w-2xl mx-auto mb-6 leading-relaxed"> <p className="text-gray-400 text-sm text-center max-w-2xl mx-auto mb-6 leading-relaxed">
SkateSpots is 100% free, open-source, and built on Nostr — a decentralized protocol with no central server to shut it down. SkateSpots is 100% free, open-source, and built on Nostr — a decentralized protocol with no central server to shut it down.
We don't run ads, sell your data, or answer to investors. Keeping the lights on and building new features relies entirely on the skate community. No ads, no data selling, no investors. Every sat goes directly toward development, relay hosting, and keeping this free for every skater on earth.
Every sat you contribute goes directly toward development, relay hosting, and keeping this tool free for every skater on earth.
</p> </p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-6">
{[ {[
{ emoji: '🛹', title: 'Nostr-native', desc: 'No central server. Your data lives on relays you choose.' }, { emoji: '🛹', title: 'Nostr-native', desc: 'No central server. Your data lives on relays you choose.' },
{ emoji: '⚡', title: 'Bitcoin powered', desc: 'Earn sats for mapping spots. Spend them at local shops.' }, { emoji: '⚡', title: 'Bitcoin powered', desc: 'Earn sats for mapping spots. Spend them at local shops.' },
@ -246,29 +504,7 @@ export default function SupportersPage() {
</div> </div>
</section> </section>
{/* Payment options */} {/* ── Footer ── */}
<section className="mb-12">
<h2 className="text-xl font-black uppercase mb-5 flex items-center gap-2">
<Zap className="h-5 w-5 text-[#f7931a]" />
<span>Send Sats</span>
</h2>
<div className="p-5 rounded-xl border-2 border-[#ff0040]/20 bg-black/60 flex flex-col justify-between gap-3">
<p className="text-xs font-bold uppercase text-gray-400 tracking-widest">Via Geyser</p>
<p className="text-sm text-gray-400">
Contribute via the Geyser campaign — your name goes on the leaderboard above.
</p>
<a
href={GEYSER_PROJECT_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-sm font-bold text-[#ccff00] hover:text-[#ccff00]/80 transition-colors"
>
Open campaign <ExternalLink className="h-3.5 w-3.5" />
</a>
</div>
</section>
{/* Footer links */}
<div className="border-t border-gray-800 pt-8 text-center space-y-2 text-sm text-gray-500"> <div className="border-t border-gray-800 pt-8 text-center space-y-2 text-sm text-gray-500">
<p>Thank you. Every sat keeps the spots on the map. 🛹⚡</p> <p>Thank you. Every sat keeps the spots on the map. 🛹⚡</p>
<p> <p>
@ -279,6 +515,7 @@ export default function SupportersPage() {
<Link to="/faq" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">FAQ</Link> <Link to="/faq" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">FAQ</Link>
</p> </p>
</div> </div>
</div> </div>
</Layout> </Layout>
); );