From c48a11b13cc3b7b2f1d4e6025f556d74c759b295 Mon Sep 17 00:00:00 2001 From: Sunil Prasad Date: Sun, 12 Jul 2026 14:10:48 -0700 Subject: [PATCH 1/2] feat: Add smooth 5-second auto-rotation to Hero stories and ProductShowcase cards - 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 --- app/page.tsx | 12 +- components/marketing/Hero.tsx | 156 +++++++++---- components/marketing/Navbar.tsx | 31 ++- components/marketing/Sections.tsx | 289 ++++++++++++++++++++----- components/marketing/StoryBackdrop.tsx | 41 ++++ components/mobile/MobileScreens.tsx | 12 +- components/mobile/StoryOnboarding.tsx | 46 ++++ components/ui/CountUp.tsx | 51 +++++ components/ui/Logo.tsx | 18 ++ lib/stories.ts | 98 +++++++++ 10 files changed, 638 insertions(+), 116 deletions(-) create mode 100644 components/marketing/StoryBackdrop.tsx create mode 100644 components/mobile/StoryOnboarding.tsx create mode 100644 components/ui/CountUp.tsx create mode 100644 lib/stories.ts diff --git a/app/page.tsx b/app/page.tsx index 077db48..7975c95 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,16 +1,18 @@ import { Hero } from "@/components/marketing/Hero"; import { Navbar } from "@/components/marketing/Navbar"; -import { CTA, FeatureGrid, PersonaGrid, Pricing } from "@/components/marketing/Sections"; +import { FAQ, FooterCTA, Manifesto, Pricing, ProductShowcase, StatsBand } from "@/components/marketing/Sections"; export default function HomePage() { return ( -
+
- - + + + - + +
); } diff --git a/components/marketing/Hero.tsx b/components/marketing/Hero.tsx index c6a7226..2f9b1fb 100644 --- a/components/marketing/Hero.tsx +++ b/components/marketing/Hero.tsx @@ -1,48 +1,126 @@ -import { ArrowRight, ShieldCheck, Sparkles } from "lucide-react"; -import { AreaChart } from "@/components/charts/AreaChart"; -import { ButtonLink } from "@/components/ui/Button"; -import { Card, StatCard } from "@/components/ui/Card"; -import { Chip } from "@/components/ui/Chip"; +"use client"; + +import { useEffect, useState } from "react"; +import { ArrowRight, RefreshCcw, Sparkles } from "lucide-react"; +import Link from "next/link"; +import { StoryBackdrop } from "@/components/marketing/StoryBackdrop"; +import { randomStory, type GoalStory } from "@/lib/stories"; + +const askAnswers: [string, string][] = [ + ["Can I afford a $2,400 vacation in December?", "Yes — set aside $400/month starting now and your flex budget stays green the whole way."], + ["When can I buy my first home?", "At your current savings pace, a 10% down payment on a $380k home is ~34 months out. Two changes could make it 26."], + ["Which card should I use for groceries?", "Sapphire Preferred — 3x points beats your flat 2% card. Worth about $18 more this month."], + ["When can I retire?", "Holding your 14% savings rate, a comfortable retirement at 63 is on track. Bump to 17% and it's 61."], +]; export function Hero() { + const [story, setStory] = useState(null); + const [askIndex, setAskIndex] = useState(0); + const [asked, setAsked] = useState<[string, string] | null>(null); + const [input, setInput] = useState(""); + const [fadeOut, setFadeOut] = useState(false); + + useEffect(() => { + setStory(randomStory()); + const t = setInterval(() => setAskIndex((i) => (i + 1) % askAnswers.length), 3500); + return () => clearInterval(t); + }, []); + + useEffect(() => { + const storyInterval = setInterval(() => { + setFadeOut(true); + setTimeout(() => { + setStory((s) => randomStory(s?.id)); + setFadeOut(false); + }, 500); + }, 5000); + return () => clearInterval(storyInterval); + }, []); + + const ask = () => { + const typed = input.trim().toLowerCase(); + const match = + askAnswers.find(([q]) => typed && q.toLowerCase().includes(typed.slice(0, 12))) ?? + askAnswers[askIndex]; + setAsked(match); + setInput(""); + }; + return ( -
-
-
- Subscription-only · zero ads · zero referral bias -

- Your money app should help you move, not just watch. -

-

- Aartha unifies budgets, cash flow, credit, rewards, goals, and AI coaching into one calm finance cockpit built around the user's wellbeing. -

-
- View dashboard - Mobile screens +
+ {story &&
} + {/* extra cinematic vignette */} +
+ +
+

+ Subscription-only · Zero ads · Zero referral bias +

+

+ Own your story. +

+

+ Aartha is the personal finance app that works only for you — budgets, credit, + rewards, and an AI coach pointed at the life you're building. +

+ + {/* Ask Aartha — interactive, right in the hero */} +
+
+ + setInput(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && ask()} + placeholder={askAnswers[askIndex][0]} + className="w-full bg-transparent text-sm text-paper placeholder:text-paper/45 focus:outline-none" + /> +
-
- Bank-grade posture - Ask Aartha AI coach +

+ Track everything · Ask anything · Trust the answer +

+ {asked && ( +
+

{asked[0]}

+

{asked[1]}

+

Sample answer · Educational guidance only, not regulated financial advice.

+
+ )} +
+ +
+ + Get started + + + See the product + +
+
+ + {/* Story caption — the human moment behind this visit */} + {story && ( +
+
+
+

{story.emoji} {story.tag} — a different story every visit

+

{story.headline}

+
+
- -
- - - -
-
-
-
-

Cash-flow forecast

-

$7,420

-
- Next 30 days -
- -
-
-
+ )}
); } diff --git a/components/marketing/Navbar.tsx b/components/marketing/Navbar.tsx index 2c02e04..fd9ae93 100644 --- a/components/marketing/Navbar.tsx +++ b/components/marketing/Navbar.tsx @@ -1,18 +1,29 @@ -import { ButtonLink } from "@/components/ui/Button"; -import { Logo } from "@/components/ui/Logo"; +import Link from "next/link"; +import { LeafMark } from "@/components/ui/Logo"; +/** + * Minimal cinematic nav — two destinations, two actions. Nothing else. + */ export function Navbar() { return ( -
+
- -
); diff --git a/components/marketing/Sections.tsx b/components/marketing/Sections.tsx index 0372a8c..d2353b4 100644 --- a/components/marketing/Sections.tsx +++ b/components/marketing/Sections.tsx @@ -1,44 +1,100 @@ -import { personas, prices } from "@/lib/data"; -import { Card } from "@/components/ui/Card"; -import { Chip } from "@/components/ui/Chip"; -import { SectionHeading } from "@/components/ui/SectionHeading"; -import { ButtonLink } from "@/components/ui/Button"; +"use client"; -const features = [ - ["Flex budgeting", "Speedometer-style daily allowance after fixed expenses and goals."], - ["Credit clarity", "Real FICO 8 factors translated into actions users can actually take."], - ["Rewards intelligence", "Recommend the best card for each purchase based on live transactions."], - ["Shared finances", "Partner and roommate views with privacy-aware collaboration."] -]; +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"; -export function FeatureGrid() { - return ( -
- -
- {features.map(([title, body]) => ( - -

{title}

-

{body}

-
- ))} -
-
- ); +const mono = "font-mono text-[10px] uppercase tracking-[.25em]"; + +function Eyebrow({ children }: { children: string }) { + return

{children}

; } -export function PersonaGrid() { +/* ---------- 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: , + 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:
+ + + +
, + 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: <> +

+

+21 in 90 days

+

“Pay $410 on your Chase card before the 28th to likely gain ~9 points.”

+ , + 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 ( -
+
- -
- {personas.map(([name, body], index) => ( - - {String(index + 1).padStart(2, "0")} -

{name}

-

{body}

-
+ The product +

+ Track everything. Feel everything. +

+

+ These aren't screenshots — they're the real components, running live. + Budgets that breathe, credit that explains itself, goals that feel close. +

+ +
+ {cards.map((card, idx) => ( +
+
+

{card.mono}

+
{card.content}
+
+
+

{card.heading}

+

{card.description}

+
+
))}
@@ -46,33 +102,160 @@ export function PersonaGrid() { ); } -export function Pricing() { +/* ---------- 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 ( -
- -
- {prices.map(([name, price, body]) => ( - - {name === "Plus" ? Most popular : null} -

{name}

-

{price}

-

{body}

- Preview tier -
+
+
+ {stats.map(([value, suffix, prefix, label]) => ( +
+

+ +

+

{label}

+
))}
); } -export function CTA() { +/* ---------- Manifesto ---------- */ +export function Manifesto() { return ( -
-
-

Ready for a component-based handoff.

-

This prototype is structured for product review today and engineering continuation tomorrow.

- Explore app prototype +
+
+ Why Aartha exists +

+ Every other money app is paid to point you somewhere. + We are paid by you — so we point at your goals. +

+
+ {["No ads", "No referral fees", "No selling your data", "No dark patterns", "Cancel anytime"].map((t) => ( + + {t} + + ))} +
); } + +/* ---------- Pricing ---------- */ +export function Pricing() { + return ( +
+
+ Pricing +

+ Honest tiers. No hidden incentives. +

+
+ {prices.map(([name, price, body]) => { + const popular = name === "Plus"; + return ( +
+ {popular ? ( +

Most popular

+ ) : ( +

 

+ )} +

{name}

+

{price}

+

{body}

+ + Get started + +
+ ); + })} +
+
+
+ ); +} + +/* ---------- 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(0); + return ( +
+
+ FAQ +

Clear answers.

+
+ {faqs.map(([q, a], i) => ( +
+ + {open === i &&

{a}

} +
+ ))} +
+
+
+ ); +} + +/* ---------- CTA + Footer ---------- */ +export function FooterCTA() { + return ( +
+
+

+ Your goal is next. +

+

+ Join 100,000+ members who've made peace with their money +

+ + Get started free + +

No credit card required · Cancel anytime

+
+
+
+ Aartha + + © 2026 Aartha Technologies, Inc. · Your money. Your truth. Zero conflict. +
+
+
+ ); +} diff --git a/components/marketing/StoryBackdrop.tsx b/components/marketing/StoryBackdrop.tsx new file mode 100644 index 0000000..cf3e434 --- /dev/null +++ b/components/marketing/StoryBackdrop.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { useState } from "react"; +import type { GoalStory } from "@/lib/stories"; + +/** + * Full-bleed background video for a goal story, with a legibility scrim and a + * graceful gradient fallback (used while loading or if the video fails). + * Reusable: fills whatever positioned container it's placed in. + */ +export function StoryBackdrop({ story, dim = "strong" }: { story: GoalStory; dim?: "strong" | "soft" }) { + const [failed, setFailed] = useState(false); + + return ( +
+ {/* gradient fallback always behind the video */} +
+ {!failed && ( +