feat(sponsors): add /sponsors page, homepage promo, and in-app strip

This commit is contained in:
Jamie Pine
2026-04-25 10:49:29 -07:00
parent 2c3df3873d
commit 4427af918e
9 changed files with 480 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 1180 320" xmlns="http://www.w3.org/2000/svg"><path d="m367.44 153.84c0 52.32 33.6 88.8 80.16 88.8s80.16-36.48 80.16-88.8-33.6-88.8-80.16-88.8-80.16 36.48-80.16 88.8zm129.6 0c0 37.44-20.4 61.68-49.44 61.68s-49.44-24.24-49.44-61.68 20.4-61.68 49.44-61.68 49.44 24.24 49.44 61.68z"/><path d="m614.27 242.64c35.28 0 55.44-29.76 55.44-65.52s-20.16-65.52-55.44-65.52c-16.32 0-28.32 6.48-36.24 15.84v-13.44h-28.8v169.2h28.8v-56.4c7.92 9.36 19.92 15.84 36.24 15.84zm-36.96-69.12c0-23.76 13.44-36.72 31.2-36.72 20.88 0 32.16 16.32 32.16 40.32s-11.28 40.32-32.16 40.32c-17.76 0-31.2-13.2-31.2-36.48z"/><path d="m747.65 242.64c25.2 0 45.12-13.2 54-35.28l-24.72-9.36c-3.84 12.96-15.12 20.16-29.28 20.16-18.48 0-31.44-13.2-33.6-34.8h88.32v-9.6c0-34.56-19.44-62.16-55.92-62.16s-60 28.56-60 65.52c0 38.88 25.2 65.52 61.2 65.52zm-1.44-106.8c18.24 0 26.88 12 27.12 25.92h-57.84c4.32-17.04 15.84-25.92 30.72-25.92z"/><path d="m823.98 240h28.8v-73.92c0-18 13.2-27.6 26.16-27.6 15.84 0 22.08 11.28 22.08 26.88v74.64h28.8v-83.04c0-27.12-15.84-45.36-42.24-45.36-16.32 0-27.6 7.44-34.8 15.84v-13.44h-28.8z"/><path d="m1014.17 67.68-65.28 172.32h30.48l14.64-39.36h74.4l14.88 39.36h30.96l-65.28-172.32zm16.8 34.08 27.36 72h-54.24z"/><path d="m1163.69 68.18h-30.72v172.32h30.72z"/><path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+32 -1
View File
@@ -3,6 +3,7 @@ import type { CSSProperties, ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import voiceboxLogo from '@/assets/voicebox-logo.png';
import { SPONSORS } from '@/lib/sponsors';
import { usePlatform } from '@/platform/PlatformContext';
function FadeIn({ delay = 0, children }: { delay?: number; children: ReactNode }) {
@@ -116,7 +117,37 @@ export function AboutPage() {
</div>
</FadeIn>
<FadeIn delay={400}>
{SPONSORS.length > 0 && (
<FadeIn delay={400}>
<div className="pt-4 flex flex-col items-center gap-3">
<p className="text-[10px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/60">
Sponsored by
</p>
<div className="flex flex-wrap items-center justify-center gap-3">
{SPONSORS.map((sponsor) => (
<a
key={sponsor.name}
href={sponsor.url}
target="_blank"
rel="noopener noreferrer"
aria-label={sponsor.name}
className="group flex h-12 min-w-[120px] items-center justify-center rounded-lg border border-border/60 bg-card/50 px-4 transition-colors hover:bg-muted/50"
>
<img
src={sponsor.logoSrc}
alt={sponsor.logoAlt ?? sponsor.name}
className={`h-5 w-auto max-w-[100px] object-contain opacity-80 transition-opacity group-hover:opacity-100 ${
sponsor.invertOnDark ? 'dark:brightness-0 dark:invert' : ''
}`}
/>
</a>
))}
</div>
</div>
</FadeIn>
)}
<FadeIn delay={480}>
<p className="text-xs text-muted-foreground/40 pt-4">
<Trans
i18nKey="settings.about.license"
+10
View File
@@ -0,0 +1,10 @@
export type Sponsor = {
name: string;
url: string;
logoSrc: string;
logoAlt?: string;
/** Set true for solid-black logos that need to flip white in dark mode. */
invertOnDark?: boolean;
};
export const SPONSORS: Sponsor[] = [];
+1
View File
@@ -0,0 +1 @@
<svg viewBox="0 0 1180 320" xmlns="http://www.w3.org/2000/svg"><path d="m367.44 153.84c0 52.32 33.6 88.8 80.16 88.8s80.16-36.48 80.16-88.8-33.6-88.8-80.16-88.8-80.16 36.48-80.16 88.8zm129.6 0c0 37.44-20.4 61.68-49.44 61.68s-49.44-24.24-49.44-61.68 20.4-61.68 49.44-61.68 49.44 24.24 49.44 61.68z"/><path d="m614.27 242.64c35.28 0 55.44-29.76 55.44-65.52s-20.16-65.52-55.44-65.52c-16.32 0-28.32 6.48-36.24 15.84v-13.44h-28.8v169.2h28.8v-56.4c7.92 9.36 19.92 15.84 36.24 15.84zm-36.96-69.12c0-23.76 13.44-36.72 31.2-36.72 20.88 0 32.16 16.32 32.16 40.32s-11.28 40.32-32.16 40.32c-17.76 0-31.2-13.2-31.2-36.48z"/><path d="m747.65 242.64c25.2 0 45.12-13.2 54-35.28l-24.72-9.36c-3.84 12.96-15.12 20.16-29.28 20.16-18.48 0-31.44-13.2-33.6-34.8h88.32v-9.6c0-34.56-19.44-62.16-55.92-62.16s-60 28.56-60 65.52c0 38.88 25.2 65.52 61.2 65.52zm-1.44-106.8c18.24 0 26.88 12 27.12 25.92h-57.84c4.32-17.04 15.84-25.92 30.72-25.92z"/><path d="m823.98 240h28.8v-73.92c0-18 13.2-27.6 26.16-27.6 15.84 0 22.08 11.28 22.08 26.88v74.64h28.8v-83.04c0-27.12-15.84-45.36-42.24-45.36-16.32 0-27.6 7.44-34.8 15.84v-13.44h-28.8z"/><path d="m1014.17 67.68-65.28 172.32h30.48l14.64-39.36h74.4l14.88 39.36h30.96l-65.28-172.32zm16.8 34.08 27.36 72h-54.24z"/><path d="m1163.69 68.18h-30.72v172.32h30.72z"/><path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+4
View File
@@ -11,6 +11,7 @@ import {Footer} from "@/components/Footer";
import {Navbar} from "@/components/Navbar";
import {Personalities} from "@/components/Personalities";
import {AppleIcon, LinuxIcon, WindowsIcon} from "@/components/PlatformIcons";
import {SponsorPromo} from "@/components/SponsorPromo";
import {SupportedModels} from "@/components/SupportedModels";
import {TutorialsSection} from "@/components/TutorialsSection";
import {VoiceCreator} from "@/components/VoiceCreator";
@@ -130,6 +131,9 @@ export default function Home() {
</div>
</section>
{/* ── Sponsor promo ────────────────────────────────────────── */}
<SponsorPromo />
{/* ── Features ─────────────────────────────────────────────── */}
<Features />
+324
View File
@@ -0,0 +1,324 @@
'use client';
import { ArrowRight, Check, Coffee, Mail } from 'lucide-react';
import { useEffect, useState } from 'react';
import { Footer } from '@/components/Footer';
import { Navbar } from '@/components/Navbar';
import {
DONATE_URL,
SPONSOR_CHECKOUT_URL,
SPONSOR_CONTACT_EMAIL,
} from '@/lib/constants';
function formatCount(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`;
if (n >= 1_000) return `${(n / 1_000).toFixed(0)}k`;
return n.toLocaleString();
}
export default function SponsorsPage() {
const [downloads, setDownloads] = useState<number | null>(null);
const [stars, setStars] = useState<number | null>(null);
useEffect(() => {
fetch('/api/releases')
.then((res) => (res.ok ? res.json() : null))
.then((data) => {
if (data?.totalDownloads != null) setDownloads(data.totalDownloads);
})
.catch(() => {});
fetch('/api/stars')
.then((res) => (res.ok ? res.json() : null))
.then((data) => {
if (typeof data?.count === 'number') setStars(data.count);
})
.catch(() => {});
}, []);
return (
<>
<Navbar />
{/* ── Hero ─────────────────────────────────────────────────── */}
<section className="relative pt-32 pb-16">
<div className="hero-glow hero-glow-fade pointer-events-none absolute inset-0 -top-32">
<div className="absolute left-1/2 top-0 -translate-x-1/2 w-[900px] h-[500px] rounded-full bg-accent/12 blur-[140px]" />
<div className="absolute left-1/2 top-16 -translate-x-1/2 w-[520px] h-[360px] rounded-full bg-accent/8 blur-[80px]" />
</div>
<div className="relative mx-auto max-w-4xl px-6 text-center">
<div
className="fade-in mb-6 text-[11px] font-semibold uppercase tracking-[0.22em] text-accent"
style={{ animationDelay: '50ms' }}
>
Corporate sponsorship
</div>
<h1
className="fade-in text-5xl font-bold tracking-tighter leading-[0.95] text-foreground md:text-6xl lg:text-7xl"
style={{ animationDelay: '100ms' }}
>
Get your brand in front of half a million creators.
</h1>
<p
className="fade-in mx-auto mt-6 max-w-2xl text-lg text-muted-foreground md:text-xl"
style={{ animationDelay: '200ms' }}
>
Voicebox is the open-source AI voice studio used by creators, podcasters, voice
artists, writers, developers, accessibility users, and curious humans all over the
world. Sponsor the project, get your logo in front of all of them.
</p>
<div
className="fade-in mt-10 flex flex-row items-center justify-center gap-3 sm:gap-4"
style={{ animationDelay: '300ms' }}
>
<a
href="#sponsor"
className="rounded-full bg-accent px-8 py-3.5 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint active:shadow-[0_2px_10px_hsl(43_60%_50%/0.3),inset_0_4px_8px_rgba(0,0,0,0.3)]"
>
Become a sponsor
</a>
<a
href={`mailto:${SPONSOR_CONTACT_EMAIL}`}
className="flex items-center gap-2 rounded-full border border-border/60 bg-card/40 backdrop-blur-sm px-6 py-3 text-sm font-medium text-muted-foreground transition-colors hover:text-foreground hover:border-border"
>
<Mail className="h-4 w-4" />
Talk to us
</a>
</div>
</div>
</section>
{/* ── Traction ────────────────────────────────────────────── */}
<section className="border-t border-border py-20">
<div className="mx-auto max-w-5xl px-6">
<div className="text-center mb-12">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
Reach
</div>
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
Real distribution, real attention.
</h2>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<Stat
value={downloads != null ? formatCount(downloads) : '500k+'}
label="Downloads"
note="Since launch in February 2026"
/>
<Stat
value={stars != null ? formatCount(stars) : '22k+'}
label="GitHub stars"
note="Trending #1 maintainer, #4 repo"
/>
<Stat
value="170k+"
label="Monthly site visitors"
note="voicebox.sh, last 30 days · growing 5×"
/>
<Stat
value="Millions"
label="Social reach"
note="Tutorials, reels, TikToks"
/>
</div>
<p className="text-center text-sm text-muted-foreground mt-10 max-w-2xl mx-auto">
Voicebox users are content creators, podcasters, voice artists, writers, developers,
accessibility users, hobbyists, and AI enthusiasts. They picked a local-first tool
over a cloud subscription they care about owning their software and the brands
behind it.
</p>
</div>
</section>
{/* ── What you get ────────────────────────────────────────── */}
<section className="border-t border-border py-20">
<div className="mx-auto max-w-5xl px-6">
<div className="text-center mb-12">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
Placement
</div>
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
Where your logo shows up.
</h2>
</div>
<div className="rounded-2xl border-2 border-accent/40 bg-card/60 backdrop-blur-sm p-8 mb-4 shadow-[0_8px_40px_hsl(43_60%_50%/0.08)]">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-2">
Headline placement
</div>
<h3 className="text-xl md:text-2xl font-semibold tracking-tight text-foreground mb-2">
voicebox.sh directly below the hero.
</h3>
<p className="text-sm md:text-base text-muted-foreground leading-relaxed max-w-2xl mb-8">
Your logo lives in the prime slot on the homepage the first thing every visitor
sees after the hero. Same row as every other corporate sponsor, linked to your URL
of choice. This is the placement that actually moves the needle.
</p>
{/* Preview of what the placement looks like on the homepage */}
<div className="rounded-xl border border-dashed border-border/80 bg-background/50 p-6 md:p-8">
<div className="text-center mb-6">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/80">
Sponsored by
</div>
</div>
<div className="flex flex-wrap items-center justify-center gap-5 md:gap-6">
<div className="flex h-32 min-w-[260px] items-center justify-center rounded-2xl border border-border bg-card/40 backdrop-blur-sm px-10">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/sponsors/openai.svg"
alt="Example sponsor logo"
className="h-14 w-auto max-w-[220px] object-contain brightness-0 invert opacity-80"
/>
</div>
</div>
<p className="mt-6 text-center text-xs text-muted-foreground/70 italic">
Example only actual sponsor logos appear here once placements are live.
</p>
</div>
</div>
<div className="grid md:grid-cols-3 gap-4">
<Perk
title="GitHub README"
body="Logo in the repo's Sponsors section. The README is one of the most-viewed docs on GitHub for any trending project."
/>
<Perk
title="/sponsors page"
body="Dedicated logo card on this page with your tagline, what your company does, and a direct link out."
/>
<Perk
title="Release notes"
body="One-line acknowledgement in the next major release post — read by the long tail of users who follow Voicebox updates."
/>
</div>
</div>
</section>
{/* ── Pricing ─────────────────────────────────────────────── */}
<section id="sponsor" className="border-t border-border py-20">
<div className="mx-auto max-w-3xl px-6">
<div className="text-center mb-10">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-accent mb-4">
Pricing
</div>
<h2 className="text-3xl md:text-4xl font-semibold tracking-tight text-foreground">
One tier. Month-to-month. Cancel anytime.
</h2>
</div>
<div className="rounded-2xl border-2 border-accent/40 bg-card/60 backdrop-blur-sm p-8 md:p-10 shadow-[0_8px_40px_hsl(43_60%_50%/0.1)]">
<div className="flex items-baseline gap-2 mb-2">
<span className="text-5xl font-bold tracking-tight text-foreground">$500</span>
<span className="text-base text-muted-foreground">/ month</span>
</div>
<p className="text-sm text-muted-foreground mb-8">
Billed monthly via Stripe. Logo goes live within 48 hours of payment.
</p>
<ul className="space-y-3 mb-8">
<PerkRow text="Logo on voicebox.sh — directly below the hero" />
<PerkRow text="Logo in the GitHub README sponsors section" />
<PerkRow text="Featured card on /sponsors with your tagline and link" />
<PerkRow text="Acknowledgement in the next major release post" />
<PerkRow text="Direct line to the team for collaboration" />
</ul>
<a
href={SPONSOR_CHECKOUT_URL}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 w-full rounded-full bg-accent px-6 py-3.5 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint"
>
Sponsor Voicebox
<ArrowRight className="h-4 w-4" />
</a>
<p className="text-center text-xs text-muted-foreground/70 mt-4">
Need an annual contract, invoicing, or higher placement?{' '}
<a
href={`mailto:${SPONSOR_CONTACT_EMAIL}`}
className="text-foreground/80 underline-offset-4 hover:underline"
>
{SPONSOR_CONTACT_EMAIL}
</a>
</p>
</div>
</div>
</section>
{/* ── Individual / policy ─────────────────────────────────── */}
<section className="border-t border-border py-20">
<div className="mx-auto max-w-4xl px-6 grid md:grid-cols-2 gap-4">
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
<div className="flex items-center gap-2 mb-3">
<Coffee className="h-5 w-5 text-[#FFDD00]" />
<h3 className="text-[15px] font-semibold text-foreground">Not a company?</h3>
</div>
<p className="text-sm leading-relaxed text-muted-foreground mb-4">
Individual supporters keep Voicebox running too. Drop a tip on Buy Me a Coffee and
your name shows up in the supporters list.
</p>
<a
href={DONATE_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-sm font-medium text-foreground/80 hover:text-foreground transition-colors"
>
Support on Buy Me a Coffee
<ArrowRight className="h-4 w-4" />
</a>
</div>
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
<h3 className="text-[15px] font-semibold text-foreground mb-3">
Who we accept
</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
Voicebox is local-first and privacy-first. We don't accept sponsorships from
companies whose business model conflicts with that voice-data brokers, ad-tech
built on speech, or surveillance vendors. Everyone else is welcome.
</p>
</div>
</div>
</section>
<Footer />
</>
);
}
function Stat({ value, label, note }: { value: string; label: string; note: string }) {
return (
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-5 text-center">
<div className="text-3xl md:text-4xl font-bold tracking-tight text-foreground">{value}</div>
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-accent mt-2">
{label}
</div>
<div className="text-xs text-muted-foreground mt-2">{note}</div>
</div>
);
}
function Perk({ title, body }: { title: string; body: string }) {
return (
<div className="rounded-xl border border-border bg-card/40 backdrop-blur-sm p-6">
<h3 className="text-[15px] font-semibold text-foreground mb-2">{title}</h3>
<p className="text-sm leading-relaxed text-muted-foreground">{body}</p>
</div>
);
}
function PerkRow({ text }: { text: string }) {
return (
<li className="flex items-start gap-3 text-sm text-foreground/90">
<Check className="h-5 w-5 shrink-0 text-accent mt-px" />
<span>{text}</span>
</li>
);
}
+94
View File
@@ -0,0 +1,94 @@
import { ArrowRight, Heart } from 'lucide-react';
import { SPONSORS, type Sponsor } from '@/lib/sponsors';
export function SponsorPromo() {
if (SPONSORS.length === 0) {
return <SponsorPromoEmpty />;
}
return <SponsorStrip sponsors={SPONSORS} />;
}
function SponsorPromoEmpty() {
return (
<section className="border-t border-border py-16">
<div className="mx-auto max-w-5xl px-6">
<div className="rounded-2xl border-2 border-accent/40 bg-gradient-to-br from-card/80 to-card/40 backdrop-blur-sm p-8 md:p-10 shadow-[0_8px_40px_hsl(43_60%_50%/0.08)]">
<div className="grid md:grid-cols-[1fr_auto] items-center gap-8">
<div>
<div className="inline-flex items-center gap-2 mb-4 text-[11px] font-semibold uppercase tracking-[0.22em] text-accent">
<Heart className="h-3.5 w-3.5" />
Sponsor Voicebox
</div>
<h3 className="text-2xl md:text-3xl font-semibold tracking-tight text-foreground mb-3">
Get your logo in front of 170k+ monthly visitors.
</h3>
<p className="text-sm md:text-base text-muted-foreground leading-relaxed max-w-2xl">
Voicebox is open-source and used by creators, voice artists, podcasters,
writers, developers, accessibility users, and curious humans all over the world.
Sponsor the project and your logo lands on the homepage, in the app, in the
README, and on the sponsors page in front of every one of them.
</p>
</div>
<div className="flex flex-col items-start md:items-end gap-2">
<a
href="/sponsors"
className="inline-flex items-center gap-2 rounded-full bg-accent px-6 py-3 text-sm font-semibold uppercase tracking-wider text-white shadow-[0_4px_20px_hsl(43_60%_50%/0.3),inset_0_2px_0_rgba(255,255,255,0.2),inset_0_-2px_0_rgba(0,0,0,0.1)] transition-all hover:bg-accent-faint whitespace-nowrap"
>
Become a sponsor
<ArrowRight className="h-4 w-4" />
</a>
<span className="text-xs text-muted-foreground/70">From $500 / month</span>
</div>
</div>
</div>
</div>
</section>
);
}
function SponsorStrip({ sponsors }: { sponsors: Sponsor[] }) {
return (
<section className="border-t border-border py-14">
<div className="mx-auto max-w-6xl px-6">
<div className="text-center mb-8">
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground/80">
Sponsored by
</div>
</div>
<div className="flex flex-wrap items-center justify-center gap-5 md:gap-6">
{sponsors.map((sponsor) => (
<a
key={sponsor.name}
href={sponsor.url}
target="_blank"
rel="noopener noreferrer"
aria-label={sponsor.name}
className="group flex h-32 min-w-[260px] items-center justify-center rounded-2xl border border-border bg-card/40 backdrop-blur-sm px-10 transition-all hover:border-accent/40 hover:bg-card"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={sponsor.logoSrc}
alt={sponsor.logoAlt ?? sponsor.name}
className={`h-14 w-auto max-w-[220px] object-contain opacity-80 transition-opacity group-hover:opacity-100 ${
sponsor.invert ? 'brightness-0 invert' : ''
}`}
/>
</a>
))}
</div>
<div className="mt-8 text-center">
<a
href="/sponsors"
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground"
>
Become a sponsor
<ArrowRight className="h-3.5 w-3.5" />
</a>
</div>
</div>
</section>
);
}
+2
View File
@@ -5,6 +5,8 @@ export const LATEST_VERSION = 'v0.1.0';
export const GITHUB_REPO = 'https://github.com/jamiepine/voicebox';
export const GITHUB_RELEASES_PAGE = `${GITHUB_REPO}/releases`;
export const DONATE_URL = 'https://buymeacoffee.com/jamiepine';
export const SPONSOR_CHECKOUT_URL = 'https://buy.stripe.com/eVqdRad3n16ubcqf201Jm00';
export const SPONSOR_CONTACT_EMAIL = '[email protected]';
export const DOWNLOAD_LINKS = {
macArm: GITHUB_RELEASES_PAGE,
+12
View File
@@ -0,0 +1,12 @@
export type Sponsor = {
name: string;
url: string;
logoSrc: string;
logoAlt?: string;
tagline?: string;
/** Set true for solid-black logos that need to render white on the dark theme. */
invert?: boolean;
};
export const SPONSORS: Sponsor[] = [
];