- Hero auto-rotates story backdrops every 5 seconds with 500ms fade transitions - ProductShowcase cards rotate with mint ring highlight on the active card - Inactive cards fade to 60% opacity on mobile, full opacity on desktop - New components: StoryBackdrop, StoryOnboarding, CountUp, lib/stories - Manual Another-story control still works Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
262 lines
11 KiB
TypeScript
262 lines
11 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import Link from "next/link";
|
||
import { ArrowRight, Check, Minus, Plus } from "lucide-react";
|
||
import { GaugeChart } from "@/components/charts/GaugeChart";
|
||
import { ProgressBar } from "@/components/ui/ProgressBar";
|
||
import { CountUp } from "@/components/ui/CountUp";
|
||
import { LeafMark } from "@/components/ui/Logo";
|
||
import { prices } from "@/lib/data";
|
||
|
||
const mono = "font-mono text-[10px] uppercase tracking-[.25em]";
|
||
|
||
function Eyebrow({ children }: { children: string }) {
|
||
return <p className={`${mono} text-mint`}>{children}</p>;
|
||
}
|
||
|
||
/* ---------- Product showcase — live UI, not screenshots ---------- */
|
||
export function ProductShowcase() {
|
||
const [activeCard, setActiveCard] = useState(0);
|
||
const cardCount = 3;
|
||
|
||
useEffect(() => {
|
||
const interval = setInterval(() => {
|
||
setActiveCard((i) => (i + 1) % cardCount);
|
||
}, 5000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
const cards = [
|
||
{
|
||
title: "Flex budget",
|
||
mono: "Flex budget",
|
||
label: "$93 safe to spend today",
|
||
content: <GaugeChart value={72} label="$93 safe to spend today" />,
|
||
heading: "One number, not thirty categories.",
|
||
description: "Income − fixed costs − goals. What's left is truly yours to spend.",
|
||
gradient: "from-[#123B2E] to-[#0C1512]",
|
||
bgLight: "bg-[#0E1F19]/80"
|
||
},
|
||
{
|
||
title: "Guardrails",
|
||
mono: "Category guardrails",
|
||
label: "Warnings before the overspend",
|
||
content: <div className="space-y-5">
|
||
<ProgressBar label="Groceries" value={84} />
|
||
<ProgressBar label="Dining" value={109} />
|
||
<ProgressBar label="Transport" value={42} />
|
||
</div>,
|
||
heading: "Warnings before the overspend.",
|
||
description: "Green under 80%, amber to 100%, red past it — and a nudge before it happens.",
|
||
gradient: "from-[#3B2E12] to-[#15120C]",
|
||
bgLight: "bg-[#1F1A0E]/80"
|
||
},
|
||
{
|
||
title: "Credit",
|
||
mono: "FICO 8 · real score",
|
||
label: "+21 in 90 days",
|
||
content: <>
|
||
<p className="mt-4 font-display text-7xl text-paper"><CountUp value={742} /></p>
|
||
<p className="mt-1 text-sm font-semibold text-mint">+21 in 90 days</p>
|
||
<p className="mt-4 text-sm leading-6 text-paper/55">“Pay $410 on your Chase card before the 28th to likely gain ~9 points.”</p>
|
||
</>,
|
||
heading: "Credit coaching with zero bias.",
|
||
description: "We never earn referral fees, so the advice is only ever for you.",
|
||
gradient: "from-[#221C46] to-[#100E1C]",
|
||
bgLight: "bg-[#161230]/80"
|
||
}
|
||
];
|
||
|
||
return (
|
||
<section id="product" className="dark border-t border-white/8 py-28">
|
||
<div className="mx-auto max-w-7xl px-5">
|
||
<Eyebrow>The product</Eyebrow>
|
||
<h2 className="mt-4 max-w-2xl font-display text-5xl leading-[1.05] text-paper md:text-6xl">
|
||
Track everything. <em className="text-mint">Feel</em> everything.
|
||
</h2>
|
||
<p className="mt-5 max-w-xl text-paper/65">
|
||
These aren't screenshots — they're the real components, running live.
|
||
Budgets that breathe, credit that explains itself, goals that feel close.
|
||
</p>
|
||
|
||
<div className="mt-14 grid gap-5 md:grid-cols-3">
|
||
{cards.map((card, idx) => (
|
||
<div
|
||
key={card.title}
|
||
className={`group overflow-hidden rounded-lg bg-gradient-to-b ${card.gradient} p-1 transition-all duration-500 hover:-translate-y-1 ${activeCard === idx ? 'ring-2 ring-mint opacity-100' : 'opacity-60 md:opacity-100'}`}
|
||
>
|
||
<div className={`rounded-[7px] ${card.bgLight} p-6 backdrop-blur`}>
|
||
<p className={`${mono} text-paper/45`}>{card.mono}</p>
|
||
<div className="mt-6">{card.content}</div>
|
||
</div>
|
||
<div className="p-5">
|
||
<p className="font-bold text-paper">{card.heading}</p>
|
||
<p className="mt-1.5 text-sm leading-6 text-paper/55">{card.description}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/* ---------- Stats band ---------- */
|
||
export function StatsBand() {
|
||
const stats: [number, string, string, string][] = [
|
||
[100, "k+", "", "members across the US"],
|
||
[312, "", "$", "average saved per month"],
|
||
[4.8, "★", "", "member rating"],
|
||
[0, "", "$", "referral fees. Ever."],
|
||
];
|
||
return (
|
||
<section className="border-t border-white/8 py-20">
|
||
<div className="mx-auto grid max-w-7xl gap-10 px-5 sm:grid-cols-2 lg:grid-cols-4">
|
||
{stats.map(([value, suffix, prefix, label]) => (
|
||
<div key={label}>
|
||
<p className="font-display text-6xl text-paper">
|
||
<CountUp value={value} suffix={suffix} prefix={prefix} decimals={value % 1 ? 1 : 0} />
|
||
</p>
|
||
<p className={`mt-2 ${mono} text-paper/45`}>{label}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/* ---------- Manifesto ---------- */
|
||
export function Manifesto() {
|
||
return (
|
||
<section className="border-t border-white/8 py-28">
|
||
<div className="mx-auto max-w-4xl px-5 text-center">
|
||
<Eyebrow>Why Aartha exists</Eyebrow>
|
||
<h2 className="mt-6 font-display text-5xl leading-[1.08] text-paper md:text-6xl">
|
||
Every other money app is paid to point you somewhere.
|
||
<em className="text-mint"> We are paid by you — so we point at your goals.</em>
|
||
</h2>
|
||
<div className="mx-auto mt-10 flex max-w-2xl flex-wrap items-center justify-center gap-3">
|
||
{["No ads", "No referral fees", "No selling your data", "No dark patterns", "Cancel anytime"].map((t) => (
|
||
<span key={t} className="inline-flex items-center gap-2 rounded-full border border-white/12 px-4 py-2 text-sm text-paper/75">
|
||
<Check size={14} className="text-mint" /> {t}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/* ---------- Pricing ---------- */
|
||
export function Pricing() {
|
||
return (
|
||
<section id="pricing" className="border-t border-white/8 py-28">
|
||
<div className="mx-auto max-w-7xl px-5">
|
||
<Eyebrow>Pricing</Eyebrow>
|
||
<h2 className="mt-4 max-w-2xl font-display text-5xl leading-[1.05] text-paper">
|
||
Honest tiers. No hidden incentives.
|
||
</h2>
|
||
<div className="mt-12 grid gap-4 md:grid-cols-4">
|
||
{prices.map(([name, price, body]) => {
|
||
const popular = name === "Plus";
|
||
return (
|
||
<div
|
||
key={name}
|
||
className={`rounded-lg border p-6 transition hover:-translate-y-1 ${
|
||
popular ? "border-mint bg-mint/[.06]" : "border-white/10 bg-white/[.03]"
|
||
}`}
|
||
>
|
||
{popular ? (
|
||
<p className={`${mono} text-mint`}>Most popular</p>
|
||
) : (
|
||
<p className={`${mono} text-paper/35`}> </p>
|
||
)}
|
||
<h3 className="mt-3 text-lg font-bold text-paper">{name}</h3>
|
||
<p className="mt-1 font-display text-5xl text-paper">{price}</p>
|
||
<p className="mt-3 min-h-12 text-sm leading-6 text-paper/55">{body}</p>
|
||
<Link
|
||
href="/dashboard"
|
||
className={`mt-6 flex items-center justify-center gap-2 rounded-full py-2.5 text-sm font-bold transition ${
|
||
popular ? "bg-mint text-ink hover:bg-paper" : "border border-white/15 text-paper/85 hover:border-white/40"
|
||
}`}
|
||
>
|
||
Get started <ArrowRight size={14} />
|
||
</Link>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/* ---------- FAQ ---------- */
|
||
const faqs: [string, string][] = [
|
||
["What does subscription-only actually mean?", "Your subscription is our only revenue. No ads, no referral fees, no selling your data. When Aartha recommends a card or an action, there is no financial incentive behind it — that's structural, not a promise."],
|
||
["Is my bank data safe?", "Connections run through Plaid and MX with bank-grade encryption. Aartha never sees or stores your bank credentials, and your data is never sold or shared."],
|
||
["Is this real financial advice?", "Aartha's AI coach provides educational financial information, not regulated financial advice. It explains what it noticed, why it matters, and a suggested action — you decide."],
|
||
["What happens if I cancel?", "Cancel in two taps — no guilt screens. Your data stays exportable for 90 days, then it's deleted. We tell you the exact date."],
|
||
["How is this different from Monarch or Credit Karma?", "Monarch shows you your money; Credit Karma is paid to recommend products. Aartha actively coaches you toward your goals and is structurally unable to profit from biased advice."],
|
||
];
|
||
|
||
export function FAQ() {
|
||
const [open, setOpen] = useState<number | null>(0);
|
||
return (
|
||
<section id="faq" className="border-t border-white/8 py-28">
|
||
<div className="mx-auto max-w-3xl px-5">
|
||
<Eyebrow>FAQ</Eyebrow>
|
||
<h2 className="mt-4 font-display text-5xl text-paper">Clear answers.</h2>
|
||
<div className="mt-10 divide-y divide-white/8 border-y border-white/8">
|
||
{faqs.map(([q, a], i) => (
|
||
<div key={q}>
|
||
<button
|
||
onClick={() => setOpen(open === i ? null : i)}
|
||
className="flex w-full items-center justify-between gap-4 py-5 text-left"
|
||
>
|
||
<span className="font-semibold text-paper">{q}</span>
|
||
{open === i ? <Minus size={16} className="flex-none text-mint" /> : <Plus size={16} className="flex-none text-paper/40" />}
|
||
</button>
|
||
{open === i && <p className="animate-fadeUp pb-6 text-sm leading-7 text-paper/60">{a}</p>}
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|
||
|
||
/* ---------- CTA + Footer ---------- */
|
||
export function FooterCTA() {
|
||
return (
|
||
<footer className="border-t border-white/8">
|
||
<div className="mx-auto max-w-7xl px-5 py-28 text-center">
|
||
<p className="font-display text-6xl leading-[1.05] text-paper md:text-7xl">
|
||
Your goal is next.
|
||
</p>
|
||
<p className={`mx-auto mt-5 max-w-md ${mono} text-paper/45`}>
|
||
Join 100,000+ members who've made peace with their money
|
||
</p>
|
||
<Link href="/dashboard" className="mt-9 inline-flex items-center gap-2 rounded-full bg-mint px-8 py-4 text-sm font-bold text-ink transition hover:bg-paper">
|
||
Get started free <ArrowRight size={16} />
|
||
</Link>
|
||
<p className="mt-4 text-xs text-paper/35">No credit card required · Cancel anytime</p>
|
||
</div>
|
||
<div className="border-t border-white/8">
|
||
<div className="mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 px-5 py-8">
|
||
<span className="flex items-center gap-2 font-display text-lg text-paper"><LeafMark size={20} /> Aartha</span>
|
||
<nav className={`flex flex-wrap gap-6 ${mono} text-paper/45`}>
|
||
<a href="#product" className="hover:text-paper">Product</a>
|
||
<a href="#pricing" className="hover:text-paper">Pricing</a>
|
||
<a href="#faq" className="hover:text-paper">FAQ</a>
|
||
<a href="#" className="hover:text-paper">Privacy</a>
|
||
<a href="#" className="hover:text-paper">Security</a>
|
||
</nav>
|
||
<span className="text-xs text-paper/35">© 2026 Aartha Technologies, Inc. · Your money. Your truth. Zero conflict.</span>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
);
|
||
}
|