new full landing page

This commit is contained in:
2026-01-27 21:04:24 +00:00
parent 13e678f8e8
commit 99d53d85f6
5 changed files with 326 additions and 333 deletions

View File

@@ -12,10 +12,13 @@ import {
Close as PixelClose,
MessagePlus as PixelCommentSend,
CreditCard as PixelCreditCard,
CreditCardDelete as PixelCreditCardDelete,
Dashboard as PixelDashboard,
Debug as PixelDebug,
DebugOff as PixelDebugOff,
Edit as PixelEdit,
EyeClosed as PixelEyeClosed,
AddGrid as PixelGridAdd,
Home as PixelHome,
InfoBox as PixelInfo,
Link as PixelLink,
@@ -55,6 +58,7 @@ import {
DotsSixVerticalIcon as PhosphorDotsSixVertical,
DotsThreeVerticalIcon as PhosphorDotsThreeVertical,
PencilSimpleIcon as PhosphorEdit,
EyeClosedIcon as PhosphorEyeClosed,
HashIcon as PhosphorHash,
HashStraightIcon as PhosphorHashStraight,
HouseIcon as PhosphorHome,
@@ -73,6 +77,7 @@ import {
RocketLaunchIcon as PhosphorRocketLaunch,
HardDrivesIcon as PhosphorServer,
ShieldCheckIcon as PhosphorShieldCheck,
StackPlusIcon as PhosphorStackPlus,
StopIcon as PhosphorStop,
SunIcon as PhosphorSun,
TimerIcon as PhosphorTimer,
@@ -100,6 +105,8 @@ import {
CreditCard,
Edit,
EllipsisVertical,
EyeClosed,
Grid2x2Plus as GridAdd,
GripVerticalIcon,
Hash,
InfoIcon,
@@ -155,12 +162,15 @@ const icons = {
circleQuestionMark: { lucide: CircleQuestionMark, pixel: PixelNoteDelete, phosphor: PhosphorQuestion },
comment: { lucide: MessageSquarePlus, pixel: PixelCommentSend, phosphor: PhosphorComment },
creditCard: { lucide: CreditCard, pixel: PixelCreditCard, phosphor: PhosphorCreditCard },
creditCardDelete: { lucide: CreditCard, pixel: PixelCreditCardDelete, phosphor: PhosphorCreditCard },
edit: { lucide: Edit, pixel: PixelEdit, phosphor: PhosphorEdit },
ellipsisVertical: {
lucide: EllipsisVertical,
pixel: PixelMoreVertical,
phosphor: PhosphorDotsThreeVertical,
},
eyeClosed: { lucide: EyeClosed, pixel: PixelEyeClosed, phosphor: PhosphorEyeClosed },
gridAdd: { lucide: GridAdd, pixel: PixelGridAdd, phosphor: PhosphorStackPlus },
gripVerticalIcon: {
lucide: GripVerticalIcon,
pixel: PixelViewportWide,

View File

@@ -5,10 +5,12 @@ import { cn } from "@/lib/utils";
function Switch({
className,
thumbClassName,
size = "default",
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
size?: "sm" | "default";
thumbClassName?: string;
size?: "sm" | "default" | "lg";
}) {
return (
<SwitchPrimitive.Root
@@ -18,8 +20,8 @@ function Switch({
"peer data-[state=checked]:bg-personality data-[state=unchecked]:bg-input",
"focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80",
"group/switch inline-flex shrink-0 items-center rounded-full border border-transparent",
"outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
"data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6",
"outline-none focus-visible:ring-[3px] cursor-pointer disabled:cursor-not-allowed disabled:opacity-50",
"data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[size=lg]:h-7 data-[size=lg]:w-12",
className,
)}
{...props}
@@ -27,7 +29,8 @@ function Switch({
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background dark:data-[state=unchecked]:bg-personality dark:data-[state=checked]:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
"bg-background dark:data-[state=unchecked]:bg-personality dark:data-[state=checked]:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=lg]/switch:size-6 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0",
thumbClassName,
)}
/>
</SwitchPrimitive.Root>

View File

@@ -13,7 +13,6 @@ import Issues from "@/pages/Issues";
import Landing from "@/pages/Landing";
import Login from "@/pages/Login";
import NotFound from "@/pages/NotFound";
import Pricing from "@/pages/Pricing";
import Test from "@/pages/Test";
import Timeline from "@/pages/Timeline";
@@ -28,7 +27,6 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
{/* public routes */}
<Route path="/" element={<Landing />} />
<Route path="/font" element={<Font />} />
<Route path="/pricing" element={<Pricing />} />
<Route path="/login" element={<Login />} />
{/* authed routes */}

View File

@@ -1,32 +1,141 @@
import { HumanRun } from "@nsmr/pixelart-react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { useSession } from "@/components/session-provider";
import ThemeToggle from "@/components/theme-toggle";
import { Button } from "@/components/ui/button";
import Icon from "@/components/ui/icon";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";
const pricingTiers = [
{
name: "Starter",
price: "£0",
priceAnnual: "£0",
period: "Free forever",
periodAnnual: "Free forever",
description: "Perfect for side projects and solo developers",
tagline: "For solo devs and small projects",
features: [
"1 organisation (owned or joined)",
"1 project",
"100 issues",
"Up to 5 team members",
"Email support",
],
cta: "Get started free",
ctaLink: "/login",
highlighted: false,
},
{
name: "Pro",
price: "£11.99",
priceAnnual: "£9.99",
period: "per user/month",
periodAnnual: "per user/month",
description: "For growing teams and professionals",
tagline: "Most Popular",
features: [
"Everything in starter",
"Unlimited organisations",
"Unlimited projects",
"Unlimited issues",
"Advanced time tracking & reports",
"Custom issue statuses",
"Priority email support",
],
cta: "Try pro free for 14 days",
ctaLink: "/login",
highlighted: true,
},
];
const faqs = [
{
question: "Can I switch plans?",
answer:
"Yes, you can upgrade or downgrade at any time. Changes take effect immediately, and we'll prorate any charges.",
},
{
question: "Is there a free trial?",
answer: "Yes, pro plan includes a 14-day free trial with full access. No credit card required to start.",
},
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards.",
},
{
question: "What if I need more users?",
answer:
"Pro plan pricing scales with your team. Add or remove users anytime, and we'll adjust your billing automatically.",
},
{
question: "What happens when my trial ends?",
answer:
"You'll automatically downgrade to the free starter plan. No charges unless you actively upgrade to pro.",
},
{
question: "Can I cancel anytime?",
answer:
"Absolutely. Cancel anytime with no questions asked. You'll keep access until the end of your billing period.",
},
{
question: "Do you offer refunds?",
answer: "Yes, we offer a 30-day money-back guarantee. If Sprint isn't right for you, just let us know.",
},
];
export default function Landing() {
const { user, isLoading } = useSession();
const [billingPeriod, setBillingPeriod] = useState<"monthly" | "annual">("monthly");
return (
<div className="min-h-screen flex flex-col">
<header className="relative flex items-center justify-center p-2 border-b">
<div className="text-3xl font-basteleur font-700">Sprint</div>
<nav className="absolute right-2 flex items-center gap-4">
<Link to="/pricing" className="text-sm hover:text-personality transition-colors">
Pricing
</Link>
<ThemeToggle />
{!isLoading && user ? (
<Button asChild variant="outline" size="sm">
<Link to="/issues">Open app</Link>
</Button>
) : (
<Button asChild variant="outline" size="sm">
<Link to="/login">Sign in</Link>
</Button>
)}
</nav>
<div className="min-h-screen flex flex-col" id="top">
<header className="sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-md">
<div className="w-full flex h-14 items-center justify-between px-2">
<div className="flex items-center gap-2">
<img src="/favicon.svg" alt="Sprint" className="size-12 -mt-0.5" />
<span className="text-3xl font-basteleur font-700 transition-colors -mt-0.5">Sprint</span>
</div>
<nav className="flex items-center gap-6">
<a
href="#top"
className="hidden md:block text-sm font-500 hover:text-personality transition-colors"
>
Home
</a>
<a
href="#features"
className="hidden md:block text-sm font-500 hover:text-personality transition-colors"
>
Features
</a>
<a
href="#pricing"
className="hidden md:block text-sm font-500 hover:text-personality transition-colors"
>
Pricing
</a>
<a
href="#faq"
className="hidden md:block text-sm font-500 hover:text-personality transition-colors"
>
FAQ
</a>
<div className="flex items-center gap-2">
<ThemeToggle />
{!isLoading && user ? (
<Button asChild variant="outline" size="sm">
<Link to="/issues">Open app</Link>
</Button>
) : (
<Button asChild variant="outline" size="sm">
<Link to="/login">Sign in</Link>
</Button>
)}
</div>
</nav>
</div>
</header>
<main className="flex-1 flex flex-col items-center py-16 px-4">
@@ -34,7 +143,7 @@ export default function Landing() {
{/* hero section */}
<div className="text-center space-y-8 pt-8">
<div className="flex justify-center mb-8">
<HumanRun size={144} fill="var(--foreground)" />
<img src="/favicon.svg" alt="Sprint" className="size-48" />
</div>
<div className="space-y-4">
<h1 className="text-[64px] font-basteleur font-700 leading-tight">
@@ -57,7 +166,7 @@ export default function Landing() {
<Link to="/login">Start free trial</Link>
</Button>
<Button asChild variant="outline" size="lg" className="text-lg px-8 py-6">
<Link to="/pricing">See pricing</Link>
<a href="#pricing">See pricing</a>
</Button>
</>
)}
@@ -69,38 +178,58 @@ export default function Landing() {
{/* problem section */}
<div className="max-w-4xl mx-auto space-y-16">
<h2 className="text-4xl font-basteleur font-700 text-center">
Tired of spending more time managing Jira than building your product?
Tired of spending more time managing Jira than building products?
</h2>
<div className="grid md:grid-cols-3 gap-6">
<div className="space-y-2">
<Icon icon="timerOff" className="size-16 mx-auto" color={"var(--muted-foreground)"} />
<Icon
icon="timerOff"
iconStyle={"pixel"}
className="size-16 mx-auto"
color={"var(--muted-foreground)"}
/>
<p className="text-center text-muted-foreground">
Wasting hours configuring workflows instead of shipping features
</p>
</div>
<div className="space-y-2">
<Icon icon="box" className="size-16 mx-auto" color={"var(--muted-foreground)"} />
<Icon
icon="gridAdd"
iconStyle={"pixel"}
className="size-16 mx-auto"
color={"var(--muted-foreground)"}
/>
<p className="text-center text-muted-foreground">
Drowning in features you'll never use while missing the basics
</p>
</div>
<div className="space-y-2">
<Icon icon="bugOff" className="size-16 mx-auto" color={"var(--muted-foreground)"} />
<Icon
icon="bugOff"
iconStyle={"pixel"}
className="size-16 mx-auto"
color={"var(--muted-foreground)"}
/>
<p className="text-center text-muted-foreground">
Context switching kills momentum—your flow state doesn't stand a chance
The software is full of bugs. Your flow state doesn't stand a chance
</p>
</div>
</div>
</div>
{/* solution section */}
<div className="max-w-5xl mx-auto space-y-12">
<div id="features" className="max-w-5xl mx-auto space-y-12 scroll-mt-20 border-t pt-24">
<h2 className="text-5xl font-basteleur font-700 text-center">
Everything you need, nothing you don't
</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="border p-8 space-y-4">
<Icon icon="layoutDashboard" className="size-10" color={"var(--personality)"} />
<Icon
icon="layoutDashboard"
iconStyle={"pixel"}
className="size-10"
color={"var(--personality)"}
/>
<h3 className="text-2xl font-basteleur font-700">See your work at a glance</h3>
<p className="text-muted-foreground text-lg">
Beautiful, intuitive issue tracking with customizable statuses. Organize by projects and
@@ -109,7 +238,7 @@ export default function Landing() {
</div>
<div className="border p-8 space-y-4">
<Icon icon="timer" className="size-10" color={"var(--personality)"} />
<Icon icon="timer" iconStyle={"pixel"} className="size-10" color={"var(--personality)"} />
<h3 className="text-2xl font-basteleur font-700">Track time without thinking</h3>
<p className="text-muted-foreground text-lg">
Built-in time tracking that actually works. Start, pause, resume. See where your time goes
@@ -118,7 +247,7 @@ export default function Landing() {
</div>
<div className="border p-8 space-y-4">
<Icon icon="rocket" className="size-10" color={"var(--personality)"} />
<Icon icon="rocket" iconStyle={"pixel"} className="size-10" color={"var(--personality)"} />
<h3 className="text-2xl font-basteleur font-700">Ship in sprints</h3>
<p className="text-muted-foreground text-lg">
Sprint planning that actually works. Set date ranges, assign issues, track velocity. Keep
@@ -127,7 +256,7 @@ export default function Landing() {
</div>
<div className="border p-8 space-y-4">
<Icon icon="checkBox" className="size-10" color={"var(--personality)"} />
<Icon icon="checkBox" iconStyle={"pixel"} className="size-10" color={"var(--personality)"} />
<h3 className="text-2xl font-basteleur font-700">Only use what you need</h3>
<p className="text-muted-foreground text-lg">
Every feature is optional. Sprints, time tracking, and other modules can be enabled or
@@ -149,23 +278,165 @@ export default function Landing() {
"Customizable issue statuses",
"Sprint planning with date ranges",
"Built-in time tracking",
"Role-based access control",
"Native desktop app (Tauri)",
"Self-hostable on your infrastructure",
"Clean, resizable interface",
"Issue assignment and collaboration",
"Individual feature toggles (org level)",
].map((feature) => (
<div key={feature} className="flex items-start gap-2">
<Icon icon="check" className="size-5 shrink-0 mt-0.5" color={"var(--personality)"} />
<Icon icon="check" iconStyle={"pixel"} className="size-6" color={"var(--personality)"} />
<span>{feature}</span>
</div>
))}
</div>
</div>
{/* pricing section */}
<div
id="pricing"
className="max-w-5xl mx-auto space-y-16 flex flex-col items-center border-t pt-24 scroll-mt-20"
>
<div className="text-center space-y-6">
<h2 className="text-5xl font-basteleur font-700">Simple, transparent pricing</h2>
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
Choose the plan that fits your team. Scale as you grow.
</p>
{/* billing toggle */}
<div className="flex items-center justify-center gap-4 pt-4">
<button
type="button"
onClick={() => setBillingPeriod("monthly")}
className={cn(
"text-lg transition-colors",
billingPeriod === "monthly" ? "text-foreground font-700" : "text-muted-foreground",
)}
>
monthly
</button>
<Switch
size="lg"
checked={billingPeriod === "annual"}
onCheckedChange={(checked) => setBillingPeriod(checked ? "annual" : "monthly")}
className="bg-border data-[state=checked]:bg-border! data-[state=unchecked]:bg-border!"
thumbClassName="bg-personality dark:bg-personality data-[state=checked]:bg-personality! data-[state=unchecked]:bg-personality!"
aria-label="toggle billing period"
/>
<button
type="button"
onClick={() => setBillingPeriod("annual")}
className={cn(
"text-lg transition-colors",
billingPeriod === "annual" ? "text-foreground font-700" : "text-muted-foreground",
)}
>
annual
</button>
<span className="text-sm px-3 py-1 bg-personality/10 text-personality rounded-full font-600">
Save 17%
</span>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 w-full max-w-4xl">
{pricingTiers.map((tier) => (
<div
key={tier.name}
className={cn(
"flex flex-col border p-8 space-y-6 relative",
tier.highlighted ? "border-2 border-personality shadow-lg scale-105" : "border-border",
)}
>
{tier.highlighted && (
<div className="absolute -top-4 left-4 bg-personality text-background px-3 py-1 text-xs font-700">
{tier.tagline}
</div>
)}
<div className="space-y-4">
<h3 className="text-3xl font-basteleur font-700">{tier.name}</h3>
<div className="flex items-baseline gap-2">
<span className="text-4xl font-700">
{billingPeriod === "annual" ? tier.priceAnnual : tier.price}
</span>
<span className="text-sm text-muted-foreground">
{billingPeriod === "annual" ? tier.periodAnnual : tier.period}
</span>
</div>
<p className="text-muted-foreground">{tier.description}</p>
</div>
<ul className="space-y-3 flex-1">
{tier.features.map((feature) => (
<li key={feature} className="flex items-start gap-2 text-sm">
<Icon
icon="check"
iconStyle={"pixel"}
className="size-6 -mt-0.5"
color="var(--personality)"
/>
<span>{feature}</span>
</li>
))}
</ul>
<Button
asChild
variant={tier.highlighted ? "default" : "outline"}
className={cn(
"font-700 py-6",
tier.highlighted ? "bg-personality hover:bg-personality/90 text-background" : "",
)}
>
<Link to={tier.ctaLink}>{tier.cta}</Link>
</Button>
</div>
))}
</div>
{/* trust signals */}
<div className="grid md:grid-cols-3 gap-8 w-full border-t pt-16 pb-8">
<div className="flex flex-col items-center text-center gap-2">
<Icon icon="eyeClosed" iconStyle={"pixel"} className="size-8" color="var(--personality)" />
<p className="font-700">Secure & Encrypted</p>
<p className="text-sm text-muted-foreground">Your data is safe with us</p>
</div>
<div className="flex flex-col items-center text-center gap-2">
<Icon
icon="creditCardDelete"
iconStyle={"pixel"}
className="size-8"
color="var(--personality)"
/>
<p className="font-700">No Card Required</p>
<p className="text-sm text-muted-foreground">Start your trial instantly</p>
</div>
<div className="flex flex-col items-center text-center gap-2">
<Icon icon="rotateCcw" iconStyle={"pixel"} className="size-8" color="var(--personality)" />
<p className="font-700">Money Back Guarantee</p>
<p className="text-sm text-muted-foreground">30-day no-risk policy</p>
</div>
</div>
{/* faq section */}
<div className="w-full max-w-5xl flex justify-center border-t pt-24 scroll-mt-20" id="faq">
<div className="w-full max-w-4xl flex flex-col items-center space-y-12">
<h2 className="text-5xl font-basteleur font-700 text-center">Frequently Asked Questions</h2>
<div className="grid gap-8 max-w-3xl">
{faqs.map((faq) => (
<div key={faq.question} className="space-y-2">
<h4 className="text-lg font-700">{faq.question}</h4>
<p className="text-muted-foreground">{faq.answer}</p>
</div>
))}
</div>
</div>
</div>
</div>
{/* TODO:> commented out until we have actual testimonies */}
{/* social proof placeholder */}
<div className="max-w-4xl mx-auto text-center space-y-8">
{/* <div className="max-w-4xl mx-auto text-center space-y-8">
<h2 className="text-4xl font-basteleur font-700">Join developers who've escaped Jira</h2>
<div className="grid md:grid-cols-3 gap-8">
<div className="border p-6 space-y-4">
@@ -187,13 +458,13 @@ export default function Landing() {
<p className="text-sm font-700">Early user feedback</p>
</div>
</div>
</div>
</div> */}
{/* final cta */}
<div className="text-center space-y-6 border-t pt-16">
<div className="max-w-5xl mx-auto text-center space-y-6 border-t pt-16">
<h2 className="text-5xl font-basteleur font-700">Ready to ship faster?</h2>
<p className="text-xl text-muted-foreground">
Start tracking issues, managing sprints, and shipping product in minutes
Start tracking issues, managing sprints, and shipping products in minutes
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
{!isLoading && user ? (

View File

@@ -1,289 +0,0 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import { useSession } from "@/components/session-provider";
import ThemeToggle from "@/components/theme-toggle";
import { Button } from "@/components/ui/button";
import Icon from "@/components/ui/icon";
import { cn } from "@/lib/utils";
const pricingTiers = [
{
name: "Starter",
price: "£0",
priceAnnual: "£0",
period: "Free forever",
description: "Perfect for side projects and solo developers",
tagline: "For solo devs and small projects",
features: [
"1 organisation (owned or joined)",
"1 project",
"100 issues",
"Up to 5 team members",
"Basic time tracking",
"Email support",
],
cta: "Get started free",
ctaLink: "/login",
highlighted: false,
},
{
name: "Pro",
price: "£11.99",
priceAnnual: "£9.99",
period: "per user/month",
periodAnnual: "per user/month",
description: "For growing teams and professionals",
tagline: "Most Popular",
features: [
"Everything in starter",
"Unlimited organisations",
"Unlimited projects",
"Unlimited issues",
"Advanced time tracking & reports",
"Sprint velocity tracking",
"Priority email support",
"Custom issue statuses",
"Role-based permissions",
],
cta: "Try pro free for 14 days",
ctaLink: "/login",
highlighted: true,
},
];
const faqs = [
{
question: "Can I switch plans?",
answer:
"Yes, you can upgrade or downgrade at any time. Changes take effect immediately, and we'll prorate any charges.",
},
{
question: "Is there a free trial?",
answer: "Yes, pro plan includes a 14-day free trial with full access. No credit card required to start.",
},
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards.",
},
{
question: "What if I need more users?",
answer:
"Pro plan pricing scales with your team. Add or remove users anytime, and we'll adjust your billing automatically.",
},
{
question: "What happens when my trial ends?",
answer:
"You'll automatically downgrade to the free starter plan. No charges unless you actively upgrade to pro.",
},
{
question: "Can I cancel anytime?",
answer:
"Absolutely. Cancel anytime with no questions asked. You'll keep access until the end of your billing period.",
},
{
question: "Do you offer refunds?",
answer: "Yes, we offer a 30-day money-back guarantee. If Sprint isn't right for you, just let us know.",
},
];
export default function Pricing() {
const { user, isLoading } = useSession();
const [billingPeriod, setBillingPeriod] = useState<"monthly" | "annual">("monthly");
return (
<div className="min-h-screen flex flex-col">
<header className="relative flex items-center justify-center p-2 border-b">
<div className="text-3xl font-basteleur font-700">Sprint</div>
<nav className="absolute right-2 flex items-center gap-4">
<Link to="/" className="text-sm hover:text-personality transition-colors">
Home
</Link>
<ThemeToggle />
{!isLoading && user ? (
<Button asChild variant="outline" size="sm">
<Link to="/issues">Open app</Link>
</Button>
) : (
<Button asChild variant="outline" size="sm">
<Link to="/login">Sign in</Link>
</Button>
)}
</nav>
</header>
<main className="flex-1 flex flex-col items-center py-16 px-4">
<div className="max-w-5xl w-full space-y-16 flex flex-col items-center">
{/* header section */}
<div className="text-center space-y-8">
<h1 className="text-5xl font-basteleur font-700">Simple, transparent pricing</h1>
<p className="text-2xl font-goudy text-muted-foreground">
Choose the plan that fits your team. Scale as you grow.
</p>
{/* billing toggle */}
<div className="flex items-center justify-center gap-4">
<button
type="button"
onClick={() => setBillingPeriod("monthly")}
className={cn(
"text-lg transition-colors",
billingPeriod === "monthly" ? "text-foreground font-700" : "text-muted-foreground",
)}
>
monthly
</button>
<button
type="button"
className="relative w-14 h-8 bg-border rounded-full"
onClick={() => setBillingPeriod(billingPeriod === "monthly" ? "annual" : "monthly")}
aria-label="toggle billing period"
>
<div
className={cn(
"absolute top-1 w-6 h-6 bg-personality rounded-full transition-transform",
billingPeriod === "annual" ? "translate-x-7" : "translate-x-1",
)}
/>
</button>
<button
type="button"
onClick={() => setBillingPeriod("annual")}
className={cn(
"text-lg transition-colors",
billingPeriod === "annual" ? "text-foreground font-700" : "text-muted-foreground",
)}
>
annual
</button>
<span className="text-md px-3 pb-1 pt-1.25 bg-personality/10 text-personality rounded-full font-600">
Save 17%
</span>
</div>
</div>
{/* pricing tiers */}
<div className="max-w-3xl w-full grid grid-cols-1 md:grid-cols-2 gap-8">
{pricingTiers.map((tier) => (
<div
key={tier.name}
className={`flex flex-col border p-8 ${
tier.highlighted
? "border-2 border-personality shadow-lg relative scale-105"
: "border-border"
}`}
>
{tier.highlighted && (
<div className="absolute -top-4 left-1/2 -translate-x-1/2 bg-personality text-background px-4 py-1 text-sm font-600">
{tier.tagline}
</div>
)}
<div className="space-y-4 mb-8">
<h2 className="text-3xl font-basteleur font-700">{tier.name}</h2>
<div className="space-y-1">
<div className="flex items-baseline gap-2">
<span className="text-4xl font-600">
{billingPeriod === "annual" ? tier.priceAnnual : tier.price}
</span>
<span className="text-sm text-muted-foreground">
{billingPeriod === "annual" ? tier.periodAnnual : tier.period}
</span>
</div>
</div>
<p className="text-lg font-goudy text-muted-foreground">{tier.description}</p>
</div>
<ul className="space-y-3 mb-8 flex-1">
{tier.features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<Icon icon="check" className="size-5 text-personality shrink-0 mt-0.5" />
<span className={"text-sm"}>{feature}</span>
</li>
))}
</ul>
<Button
asChild
variant={tier.highlighted ? "default" : "outline"}
className={cn(
"font-700 h-auto py-3 whitespace-normal text-center",
tier.highlighted ? "bg-personality hover:bg-personality/90" : "",
)}
>
<Link to={tier.ctaLink}>{tier.cta}</Link>
</Button>
</div>
))}
</div>
{/* trust signals */}
<div className="text-center space-y-6 border-t pt-12">
<div className="grid md:grid-cols-3 gap-8 max-w-4xl mx-auto">
<div className="flex flex-col items-center gap-2">
<Icon icon="shieldCheck" className="size-8 text-personality" />
<p className="font-goudy font-700">Secure & Encrypted</p>
<p className="text-sm text-muted-foreground">Your data is encrypted and secure</p>
</div>
<div className="flex flex-col items-center gap-2">
<Icon icon="creditCard" className="size-8 text-personality" />
<p className="font-goudy font-700">No Credit Card Required</p>
<p className="text-sm text-muted-foreground">Start your free trial today</p>
</div>
<div className="flex flex-col items-center gap-2">
<Icon icon="rotateCcw" className="size-8 text-personality" />
<p className="font-goudy font-700">30-day money back</p>
<p className="text-sm text-muted-foreground">Cancel anytime, no questions</p>
</div>
</div>
</div>
{/* faq section */}
<div className="max-w-3xl mx-auto space-y-8 border-t pt-12">
<h2 className="text-4xl font-basteleur font-700 text-center">Frequently Asked Questions</h2>
<div className="space-y-6">
{faqs.map((faq) => (
<div key={faq.question} className="space-y-2">
<h3 className="text-xl font-goudy font-700">{faq.question}</h3>
<p className="text-muted-foreground font-goudy">{faq.answer}</p>
</div>
))}
</div>
</div>
{/* final cta */}
<div className="text-center space-y-6 border-t pt-12">
<h2 className="text-4xl font-basteleur font-700">Still have questions?</h2>
<p className="text-xl font-goudy text-muted-foreground">
We're here to help. Get in touch with any questions.
</p>
<a
href="mailto:ob248@proton.me?subject=Sprint Pricing Question"
className="inline-block text-personality hover:underline font-goudy text-lg font-700"
>
Contact Us
</a>
</div>
</div>
</main>
<footer className="flex justify-center gap-2 items-center py-1 border-t">
<span className="font-300 text-lg text-muted-foreground font-goudy">
Built by{" "}
<a
href="https://ob248.com"
target="_blank"
rel="noopener noreferrer"
className="hover:text-personality font-goudy font-700"
>
Oliver Bryan
</a>
</span>
<a href="https://ob248.com" target="_blank" rel="noopener noreferrer">
<img src="oliver-bryan.svg" alt="Oliver Bryan" className="w-4 h-4" />
</a>
</footer>
</div>
);
}