ui(support): compact tabbed support page with friendlier /support alias

This commit is contained in:
SkateSpots 2026-10-30 17:13:25 +07:00
parent a1b069da13
commit f2787d79cf
3 changed files with 289 additions and 308 deletions

View file

@ -194,6 +194,8 @@ export function AppRouter() {
<Route path="/license" element={<LicensePage />} /> <Route path="/license" element={<LicensePage />} />
<Route path="/privacy" element={<PrivacyPage />} /> <Route path="/privacy" element={<PrivacyPage />} />
<Route path="/supporters" element={<SupportersPage />} /> <Route path="/supporters" element={<SupportersPage />} />
{/* Friendly alias: people naturally type /support. */}
<Route path="/support" element={<Navigate to="/supporters" replace />} />
<Route path="/donate" element={<DonatePage />} /> <Route path="/donate" element={<DonatePage />} />
{/* Marketplace */} {/* Marketplace */}
<Route path="/marketplace-listings" element={<MarketplacePage />} /> <Route path="/marketplace-listings" element={<MarketplacePage />} />

View file

@ -1,11 +1,20 @@
import { useTranslation } from '@/contexts/language'; import { useTranslation } from '@/contexts/language';
import { cn } from '@/lib/utils';
/** /**
* "Ways to support" — three purpose-first cards; each scrolls to its rail by id * "Ways to support" — three purpose-first cards; each scrolls to its rail by id
* (#support-lightning / #support-silent / #support-angor), so it works anywhere * (#support-lightning / #support-silent / #support-angor), so it works anywhere
* those anchors exist (/supporters and /donate). * those anchors exist (/supporters and /donate).
*/ */
export function WaysToSupport({ className }: { className?: string }) { export function WaysToSupport({
className,
active,
onSelect,
}: {
className?: string;
active?: string;
onSelect?: (id: string) => void;
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const ways = [ const ways = [
{ id: 'support-lightning', emoji: '⚡', title: t('walletUi.supporters.wayLightningTitle'), desc: t('walletUi.supporters.wayLightningDesc') }, { id: 'support-lightning', emoji: '⚡', title: t('walletUi.supporters.wayLightningTitle'), desc: t('walletUi.supporters.wayLightningDesc') },
@ -13,16 +22,29 @@ export function WaysToSupport({ className }: { className?: string }) {
{ id: 'support-angor', emoji: '⛓', title: t('walletUi.supporters.wayAngorTitle'), desc: t('walletUi.supporters.wayAngorDesc') }, { id: 'support-angor', emoji: '⛓', title: t('walletUi.supporters.wayAngorTitle'), desc: t('walletUi.supporters.wayAngorDesc') },
]; ];
return ( return (
<div className={`grid grid-cols-1 gap-3 sm:grid-cols-3 ${className ?? ''}`}> <div className={`grid grid-cols-3 gap-2 ${className ?? ''}`}>
{ways.map((w) => ( {ways.map((w) => (
<button <button
key={w.id} key={w.id}
onClick={() => document.getElementById(w.id)?.scrollIntoView({ behavior: 'smooth', block: 'start' })} type="button"
className="min-h-0 min-w-0 rounded-xl border border-white/10 bg-black/60 p-4 text-left transition-colors hover:border-[#ccff00]/40 hover:bg-white/5" aria-pressed={active === w.id}
onClick={() => {
if (onSelect) {
onSelect(w.id);
return;
}
document.getElementById(w.id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}}
className={cn(
'min-h-0 min-w-0 rounded-xl border p-2.5 text-left transition-colors sm:p-3',
active === w.id
? 'border-[#ccff00]/60 bg-[#ccff00]/10'
: 'border-white/10 bg-black/60 hover:border-[#ccff00]/40 hover:bg-white/5',
)}
> >
<span className="text-2xl">{w.emoji}</span> <span className="text-xl">{w.emoji}</span>
<p className="mt-2 text-sm font-black uppercase text-white">{w.title}</p> <p className="mt-1 text-[10px] font-black uppercase leading-tight text-white sm:mt-2 sm:text-xs">{w.title}</p>
<p className="mt-1 text-xs text-gray-400">{w.desc}</p> <p className="mt-0.5 hidden text-[10px] leading-snug text-gray-400 sm:block">{w.desc}</p>
</button> </button>
))} ))}
</div> </div>

View file

@ -20,6 +20,7 @@ import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
// Only count support received from this point (the project's public launch era). // Only count support received from this point (the project's public launch era).
@ -27,7 +28,7 @@ import { Button } from '@/components/ui/button';
// platform zap is already ≥ 2026-01, so this doesn't currently trim the list — // platform zap is already ≥ 2026-01, so this doesn't currently trim the list —
// the visible cap below is what keeps it short. // the visible cap below is what keeps it short.
const SUPPORTERS_SINCE = Math.floor(Date.UTC(2025, 11, 1) / 1000); // 2025-12-01 const SUPPORTERS_SINCE = Math.floor(Date.UTC(2025, 11, 1) / 1000); // 2025-12-01
const VISIBLE_LIMIT = 21; const VISIBLE_LIMIT = 12;
/** npub → hex pubkey (useAuthor queries relays by HEX authors). */ /** npub → hex pubkey (useAuthor queries relays by HEX authors). */
function npubToHex(npub?: string): string | undefined { function npubToHex(npub?: string): string | undefined {
@ -261,15 +262,15 @@ function SectionHeader({
color?: string; color?: string;
}) { }) {
return ( return (
<div className="mb-6 md:mb-8"> <div className="mb-3 md:mb-4">
<div className="flex items-center gap-3 mb-2"> <div className="mb-1.5 flex items-center gap-2.5">
<Icon className="h-5 w-5 flex-shrink-0" style={{ color }} /> <Icon className="h-4 w-4 flex-shrink-0" style={{ color }} />
<h2 className="text-2xl md:text-3xl font-black uppercase tracking-wide" style={{ color }}> <h2 className="text-lg font-black uppercase tracking-wide md:text-xl" style={{ color }}>
{title} {title}
</h2> </h2>
<div className="flex-1 h-px" style={{ background: `linear-gradient(to right, ${color}40, transparent)` }} /> <div className="h-px flex-1" style={{ background: `linear-gradient(to right, ${color}40, transparent)` }} />
</div> </div>
{subtitle && <p className="text-gray-400 text-sm ml-8">{subtitle}</p>} {subtitle && <p className="ml-7 text-xs text-gray-400">{subtitle}</p>}
</div> </div>
); );
} }
@ -547,19 +548,6 @@ function SupporterRow({ rank, entry }: { rank: number; entry: SupporterEntry })
); );
} }
// ─── 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 ──────────────────────────────────────────────────────────────── // ─── Main page ────────────────────────────────────────────────────────────────
export default function SupportersPage() { export default function SupportersPage() {
@ -571,6 +559,8 @@ export default function SupportersPage() {
const totalUsd = useSatsToUsd(allSats); const totalUsd = useSatsToUsd(allSats);
const { data: stats } = useGrowthStats(); const { data: stats } = useGrowthStats();
const [showAll, setShowAll] = useState(false); const [showAll, setShowAll] = useState(false);
const [tab, setTab] = useState<'support' | 'backers' | 'community' | 'grants'>('support');
const [rail, setRail] = useState<'support-lightning' | 'support-silent' | 'support-angor'>('support-lightning');
// One ranked wall: Angor backers + Lightning zaps to the project, merged by npub. // One ranked wall: Angor backers + Lightning zaps to the project, merged by npub.
const supporters = useMemo<SupporterEntry[]>(() => { const supporters = useMemo<SupporterEntry[]>(() => {
@ -652,317 +642,284 @@ export default function SupportersPage() {
return ( return (
<> <>
<div className="container max-w-4xl px-4 py-12 md:py-16 space-y-16"> <div className="container max-w-4xl space-y-6 px-4 py-8 md:py-10">
{/* ── Hero ── */} {/* ── Hero ── */}
<div className="text-center"> <div className="text-center">
<div className="text-6xl mb-4">⚡</div> <h1 className="text-4xl font-black uppercase leading-none md:text-5xl">
<h1 className="text-5xl md:text-6xl font-black uppercase mb-3 leading-none">
<span className="neon-red">{t('walletUi.supporters.our')}</span>{' '} <span className="neon-red">{t('walletUi.supporters.our')}</span>{' '}
<span className="neon-lime">{t('walletUi.supporters.community')}</span> <span className="neon-lime">{t('walletUi.supporters.community')}</span>
</h1> </h1>
<p className="text-gray-400 max-w-lg mx-auto text-base mb-8"> <p className="mx-auto mt-2 max-w-xl text-sm text-gray-400">
{t('walletUi.supporters.heroDesc')} {t('walletUi.supporters.heroDesc')}
</p> </p>
<a <div className="mt-5 flex flex-wrap items-center justify-center gap-2">
href={ANGOR_PROJECT_URL} <a
target="_blank" href={ANGOR_PROJECT_URL}
rel="noopener noreferrer" target="_blank"
className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-gradient-to-r from-[#ff0040] to-[#ff6600] text-white font-black uppercase text-sm hover:opacity-90 transition-all shadow-lg shadow-[#ff0040]/30" rel="noopener noreferrer"
> className="inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-[#ff0040] to-[#ff6600] px-5 py-2.5 text-xs font-black uppercase text-white shadow-lg shadow-[#ff0040]/25 transition-opacity hover:opacity-90"
<Zap className="h-4 w-4" /> >
{t('walletUi.supporters.backAngor')} <Zap className="h-4 w-4" />
<ExternalLink className="h-3.5 w-3.5 opacity-70" /> {t('walletUi.supporters.backAngor')}
</a> <ExternalLink className="h-3 w-3 opacity-70" />
<Link </a>
to="/donate" <Link
className="mt-5 inline-flex items-center gap-1.5 text-xs font-bold text-[#ccff00]/80 transition-colors hover:text-[#ccff00]" to="/donate"
> className="inline-flex items-center gap-1.5 rounded-full border border-white/15 px-4 py-2.5 text-xs font-bold text-[#ccff00]/80 transition-colors hover:border-[#ccff00]/40 hover:text-[#ccff00]"
{t('walletUi.supporters.donatePageLink')} → >
</Link> {t('walletUi.supporters.donatePageLink')} →
</Link>
</div>
<div className="mt-4 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-[11px] text-gray-500">
<span>{supporters.length.toLocaleString()} {t('walletUi.supporters.backers')}</span>
<span aria-hidden="true">·</span>
<span>{allSats.toLocaleString()} sats</span>
{typeof totalUsd === 'number' && (
<>
<span aria-hidden="true">·</span>
<span>≈ ${totalUsd.toFixed(2)}</span>
</>
)}
</div>
</div> </div>
{/* ── 1. Support ── */} {/* ── Compact navigation ── */}
<section> <Tabs
<SectionHeader value={tab}
icon={Zap} onValueChange={(value) => setTab(value as 'support' | 'backers' | 'community' | 'grants')}
title={t('walletUi.supporters.support')} >
subtitle={t('walletUi.supporters.supportDesc')} <TabsList className="grid h-auto w-full grid-cols-4 gap-1 rounded-2xl border border-white/10 bg-black/70 p-1">
color="#f7931a" {[
/> { value: 'support', label: t('walletUi.supporters.support') },
{ value: 'backers', label: t('walletUi.supporters.backers') },
<WaysToSupport className="mb-4" /> { value: 'community', label: t('walletUi.supporters.community') },
{ value: 'grants', label: t('walletUi.supporters.grantsSection') },
{/* Lightning rail — the in-app way to chip in (zap publicly or pay quietly). */} ].map((item) => (
<div id="support-lightning" className="scroll-mt-24"> <TabsTrigger
<SupportSkateSpots className="mt-4" /> key={item.value}
</div> value={item.value}
className="h-auto min-h-10 whitespace-normal rounded-xl px-1.5 py-2 text-center text-[9px] font-black uppercase leading-tight tracking-wide data-[state=active]:bg-[#ccff00] data-[state=active]:text-black data-[state=active]:shadow-none sm:px-2 sm:text-[11px]"
{/* Silent payments — private on-chain (and Lightning) via a BIP-353 name. */}
<div id="support-silent" className="scroll-mt-24">
<SilentPaymentsCard className="mt-4" />
</div>
<div id="support-angor" className="scroll-mt-24">
<AngorProjectCard count={supporters.length} className="mt-4" />
</div>
</section>
{/* ── 2. Bitcoin grantmakers ── */}
<section>
<SectionHeader
icon={Handshake}
title={t('walletUi.supporters.grantsSection')}
subtitle={t('walletUi.supporters.grantsSectionDesc')}
color="#f7931a"
/>
<GrantmakerForm />
</section>
{/* ── 3. What your support pays for (words only) ── */}
<section>
<SectionHeader
icon={Heart}
title={t('walletUi.supporters.paysFor')}
subtitle={t('walletUi.supporters.paysForDesc')}
color="#22d3ee"
/>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{PAYS_FOR.map(({ icon: Icon, key, desc }) => (
<div
key={key}
className="flex items-start gap-3 rounded-xl border border-white/10 bg-black/60 px-4 py-3"
> >
<Icon className="mt-0.5 h-4 w-4 flex-shrink-0 text-[#22d3ee]" /> {item.label}
<div className="min-w-0"> </TabsTrigger>
<span className="text-sm font-medium text-white">{t(key)}</span>
<p className="text-[11px] text-gray-500">{t(desc)}</p>
</div>
</div>
))} ))}
</div> </TabsList>
</section>
{/* ── 3. Impact so far (what the community built) ── */} {/* ── Support rails ── */}
{impact.length > 0 && ( <TabsContent value="support" className="space-y-4 pt-4">
<section> <div>
<SectionHeader <h2 className="text-xl font-black uppercase text-white">{t('walletUi.supporters.support')}</h2>
icon={TrendingUp} <p className="mt-0.5 text-sm text-gray-400">{t('walletUi.supporters.donateDesc')}</p>
title={t('walletUi.supporters.impact')} </div>
subtitle={t('walletUi.supporters.impactDesc')} <WaysToSupport active={rail} onSelect={(id) => setRail(id as typeof rail)} />
color="#ccff00" <div id={rail} className="scroll-mt-24">
/> {rail === 'support-lightning' && <SupportSkateSpots />}
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3"> {rail === 'support-silent' && <SilentPaymentsCard />}
{impact.map(({ key, value }) => ( {rail === 'support-angor' && <AngorProjectCard count={supporters.length} />}
<div key={key} className="rounded-xl border border-white/10 bg-black/60 px-4 py-3 text-center">
<div className="text-2xl font-black text-[#ccff00]">{value.toLocaleString()}</div>
<div className="text-xs text-gray-400">{t(key)}</div>
</div>
))}
</div> </div>
<p className="mt-4 text-center text-xs text-gray-500">
<Link to="/stats" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">
{t('walletUi.supporters.impactSeeStats')}
</Link>
{' · '}
{t('walletUi.supporters.impactAsOf')}
</p>
</section>
)}
{/* ── 4. Supporters (Angor + Lightning zaps) ── */} <section className="pt-1">
<section> <SectionHeader
<SectionHeader icon={Heart}
icon={Zap} title={t('walletUi.supporters.paysFor')}
title={t('walletUi.supporters.backers')} subtitle={t('walletUi.supporters.paysForDesc')}
subtitle={t('walletUi.supporters.backersDesc')} color="#22d3ee"
color="#22d3ee" />
/> <div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
{PAYS_FOR.map(({ icon: Icon, key, desc }) => (
{supporters.length > 0 ? ( <div key={key} className="flex items-start gap-2 rounded-xl border border-white/10 bg-black/60 px-3 py-2">
<> <Icon className="mt-0.5 h-3.5 w-3.5 flex-shrink-0 text-[#22d3ee]" />
<div className="flex flex-col gap-2"> <div className="min-w-0">
{(showAll ? supporters : supporters.slice(0, VISIBLE_LIMIT)).map((entry, i) => ( <span className="text-xs font-medium text-white">{t(key)}</span>
<SupporterRow key={entry.key} rank={i + 1} entry={entry} /> <p className="text-[10px] leading-snug text-gray-500">{t(desc)}</p>
</div>
</div>
))} ))}
</div> </div>
{supporters.length > VISIBLE_LIMIT && ( </section>
<div className="mt-3 text-center"> </TabsContent>
<button
onClick={() => setShowAll((v) => !v)}
className="min-h-0 min-w-0 text-xs font-bold uppercase tracking-wide text-[#22d3ee]/80 transition-colors hover:text-[#22d3ee]"
>
{showAll
? t('walletUi.supporters.showLess')
: t('walletUi.supporters.showAll', { count: supporters.length })}
</button>
</div>
)}
<p className="mt-4 text-center text-sm text-gray-400"> {/* ── Backers, impact and tiers ── */}
{t('walletUi.supporters.supportersTotals', { <TabsContent value="backers" className="space-y-5 pt-4">
count: supporters.length, {impact.length > 0 && (
angor: BACKERS_TOTAL_SATS.toLocaleString(), <section>
zap: zapTotal.toLocaleString(), <SectionHeader
})} icon={TrendingUp}
{typeof totalUsd === 'number' && ( title={t('walletUi.supporters.impact')}
<span className="text-gray-500"> · ≈ ${totalUsd.toFixed(2)}</span> subtitle={t('walletUi.supporters.impactDesc')}
)} color="#ccff00"
</p> />
<p className="mt-2 text-center text-xs text-gray-600"> <div className="grid grid-cols-3 gap-2 sm:grid-cols-6">
{t('walletUi.supporters.quietNote')} {impact.map(({ key, value }) => (
</p> <div key={key} className="rounded-xl border border-white/10 bg-black/60 px-2 py-2.5 text-center">
{latestAt > 0 && ( <div className="text-lg font-black text-[#ccff00]">{value.toLocaleString()}</div>
<p className="mt-1 text-center text-[11px] text-gray-600"> <div className="truncate text-[10px] text-gray-400">{t(key)}</div>
{t('walletUi.supporters.updatedAt', { </div>
when: formatDistanceToNow(new Date(latestAt * 1000), { addSuffix: true }), ))}
})} </div>
<p className="mt-2 text-center text-[11px] text-gray-500">
<Link to="/stats" className="text-[#ccff00]/70 underline underline-offset-2 hover:text-[#ccff00]">
{t('walletUi.supporters.impactSeeStats')}
</Link>
{' · '}
{t('walletUi.supporters.impactAsOf')}
</p> </p>
</section>
)}
<section>
<SectionHeader
icon={Zap}
title={t('walletUi.supporters.backers')}
subtitle={t('walletUi.supporters.backersDesc')}
color="#22d3ee"
/>
{supporters.length > 0 ? (
<>
<div className="flex flex-col gap-1.5">
{(showAll ? supporters : supporters.slice(0, VISIBLE_LIMIT)).map((entry, i) => (
<SupporterRow key={entry.key} rank={i + 1} entry={entry} />
))}
</div>
{supporters.length > VISIBLE_LIMIT && (
<div className="mt-2 text-center">
<button
type="button"
onClick={() => setShowAll((v) => !v)}
className="min-h-0 min-w-0 text-[11px] font-bold uppercase tracking-wide text-[#22d3ee]/80 transition-colors hover:text-[#22d3ee]"
>
{showAll
? t('walletUi.supporters.showLess')
: t('walletUi.supporters.showAll', { count: supporters.length })}
</button>
</div>
)}
<p className="mt-3 text-center text-xs text-gray-400">
{t('walletUi.supporters.supportersTotals', {
count: supporters.length,
angor: BACKERS_TOTAL_SATS.toLocaleString(),
zap: zapTotal.toLocaleString(),
})}
{typeof totalUsd === 'number' && <span className="text-gray-500"> · ≈ ${totalUsd.toFixed(2)}</span>}
</p>
<p className="mt-1 text-center text-[11px] text-gray-600">{t('walletUi.supporters.quietNote')}</p>
{latestAt > 0 && (
<p className="mt-0.5 text-center text-[10px] text-gray-600">
{t('walletUi.supporters.updatedAt', { when: formatDistanceToNow(new Date(latestAt * 1000), { addSuffix: true }) })}
</p>
)}
<TierLadder supporters={supporters} />
</>
) : (
<div className="rounded-xl border border-dashed border-[#22d3ee]/20 p-6 text-center">
<Zap className="mx-auto mb-2 h-8 w-8 text-[#22d3ee]/30" />
<p className="mb-1 text-sm text-gray-500">{t('walletUi.supporters.beFirstBacker')}</p>
<a href={ANGOR_PROJECT_URL} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1.5 text-xs font-bold text-[#22d3ee]/80 hover:text-[#22d3ee]">
<ExternalLink className="h-3 w-3" /> {t('walletUi.supporters.backAngor')}
</a>
</div>
)} )}
<TierLadder supporters={supporters} /> </section>
</> </TabsContent>
) : (
<div className="rounded-xl border border-dashed border-[#22d3ee]/20 p-8 text-center">
<Zap className="h-10 w-10 mx-auto mb-3 text-[#22d3ee]/30" />
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.beFirstBacker')}</p>
<a
href={ANGOR_PROJECT_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 text-xs font-bold text-[#22d3ee]/80 hover:text-[#22d3ee] transition-colors"
>
<ExternalLink className="h-3 w-3" />
{t('walletUi.supporters.backAngor')}
</a>
</div>
)}
</section>
{/* ── 6. Collaborators ── */} {/* ── Community, mission, collaborators, partners, friends ── */}
<section> <TabsContent value="community" className="space-y-6 pt-4">
<SectionHeader <section className="rounded-2xl border border-[#ff0040]/25 bg-black/70 p-5">
icon={Users} <div className="flex items-center justify-center gap-2">
title={t('walletUi.supporters.collaborators')} <Heart className="h-5 w-5 text-[#ff0040]" />
subtitle={t('walletUi.supporters.collaboratorsDesc')} <h2 className="text-center text-lg font-black uppercase text-white">
color="#ff0040" {t('walletUi.supporters.whyTitle1')}{' '}
/> <span className="neon-red">{t('walletUi.supporters.whyTitle2')}</span>
</h2>
{COLLABORATORS.length > 0 ? ( </div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <p className="mx-auto mt-2 max-w-2xl text-center text-xs leading-relaxed text-gray-400">
{COLLABORATORS.map((c) => ( {t('walletUi.supporters.whyDesc')}
<CollaboratorCard key={c.name} collab={c} /> </p>
))} <div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
</div> {[
) : ( { emoji: '🛹', title: t('walletUi.supporters.why1Title'), desc: t('walletUi.supporters.why1Desc') },
<div className="rounded-xl border border-dashed border-[#ff0040]/20 p-8 text-center"> { emoji: '⚡', title: t('walletUi.supporters.why2Title'), desc: t('walletUi.supporters.why2Desc') },
<Users className="h-10 w-10 mx-auto mb-3 text-[#ff0040]/30" /> { emoji: '🌍', title: t('walletUi.supporters.why3Title'), desc: t('walletUi.supporters.why3Desc') },
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.wantCollaborate')}</p> ].map(({ emoji, title, desc }) => (
<p className="text-xs text-gray-600 mb-4">{t('walletUi.supporters.wantCollaborateDesc')}</p> <div key={title} className="rounded-xl border border-white/10 bg-white/[0.03] p-3 text-center">
<a <div className="text-2xl">{emoji}</div>
href="https://nostrhub.io/naddr1qvzqqqrhnypzqg88e9f588nx7wyz4wt0ura23w3yd4hmztqrkr95utwyvgachjepqyd8wumn8ghj7emfwsh8x6rpddjhxur9v9ex2tnyd9uj7qq22d4kzar92dcx7arnhyhdwn" <p className="mt-1 text-xs font-bold">{title}</p>
target="_blank" <p className="mt-0.5 text-[11px] text-gray-500">{desc}</p>
rel="noopener noreferrer" </div>
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" />
{t('walletUi.supporters.viewNostrHub')}
</a>
</div>
)}
</section>
{/* ── 7. Partners ── */}
<section>
<SectionHeader
icon={Handshake}
title={t('walletUi.supporters.partners')}
subtitle={t('walletUi.supporters.partnersDesc')}
color="#ff6600"
/>
{PARTNERS.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{PARTNERS.map((p) => (
<PartnerCard key={p.name} partner={p} />
))}
</div>
) : (
<>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 mb-4">
{Array.from({ length: 4 }).map((_, i) => (
<PlaceholderCard key={i} label={t('walletUi.supporters.comingSoon')} />
))} ))}
</div> </div>
<div className="rounded-xl border border-dashed border-[#ff6600]/20 p-6 text-center"> </section>
<p className="text-gray-500 text-sm">{t('walletUi.supporters.wantPartner')}</p>
</div>
</>
)}
</section>
{/* ── 8. Friends ── */} <section>
<section> <SectionHeader
<SectionHeader icon={Users}
icon={Star} title={t('walletUi.supporters.collaborators')}
title={t('walletUi.supporters.friends')} subtitle={t('walletUi.supporters.collaboratorsDesc')}
subtitle={t('walletUi.supporters.friendsDesc')} color="#ff0040"
color="#ccff00" />
/> {COLLABORATORS.length > 0 ? (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3"> {COLLABORATORS.map((c) => <CollaboratorCard key={c.name} collab={c} />)}
{FRIENDS.map((f) => (
<FriendCard key={f.name} friend={f} />
))}
{/* Fill remaining slots up to 5 with placeholders */}
{FRIENDS.length < 5 && Array.from({ length: 5 - FRIENDS.length }).map((_, i) => (
<PlaceholderCard key={`ph-${i}`} label={t('walletUi.supporters.comingSoon')} />
))}
</div>
<div className="mt-4 rounded-xl border border-dashed border-[#ccff00]/20 p-5 text-center">
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.wantListed')}</p>
<p className="text-xs text-gray-600">
{t('walletUi.supporters.wantListedDesc')}
</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="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#ff0040] via-[#ff6600] to-[#ccff00]" />
<div className="flex justify-center mb-5">
<Heart className="h-10 w-10 text-[#ff0040]" />
</div>
<h2 className="text-2xl font-black uppercase text-center mb-3">
{t('walletUi.supporters.whyTitle1')} <span className="neon-red">{t('walletUi.supporters.whyTitle2')}</span>
</h2>
<p className="text-gray-400 text-sm text-center max-w-2xl mx-auto mb-6 leading-relaxed">
{t('walletUi.supporters.whyDesc')}
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
{[
{ emoji: '🛹', title: t('walletUi.supporters.why1Title'), desc: t('walletUi.supporters.why1Desc') },
{ emoji: '⚡', title: t('walletUi.supporters.why2Title'), desc: t('walletUi.supporters.why2Desc') },
{ emoji: '🌍', title: t('walletUi.supporters.why3Title'), desc: t('walletUi.supporters.why3Desc') },
].map(({ emoji, title, desc }) => (
<div key={title} className="p-4 rounded-xl bg-white/5 border border-white/10 text-center">
<div className="text-3xl mb-2">{emoji}</div>
<p className="font-bold text-sm mb-1">{title}</p>
<p className="text-xs text-gray-500">{desc}</p>
</div> </div>
))} ) : (
</div> <div className="rounded-xl border border-dashed border-[#ff0040]/20 p-5 text-center">
</div> <Users className="mx-auto mb-2 h-8 w-8 text-[#ff0040]/30" />
</section> <p className="mb-1 text-sm text-gray-500">{t('walletUi.supporters.wantCollaborate')}</p>
<p className="mb-3 text-xs text-gray-600">{t('walletUi.supporters.wantCollaborateDesc')}</p>
<a href="https://nostrhub.io/naddr1qvzqqqrhnypzqg88e9f588nx7wyz4wt0ura23w3yd4hmztqrkr95utwyvgachjepqyd8wumn8ghj7emfwsh8x6rpddjhxur9v9ex2tnyd9uj7qq22d4kzar92dcx7arnhyhdwn" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1.5 text-xs font-bold text-[#ff0040]/70 hover:text-[#ff0040]">
<ExternalLink className="h-3 w-3" /> {t('walletUi.supporters.viewNostrHub')}
</a>
</div>
)}
</section>
<section>
<SectionHeader icon={Handshake} title={t('walletUi.supporters.partners')} subtitle={t('walletUi.supporters.partnersDesc')} color="#ff6600" />
{PARTNERS.length > 0 ? (
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
{PARTNERS.map((p) => <PartnerCard key={p.name} partner={p} />)}
</div>
) : (
<div className="rounded-xl border border-dashed border-[#ff6600]/20 p-5 text-center text-sm text-gray-500">
{t('walletUi.supporters.wantPartner')}
</div>
)}
</section>
<section>
<SectionHeader icon={Star} title={t('walletUi.supporters.friends')} subtitle={t('walletUi.supporters.friendsDesc')} color="#ccff00" />
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
{FRIENDS.map((f) => <FriendCard key={f.name} friend={f} />)}
</div>
<div className="mt-3 rounded-xl border border-dashed border-[#ccff00]/20 p-4 text-center">
<p className="text-xs text-gray-500">{t('walletUi.supporters.wantListed')}</p>
<p className="mt-0.5 text-[11px] text-gray-600">{t('walletUi.supporters.wantListedDesc')}</p>
</div>
</section>
</TabsContent>
{/* ── Grantmakers ── */}
<TabsContent value="grants" className="space-y-4 pt-4">
<SectionHeader
icon={Handshake}
title={t('walletUi.supporters.grantsSection')}
subtitle={t('walletUi.supporters.grantsSectionDesc')}
color="#f7931a"
/>
<GrantmakerForm />
</TabsContent>
</Tabs>
{/* ── Footer ── */} {/* ── Footer ── */}
<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-5 text-center text-xs text-gray-500">
<p>{t('walletUi.supporters.thankYou')}</p> <p>{t('walletUi.supporters.thankYou')}</p>
<p> <p className="mt-1.5">
<Link to="/license" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('walletUi.supporters.license')}</Link> <Link to="/license" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('walletUi.supporters.license')}</Link>
{' '}&middot;{' '} {' '}&middot;{' '}
<Link to="/about" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('nav.about')}</Link> <Link to="/about" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('nav.about')}</Link>
{' '}&middot;{' '} {' '}&middot;{' '}
<Link to="/faq" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('walletUi.supporters.faq')}</Link> <Link to="/faq" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('walletUi.supporters.faq')}</Link>
</p> </p>
</div> </div>