(unfinished) revised landing and pricing pages

This commit is contained in:
2026-01-27 17:44:01 +00:00
parent 75168762b8
commit 3f1ff128b6
2 changed files with 350 additions and 46 deletions

View File

@@ -1,3 +1,4 @@
import { Icon } from "@iconify/react";
import { Link } from "react-router-dom";
import { useSession } from "@/components/session-provider";
import ThemeToggle from "@/components/theme-toggle";
@@ -27,27 +28,185 @@ export default function Landing() {
</nav>
</header>
<main className="flex-1 flex flex-col items-center justify-center gap-8">
<div className="max-w-3xl text-center space-y-4">
<h1 className="text-[54px] font-basteleur font-700">Need a snappy project management tool?</h1>
<p className="text-[24px] font-goudy text-muted-foreground">
Build your next project with <span className="font-goudy font-700">Sprint.</span>
</p>
<p className="text-[18px] font-goudy text-muted-foreground font-700">
Sick of Jira? Say hello to your new favorite project management tool.
</p>
</div>
<main className="flex-1 flex flex-col items-center py-16 px-4">
<div className="max-w-6xl w-full space-y-24">
{/* hero section */}
<div className="text-center space-y-8 pt-8">
<div className="space-y-4">
<h1 className="text-[64px] font-basteleur font-700 leading-tight">
ship faster without the chaos
</h1>
<p className="text-[24px] font-goudy text-muted-foreground max-w-3xl mx-auto">
sprint is project management that stays out of your way. track issues, manage sprints, and
keep your team movingwithout the jira headache.
</p>
</div>
<div className="flex flex-col items-center gap-8">
{!isLoading && user ? (
<Button asChild size="lg">
<Link to="/issues">Open app</Link>
</Button>
) : (
<Button asChild size="lg">
<Link to="/login">Get started</Link>
</Button>
)}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
{!isLoading && user ? (
<Button asChild size="lg" className="text-lg px-8 py-6">
<Link to="/issues">Open app</Link>
</Button>
) : (
<>
<Button asChild size="lg" className="text-lg px-8 py-6">
<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>
</Button>
</>
)}
</div>
<p className="text-sm text-muted-foreground font-goudy">
no credit card required · full access for 14 days
</p>
</div>
{/* problem section */}
<div className="max-w-4xl mx-auto space-y-6">
<h2 className="text-4xl font-basteleur font-700 text-center">
tired of spending more time managing jira than building product?
</h2>
<div className="grid md:grid-cols-3 gap-6">
<div className="space-y-2">
<Icon icon="lucide:timer-off" className="size-8 text-muted-foreground mx-auto" />
<p className="text-center font-goudy text-muted-foreground">
wasting hours configuring workflows instead of shipping features
</p>
</div>
<div className="space-y-2">
<Icon icon="lucide:boxes" className="size-8 text-muted-foreground mx-auto" />
<p className="text-center font-goudy text-muted-foreground">
drowning in features you'll never use while missing the basics
</p>
</div>
<div className="space-y-2">
<Icon icon="lucide:bug-off" className="size-8 text-muted-foreground mx-auto" />
<p className="text-center font-goudy text-muted-foreground">
context switching kills momentum—your flow state doesn't stand a chance
</p>
</div>
</div>
</div>
{/* solution section */}
<div className="max-w-5xl mx-auto space-y-12">
<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="lucide:layout-dashboard" className="size-10 text-personality" />
<h3 className="text-2xl font-basteleur font-700">see your work at a glance</h3>
<p className="font-goudy text-muted-foreground text-lg">
beautiful, intuitive issue tracking with customizable statuses. organize by projects and
sprints. find what you need in seconds, not minutes.
</p>
</div>
<div className="border p-8 space-y-4">
<Icon icon="lucide:timer" className="size-10 text-personality" />
<h3 className="text-2xl font-basteleur font-700">track time without thinking</h3>
<p className="font-goudy text-muted-foreground text-lg">
built-in time tracking that actually works. start, pause, resume. see where your time goes
without juggling another tool.
</p>
</div>
<div className="border p-8 space-y-4">
<Icon icon="lucide:rocket" className="size-10 text-personality" />
<h3 className="text-2xl font-basteleur font-700">ship in sprints</h3>
<p className="font-goudy text-muted-foreground text-lg">
sprint planning that actually works. set date ranges, assign issues, track velocity. keep
your team aligned without the ceremony.
</p>
</div>
<div className="border p-8 space-y-4">
<Icon icon="lucide:zap" className="size-10 text-personality" />
<h3 className="text-2xl font-basteleur font-700">stay in flow</h3>
<p className="font-goudy text-muted-foreground text-lg">
minimal clicks, maximum productivity. keyboard shortcuts, resizable panes, and a clean
interface that gets out of your way.
</p>
</div>
</div>
</div>
{/* features list */}
<div className="max-w-4xl mx-auto border p-8 space-y-6">
<h2 className="text-3xl font-basteleur font-700 text-center">
built for developers, by a developer
</h2>
<div className="grid md:grid-cols-2 gap-x-8 gap-y-3">
{[
"organization and project management",
"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",
"jwt authentication",
].map((feature) => (
<div key={feature} className="flex items-start gap-2">
<Icon icon="lucide:check" className="size-5 text-personality shrink-0 mt-0.5" />
<span className="font-goudy">{feature}</span>
</div>
))}
</div>
</div>
{/* social proof placeholder */}
<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">
<p className="font-goudy text-lg italic text-muted-foreground">
"finally, a project management tool that doesn't slow me down"
</p>
<p className="text-sm font-goudy font-700">early user feedback</p>
</div>
<div className="border p-6 space-y-4">
<p className="font-goudy text-lg italic text-muted-foreground">
"built by someone who actually understands developer workflows"
</p>
<p className="text-sm font-goudy font-700">early user feedback</p>
</div>
<div className="border p-6 space-y-4">
<p className="font-goudy text-lg italic text-muted-foreground">
"the simplicity is refreshing. no bloat, just what we need"
</p>
<p className="text-sm font-goudy font-700">early user feedback</p>
</div>
</div>
</div>
{/* final cta */}
<div className="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 font-goudy text-muted-foreground">
start tracking issues, managing sprints, and shipping product in minutes
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
{!isLoading && user ? (
<Button asChild size="lg" className="text-lg px-8 py-6">
<Link to="/issues">Open app</Link>
</Button>
) : (
<Button asChild size="lg" className="text-lg px-8 py-6">
<Link to="/login">start your free trial</Link>
</Button>
)}
</div>
<p className="text-sm text-muted-foreground font-goudy">
no credit card required · 14-day free trial · cancel anytime
</p>
</div>
</div>
</main>

View File

@@ -1,4 +1,5 @@
import { Icon } from "@iconify/react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { useSession } from "@/components/session-provider";
import ThemeToggle from "@/components/theme-toggle";
@@ -7,34 +8,87 @@ import { cn } from "@/lib/utils";
const pricingTiers = [
{
name: "Indie",
name: "Starter",
price: "£0",
priceAnnual: "£0",
period: "Free forever",
description: "Perfect for individual developers and small teams",
features: ["1 organisation (owned or joined)", "1 project", "100 issues", "Up to 5 users"],
cta: "Get started",
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: "£12",
price: "£11.99",
priceAnnual: "£9.99",
period: "per user/month",
description: "For growing teams and professional developers",
periodAnnual: "per user/month",
description: "For growing teams and professionals",
tagline: "Most Popular",
features: [
"Unlimited issues",
"Unlimited projects",
"Everything in starter",
"Unlimited organisations",
"Time tracking & reports",
"Unlimited projects",
"Unlimited issues",
"Advanced time tracking & reports",
"Sprint velocity tracking",
"Priority email support",
"Custom issue statuses",
"Role-based permissions",
],
cta: "Start free trial",
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">
@@ -58,44 +112,94 @@ export default function Pricing() {
</header>
<main className="flex-1 flex flex-col items-center py-16 px-4">
<div className="max-w-4xl w-full space-y-16">
<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</p>
<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>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* 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" : "border-border"
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">
Most popular
{tier.tagline}
</div>
)}
<div className="space-y-4 mb-8">
<h2 className="text-4xl font-basteleur font-700">{tier.name}</h2>
<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">{tier.price}</span>
<span className="text-md text-muted-foreground">{tier.period}</span>
<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-xl font-goudy text-muted-foreground">{tier.description}</p>
<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="lucide:check" className="size-5 text-personality shrink-0 mt-0.5" />
<span className="text-sm">{feature}</span>
<span className={"text-sm"}>{feature}</span>
</li>
))}
</ul>
@@ -103,7 +207,10 @@ export default function Pricing() {
<Button
asChild
variant={tier.highlighted ? "default" : "outline"}
className={cn("font-700", tier.highlighted ? "bg-personality hover:bg-personality/90" : "")}
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>
@@ -111,13 +218,51 @@ export default function Pricing() {
))}
</div>
<div className="text-center flex flex-col items-center justify-center gap-2">
<p className="text-xl font-goudy text-muted-foreground">Need something custom? </p>
{/* 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="lucide:shield-check" 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="lucide:credit-card" 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="lucide:rotate-ccw" 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"
className="text-personality hover:underline font-goudy text-lg font-700"
href="mailto:ob248@proton.me?subject=Sprint Pricing Question"
className="inline-block text-personality hover:underline font-goudy text-lg font-700"
>
Get in touch
Contact Us
</a>
</div>
</div>