Compare commits
2 Commits
d731551986
...
7a702d6d2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a702d6d2c | ||
|
|
28cefdc7e7 |
@ -1,5 +1,6 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../components/background";
|
||||
import { ContactSection } from "../../components/contact-section";
|
||||
import { DemoCta } from "../../components/demo-cta";
|
||||
import { FaqSection } from "../../components/faq-section";
|
||||
@ -68,12 +69,10 @@ export default function AboutPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<section className="grid gap-12 lg:grid-cols-[1fr_1fr] items-center">
|
||||
<div className="space-y-8 animate-slide-up">
|
||||
@ -81,7 +80,8 @@ export default function AboutPage() {
|
||||
Our Story
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl leading-tight">
|
||||
LedgerOne keeps every transaction ready for audits, review, and action.
|
||||
LedgerOne keeps every transaction ready for{" "}
|
||||
<span className="heading-hero-accent">ready for audits, review, and action.</span>
|
||||
</h1>
|
||||
<div className="space-y-4 text-lg text-muted-foreground">
|
||||
<p>
|
||||
@ -96,16 +96,10 @@ export default function AboutPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="px-6 py-3 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors shadow-glow-teal"
|
||||
>
|
||||
<Link href="/pricing" className="btn-primary">
|
||||
View pricing
|
||||
</Link>
|
||||
<Link
|
||||
href="/faq"
|
||||
className="px-6 py-3 rounded-lg bg-background border border-border text-foreground font-medium hover:bg-secondary transition-colors"
|
||||
>
|
||||
<Link href="/faq" className="btn-secondary">
|
||||
Explore FAQs
|
||||
</Link>
|
||||
</div>
|
||||
@ -120,7 +114,7 @@ export default function AboutPage() {
|
||||
height={900}
|
||||
className="h-full w-full object-cover opacity-90"
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-background/90 backdrop-blur-sm p-6 border-t border-border">
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-background/90 backdrop-blur-sm p-6">
|
||||
<p className="text-xs font-bold uppercase tracking-wider text-primary">
|
||||
Built for US operators
|
||||
</p>
|
||||
@ -133,7 +127,7 @@ export default function AboutPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-24 grid gap-8 md:grid-cols-3">
|
||||
<section className="mt-16 grid gap-8 md:grid-cols-3">
|
||||
{values.map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
@ -148,7 +142,7 @@ export default function AboutPage() {
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section className="mt-24 text-center">
|
||||
<section className="mt-16 text-center">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground backdrop-blur-sm mb-6">
|
||||
Leadership
|
||||
</div>
|
||||
@ -165,7 +159,7 @@ export default function AboutPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-24 rounded-3xl bg-secondary/30 border border-border p-8 sm:p-12">
|
||||
<section className="mt-16 rounded-3xl bg-secondary/30 p-6 sm:p-8">
|
||||
<div className="grid gap-12 lg:grid-cols-[0.9fr_1.1fr]">
|
||||
<div className="space-y-6">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-background border border-border text-xs font-medium text-muted-foreground">
|
||||
@ -196,7 +190,9 @@ export default function AboutPage() {
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { apiFetch } from "@/lib/api";
|
||||
|
||||
function GoogleCallbackContent() {
|
||||
export default function GoogleCallbackPage() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
|
||||
@ -83,22 +83,3 @@ function GoogleCallbackContent() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GoogleCallbackFallback() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background">
|
||||
<div className="glass-panel rounded-2xl p-10 text-center max-w-sm w-full shadow-lg">
|
||||
<div className="h-12 w-12 rounded-full border-4 border-primary border-t-transparent animate-spin mx-auto mb-4" />
|
||||
<p className="text-sm text-muted-foreground">Connecting your Google account...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GoogleCallbackPage() {
|
||||
return (
|
||||
<Suspense fallback={<GoogleCallbackFallback />}>
|
||||
<GoogleCallbackContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@ -63,11 +63,11 @@ export default function BlogPage() {
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<main className="flex-1 pt-24 pb-16 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16 animate-slide-up">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10 animate-slide-up">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
Insights for modern finance teams.
|
||||
</h1>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../components/background";
|
||||
import { ContactSection } from "../../components/contact-section";
|
||||
import { DemoCta } from "../../components/demo-cta";
|
||||
import { FaqSection } from "../../components/faq-section";
|
||||
@ -8,6 +9,9 @@ import { SiteHeader } from "../../components/site-header";
|
||||
import { defaultFaqs } from "../../data/faq";
|
||||
import { siteInfo } from "../../data/site";
|
||||
|
||||
const inputClass =
|
||||
"block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all";
|
||||
|
||||
export const metadata = {
|
||||
title: "Book a Demo",
|
||||
description:
|
||||
@ -37,60 +41,60 @@ export default function BookDemoPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="marketing min-h-screen">
|
||||
<div className="relative overflow-hidden">
|
||||
<div className="halo absolute inset-0" />
|
||||
<div className="grid-dots absolute inset-0" />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="relative mx-auto max-w-6xl px-6 pb-12 pt-12">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
||||
<section className="grid gap-10 lg:grid-cols-[1.1fr_0.9fr]">
|
||||
<div className="space-y-6">
|
||||
<p className="text-xs uppercase tracking-[0.4em] text-muted">Book a demo</p>
|
||||
<h1 className="text-4xl font-semibold leading-tight">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground backdrop-blur-sm">
|
||||
Book a demo
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl leading-tight">
|
||||
Schedule time with the LedgerOne team.
|
||||
</h1>
|
||||
<p className="text-sm text-muted">
|
||||
<p className="text-lg text-muted-foreground">
|
||||
We will walk you through account connections, rule automation, and
|
||||
audit-ready exports based on your workflow.
|
||||
</p>
|
||||
<div className="rounded-3xl border border-ink/10 bg-white/80 p-6 shadow-soft">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 p-6 shadow-glass backdrop-blur-sm">
|
||||
<form className="grid gap-4 md:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Full name
|
||||
</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className={inputClass}
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Work email
|
||||
</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className={inputClass}
|
||||
type="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Preferred date
|
||||
</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className={inputClass}
|
||||
type="date"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
Team size
|
||||
</label>
|
||||
<select className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm">
|
||||
<select className={inputClass}>
|
||||
<option>1-5</option>
|
||||
<option>6-20</option>
|
||||
<option>21-50</option>
|
||||
@ -98,45 +102,58 @@ export default function BookDemoPage() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
What should we focus oni
|
||||
<label className="block text-sm font-medium text-foreground">
|
||||
What should we focus on?
|
||||
</label>
|
||||
<textarea className="min-h-[120px] w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm" />
|
||||
<textarea
|
||||
className={`${inputClass} min-h-[120px] resize-y`}
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full rounded-2xl bg-ink px-4 py-3 text-sm font-semibold text-haze"
|
||||
>
|
||||
<button type="submit" className="btn-primary w-full py-3">
|
||||
Request demo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<p className="mt-4 text-xs text-muted">
|
||||
Prefer emaili Reach us at{" "}
|
||||
<Link className="underline" href="/contact">
|
||||
<p className="mt-4 text-xs text-muted-foreground">
|
||||
Prefer email? Reach us at{" "}
|
||||
<Link className="text-primary hover:underline" href="/contact">
|
||||
the contact page
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4 rounded-3xl border border-ink/10 bg-white/80 p-6 shadow-soft">
|
||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">What you'll see</p>
|
||||
<ul className="space-y-3 text-sm text-muted">
|
||||
<li>Connect accounts and review the raw ledger flow.</li>
|
||||
<li>Watch rule automation run and inspect the audit trail.</li>
|
||||
<li>Export a complete ledger package ready for review.</li>
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 p-6 shadow-glass backdrop-blur-sm space-y-4">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
What you'll see
|
||||
</p>
|
||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||
<li className="flex gap-2">
|
||||
<span className="text-primary mt-0.5">•</span>
|
||||
Connect accounts and review the raw ledger flow.
|
||||
</li>
|
||||
<li className="flex gap-2">
|
||||
<span className="text-primary mt-0.5">•</span>
|
||||
Watch rule automation run and inspect the audit trail.
|
||||
</li>
|
||||
<li className="flex gap-2">
|
||||
<span className="text-primary mt-0.5">•</span>
|
||||
Export a complete ledger package ready for review.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
<div className="relative z-10">
|
||||
<ContactSection />
|
||||
<DemoCta />
|
||||
<FaqSection limit={8} />
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<ContactSection />
|
||||
<DemoCta />
|
||||
<FaqSection limit={8} />
|
||||
<PageSchema schema={schema} />
|
||||
<SiteFooter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -22,12 +23,12 @@ export default function CompareCopilotPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground mb-6">
|
||||
Comparison
|
||||
</div>
|
||||
@ -69,17 +70,16 @@ export default function CompareCopilotPage() {
|
||||
|
||||
<div className="mt-16 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-6">Work from anywhere.</h2>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start your free trial
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -22,12 +23,12 @@ export default function CompareQuickenPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground mb-6">
|
||||
Comparison
|
||||
</div>
|
||||
@ -69,17 +70,16 @@ export default function CompareQuickenPage() {
|
||||
|
||||
<div className="mt-16 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-6">Upgrade your finance stack.</h2>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start your free trial
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -22,12 +23,12 @@ export default function CompareSpreadsheetsPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground mb-6">
|
||||
Comparison
|
||||
</div>
|
||||
@ -70,17 +71,16 @@ export default function CompareSpreadsheetsPage() {
|
||||
|
||||
<div className="mt-16 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to upgrade?</h2>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start your free trial
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -22,12 +23,12 @@ export default function CompareYnabPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground mb-6">
|
||||
Comparison
|
||||
</div>
|
||||
@ -69,17 +70,16 @@ export default function CompareYnabPage() {
|
||||
|
||||
<div className="mt-16 text-center">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to scale?</h2>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start your free trial
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -37,11 +37,11 @@ export default function ContactPage() {
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<main className="flex-1 pt-24 pb-16 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16 animate-slide-up">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10 animate-slide-up">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground backdrop-blur-sm mb-6">
|
||||
Support & Sales
|
||||
</div>
|
||||
|
||||
282
app/demo/page.tsx
Normal file
282
app/demo/page.tsx
Normal file
@ -0,0 +1,282 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
|
||||
const balances = [18420, 19280, 20540, 19980, 21450, 22890, 22120];
|
||||
const maxBalance = Math.max(...balances);
|
||||
|
||||
const expenses = [
|
||||
{ label: "Rent & mortgage", value: 2800 },
|
||||
{ label: "Payroll", value: 9200 },
|
||||
{ label: "Software & tools", value: 1450 },
|
||||
{ label: "Vendors", value: 2280 },
|
||||
];
|
||||
|
||||
const aiMessages = [
|
||||
"You’re on track to finish the month with a $6,920 surplus if spending stays at the current pace.",
|
||||
"Dining is trending 14% above your usual pattern. Consider capping at $620 to stay on target.",
|
||||
"You can safely move $1,500 into savings without dropping below your $10k buffer.",
|
||||
];
|
||||
|
||||
export default function DemoPage() {
|
||||
const [aiIndex, setAiIndex] = useState(0);
|
||||
const [aiVisible, setAiVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: NodeJS.Timeout;
|
||||
let interval: NodeJS.Timeout;
|
||||
|
||||
const startLoop = () => {
|
||||
setAiVisible(false);
|
||||
timeout = setTimeout(() => {
|
||||
setAiVisible(true);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
startLoop();
|
||||
interval = setInterval(() => {
|
||||
setAiIndex((prev) => (prev + 1) % aiMessages.length);
|
||||
startLoop();
|
||||
}, 5000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 text-foreground flex flex-col">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-20 pb-12">
|
||||
<div className="mx-auto max-w-6xl px-6 lg:px-8 space-y-8">
|
||||
<header className="space-y-3">
|
||||
<p className="text-xs font-semibold tracking-[0.25em] text-emerald-400 uppercase">
|
||||
Demo · LedgerOne
|
||||
</p>
|
||||
<h1 className="text-3xl sm:text-4xl font-semibold tracking-tight text-slate-50">
|
||||
AI-powered cash control dashboard
|
||||
</h1>
|
||||
<p className="text-sm sm:text-base text-slate-400 max-w-2xl">
|
||||
This is a looping, demo-only view designed for screen recordings. All data is fake but
|
||||
behaves like a live, AI-assisted finance cockpit.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="grid gap-6 lg:grid-cols-3">
|
||||
{/* Left: animated cashflow graph */}
|
||||
<section className="lg:col-span-2 rounded-3xl border border-slate-800 bg-slate-900/60 px-5 pt-4 pb-6 shadow-[0_30px_120px_rgba(15,23,42,0.9)] backdrop-blur-xl">
|
||||
<div className="flex items-center justify-between gap-3 mb-3">
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs font-medium text-slate-400">Projected balance · next 30 days</p>
|
||||
<p className="text-sm font-semibold text-slate-50">
|
||||
$22,890 <span className="text-emerald-400 text-xs font-normal">▲ +$3,410</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-[11px] text-slate-400">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-emerald-400" />
|
||||
<span>Balance</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-sky-400" />
|
||||
<span>Income</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-rose-400" />
|
||||
<span>Outflows</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Animated bar/line combo chart */}
|
||||
<div className="relative h-60 rounded-2xl bg-gradient-to-b from-slate-900/60 to-slate-950/90 overflow-hidden">
|
||||
<svg viewBox="0 0 100 40" className="absolute inset-0 opacity-40">
|
||||
<defs>
|
||||
<linearGradient id="balanceLine" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="#22c55e" />
|
||||
<stop offset="50%" stopColor="#38bdf8" />
|
||||
<stop offset="100%" stopColor="#a855f7" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<motion.path
|
||||
d="
|
||||
M 0 30
|
||||
C 15 28, 25 26, 35 24
|
||||
S 55 20, 65 18
|
||||
S 85 16, 100 14
|
||||
"
|
||||
fill="none"
|
||||
stroke="url(#balanceLine)"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div className="absolute inset-x-6 bottom-4 flex items-end justify-between gap-4">
|
||||
{balances.map((v, i) => {
|
||||
const height = (v / maxBalance) * 100;
|
||||
return (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="flex-1 flex flex-col items-center gap-1"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 + i * 0.08 }}
|
||||
>
|
||||
<motion.div
|
||||
className="w-full rounded-t-full bg-gradient-to-t from-slate-800 via-sky-500 to-emerald-400 shadow-[0_0_25px_rgba(56,189,248,0.5)]"
|
||||
style={{ height: `${height}%` }}
|
||||
animate={{ scaleY: [0.7, 1, 0.85, 1] }}
|
||||
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut", delay: i * 0.05 }}
|
||||
/>
|
||||
<span className="text-[10px] text-slate-500">D{i + 1}</span>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="absolute left-1/2 bottom-3 -translate-x-1/2 rounded-full bg-amber-500/15 px-3 py-1.5 text-[11px] text-amber-100 ring-1 ring-amber-400/50 flex items-center gap-2">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-amber-300 animate-pulse" />
|
||||
<span>Balance dip in 6 days · adjust discretionary by ~12%</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Right: budget ring + expense breakdown */}
|
||||
<section className="space-y-4">
|
||||
<div className="rounded-3xl border border-slate-800 bg-slate-900/70 p-4 shadow-[0_20px_80px_rgba(15,23,42,0.9)] backdrop-blur-xl">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-slate-400">Monthly budget</p>
|
||||
<p className="text-sm font-semibold text-slate-50">
|
||||
$18,400{" "}
|
||||
<span className="ml-1 text-[11px] font-normal text-emerald-400">Safe to spend: $4,120</span>
|
||||
</p>
|
||||
</div>
|
||||
<span className="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-medium text-emerald-300">
|
||||
Healthy
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Animated progress ring */}
|
||||
<div className="relative h-20 w-20">
|
||||
<svg viewBox="0 0 36 36" className="h-20 w-20 -rotate-90">
|
||||
<path
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831"
|
||||
fill="none"
|
||||
stroke="rgba(30,64,175,0.35)"
|
||||
strokeWidth="3"
|
||||
/>
|
||||
<motion.path
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831"
|
||||
fill="none"
|
||||
stroke="url(#demoRingGradient)"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
animate={{ strokeDasharray: ["55, 100", "78, 100", "65, 100"] }}
|
||||
transition={{ duration: 4.5, repeat: Infinity, ease: "easeInOut" }}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="demoRingGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="#22c55e" />
|
||||
<stop offset="50%" stopColor="#38bdf8" />
|
||||
<stop offset="100%" stopColor="#a855f7" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center gap-0.5">
|
||||
<motion.span
|
||||
className="text-sm font-semibold text-slate-50"
|
||||
animate={{ opacity: [0.7, 1, 0.7] }}
|
||||
transition={{ duration: 2.4, repeat: Infinity }}
|
||||
>
|
||||
74%
|
||||
</motion.span>
|
||||
<span className="text-[10px] text-slate-400">used</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-2">
|
||||
{expenses.map((e) => {
|
||||
const pct = e.value / 18400;
|
||||
return (
|
||||
<div key={e.label} className="space-y-0.5">
|
||||
<div className="flex items-center justify-between text-[11px] text-slate-300">
|
||||
<span>{e.label}</span>
|
||||
<span className="text-slate-400">
|
||||
${e.value.toLocaleString()}{" "}
|
||||
<span className="text-slate-500">
|
||||
· {(pct * 100).toFixed(0)}%
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-1.5 rounded-full bg-slate-800 overflow-hidden">
|
||||
<motion.div
|
||||
className="h-full rounded-full bg-gradient-to-r from-emerald-400 via-sky-400 to-violet-500"
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${Math.min(pct * 100, 100)}%` }}
|
||||
transition={{ duration: 2, repeat: Infinity, repeatType: "reverse", ease: "easeInOut" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI chat card */}
|
||||
<div className="rounded-3xl border border-slate-800 bg-slate-900/70 p-4 shadow-[0_20px_80px_rgba(15,23,42,0.9)] backdrop-blur-xl space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-7 w-7 items-center justify-center rounded-full bg-emerald-500/15 text-[11px] text-emerald-300 shadow-[0_0_22px_rgba(34,197,94,0.7)]">
|
||||
AI
|
||||
</div>
|
||||
<div className="text-xs text-slate-300">
|
||||
<p className="font-medium">LedgerOne Copilot</p>
|
||||
<p className="text-[11px] text-slate-500">Monitors cash flow in real-time</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className="rounded-full bg-slate-800 px-2 py-0.5 text-[10px] text-slate-300">
|
||||
Demo mode
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-[11px]">
|
||||
<div className="w-fit max-w-[90%] rounded-2xl bg-slate-800/90 px-3 py-2 text-slate-100">
|
||||
How much can we safely move into savings this month?
|
||||
</div>
|
||||
<motion.div
|
||||
key={aiIndex}
|
||||
initial={{ opacity: 0, y: 6 }}
|
||||
animate={{ opacity: aiVisible ? 1 : 0, y: aiVisible ? 0 : 4 }}
|
||||
transition={{ duration: 0.4, ease: "easeOut" }}
|
||||
className="ml-auto w-fit max-w-[90%] rounded-2xl bg-emerald-500/10 px-3 py-2 text-emerald-50 ring-1 ring-emerald-400/30"
|
||||
>
|
||||
{aiMessages[aiIndex]}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ export default function ExportsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border" />
|
||||
<div className="mt-6" />
|
||||
|
||||
{/* Export cards */}
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
|
||||
@ -35,12 +35,12 @@ export default function FaqPage() {
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<main className="flex-1 pt-24 pb-16 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||
|
||||
<div className="max-w-4xl mx-auto px-6 lg:px-8">
|
||||
<div className="glass-panel rounded-3xl p-8 sm:p-12 shadow-sm">
|
||||
<div className="mb-12">
|
||||
<div className="glass-panel rounded-3xl p-6 sm:p-10 shadow-sm">
|
||||
<div className="mb-8">
|
||||
<p className="text-xs uppercase tracking-[0.2em] text-primary font-bold mb-4">FAQ</p>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
Common questions, clear answers.
|
||||
@ -50,9 +50,9 @@ export default function FaqPage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-12">
|
||||
<div className="space-y-8">
|
||||
{defaultFaqs.map((faq, index) => (
|
||||
<div key={index} className="border-b border-border pb-8 last:border-0 last:pb-0">
|
||||
<div key={index} className="pb-6 last:pb-0">
|
||||
<h3 className="text-lg font-bold text-foreground uppercase tracking-wide mb-3">
|
||||
{index + 1}. {faq.question}
|
||||
</h3>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -23,18 +24,19 @@ export default function CashFlowPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<div className="animate-slide-up">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground mb-6">
|
||||
Cash Flow
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl mb-6 leading-tight">
|
||||
Know exactly where your money is going.
|
||||
Know exactly where your {" "}
|
||||
<span className="heading-hero-accent">money is going.</span>
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground mb-8">
|
||||
Stop guessing. LedgerOne gives you a crystal-clear view of your income versus expenses across all your accounts. Spot trends, identify leaks, and plan for the future.
|
||||
@ -54,10 +56,7 @@ export default function CashFlowPage() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start tracking cash flow
|
||||
</Link>
|
||||
</div>
|
||||
@ -77,7 +76,9 @@ export default function CashFlowPage() {
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../../components/background";
|
||||
import { SiteFooter } from "../../../components/site-footer";
|
||||
import { SiteHeader } from "../../../components/site-header";
|
||||
import { PageSchema } from "../../../components/page-schema";
|
||||
@ -23,12 +24,12 @@ export default function ReportsPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24">
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<div className="order-2 lg:order-1 relative animate-fade-in delay-200">
|
||||
<div className="absolute -inset-4 bg-gradient-to-r from-blue-500/20 to-purple-500/20 rounded-3xl blur-xl opacity-50"></div>
|
||||
<div className="relative rounded-2xl border border-border bg-background/50 backdrop-blur-xl shadow-2xl overflow-hidden p-2">
|
||||
@ -46,7 +47,8 @@ export default function ReportsPage() {
|
||||
Reporting
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl mb-6 leading-tight">
|
||||
Reports that make your accountant smile.
|
||||
Reports that make your {" "}
|
||||
<span className="heading-hero-accent">accountant smile.</span>
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground mb-8">
|
||||
Don't scramble at tax time. LedgerOne keeps your data organized and audit-ready year-round. Build custom reports and export them in seconds.
|
||||
@ -66,10 +68,7 @@ export default function ReportsPage() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-3 text-sm font-bold text-primary-foreground shadow-lg hover:bg-primary/90 transition-all"
|
||||
>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Start building reports
|
||||
</Link>
|
||||
</div>
|
||||
@ -77,7 +76,9 @@ export default function ReportsPage() {
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Background } from "../../components/background";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
|
||||
@ -42,13 +43,16 @@ export default function ForgotPasswordPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex justify-center">
|
||||
<div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center text-primary-foreground font-bold text-xl shadow-glow-teal">
|
||||
<div
|
||||
className="h-12 w-12 rounded-full flex items-center justify-center text-white font-bold text-xl shadow-[0_12px_30px_var(--gradient-glow)]"
|
||||
style={{ background: "linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end))" }}
|
||||
>
|
||||
L1
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +66,7 @@ export default function ForgotPasswordPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="glass-panel py-8 px-4 shadow-xl sm:rounded-xl sm:px-10">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 py-8 px-4 shadow-glass backdrop-blur-sm sm:px-10">
|
||||
{sent ? (
|
||||
<div className="text-center space-y-4">
|
||||
<div className="mx-auto h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center">
|
||||
@ -72,7 +76,7 @@ export default function ForgotPasswordPage() {
|
||||
</div>
|
||||
<p className="text-sm text-foreground font-medium">{status}</p>
|
||||
<p className="text-xs text-muted-foreground">Check your email inbox and spam folder.</p>
|
||||
<Link href="/login" className="inline-flex justify-center rounded-lg bg-primary py-2 px-4 text-sm font-bold text-primary-foreground hover:bg-primary/90 transition-all">
|
||||
<Link href="/login" className="btn-primary inline-flex justify-center py-2.5 px-4">
|
||||
Back to sign in
|
||||
</Link>
|
||||
</div>
|
||||
@ -89,19 +93,16 @@ export default function ForgotPasswordPage() {
|
||||
<input
|
||||
id="email" name="email" type="email" autoComplete="email" required
|
||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full justify-center rounded-lg border border-transparent bg-primary py-2.5 px-4 text-sm font-bold text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<button type="submit" className="btn-primary w-full py-3">
|
||||
Send reset link
|
||||
</button>
|
||||
{isError && status && (
|
||||
<div className="rounded-lg bg-red-500/10 border border-red-500/20 p-4">
|
||||
<p className="text-sm text-center">{status}</p>
|
||||
<p className="text-sm text-center text-red-600 dark:text-red-400">{status}</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
@ -109,7 +110,9 @@ export default function ForgotPasswordPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
157
app/globals.css
157
app/globals.css
@ -6,7 +6,7 @@
|
||||
--font-inter: 'Inter', sans-serif;
|
||||
--font-space: 'Space Grotesk', sans-serif;
|
||||
|
||||
/* Light Mode (Cloud Dancer) */
|
||||
/* Light Mode (hero / site-wide) */
|
||||
--background: #F0EEE9;
|
||||
--foreground: #121212;
|
||||
--primary: #316263;
|
||||
@ -18,6 +18,15 @@
|
||||
--accent: #B6FF3B;
|
||||
--accent-foreground: #121212;
|
||||
--border: #D6D4D0;
|
||||
|
||||
/* Hero gradient (CTA / headlines) - use site-wide */
|
||||
--gradient-start: #6366f1;
|
||||
--gradient-mid: #a855f7;
|
||||
--gradient-end: #34d399;
|
||||
--gradient-glow: rgba(88, 80, 236, 0.45);
|
||||
--gradient-glow-hover: rgba(56, 189, 248, 0.5);
|
||||
--accent-emerald: #10b981;
|
||||
--accent-emerald-muted: rgba(34, 197, 94, 0.35);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@ -61,6 +70,57 @@ body {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Landing hero: animated gradient glow (10–20s loop, subtle) */
|
||||
@keyframes hero-glow-shift {
|
||||
0%, 100% {
|
||||
opacity: 0.35;
|
||||
transform: scale(1) translate(0, 0);
|
||||
}
|
||||
33% {
|
||||
opacity: 0.5;
|
||||
transform: scale(1.08) translate(3%, -2%);
|
||||
}
|
||||
66% {
|
||||
opacity: 0.4;
|
||||
transform: scale(1.04) translate(-2%, 2%);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-hero-glow {
|
||||
animation: hero-glow-shift 15s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Soft gradient base for hero/feature pages: blue → lavender → green (ethereal) */
|
||||
.page-soft-bg {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
#f0f9ff 0%,
|
||||
#e0f2fe 15%,
|
||||
#f5f3ff 40%,
|
||||
#faf5ff 55%,
|
||||
#f0fdf4 75%,
|
||||
#ecfdf5 100%
|
||||
);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
/* Subtle floating animation for gradient blobs */
|
||||
@keyframes blob-float {
|
||||
0%, 100% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
33% {
|
||||
transform: translate(2%, -3%) scale(1.02);
|
||||
}
|
||||
66% {
|
||||
transform: translate(-2%, 2%) scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-blob-float {
|
||||
animation: blob-float 18s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* Mesh Gradient */
|
||||
.mesh-gradient {
|
||||
background-color: #F0EEE9;
|
||||
@ -100,4 +160,99 @@ body {
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--muted-foreground);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
GLOBAL THEME (hero section = source of truth)
|
||||
Use these classes site-wide for consistency.
|
||||
============================================ */
|
||||
|
||||
@layer components {
|
||||
/* Primary CTA - gradient, glow, rounded-full */
|
||||
.btn-primary {
|
||||
@apply inline-flex items-center justify-center rounded-full px-7 py-3 text-sm font-semibold text-white outline-none ring-2 ring-transparent ring-offset-2 ring-offset-background transition-all focus-visible:ring-emerald-400/80;
|
||||
background: linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end));
|
||||
box-shadow: 0 18px 60px var(--gradient-glow);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
box-shadow: 0 22px 70px var(--gradient-glow-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.btn-primary:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Secondary - soft border, backdrop blur */
|
||||
.btn-secondary {
|
||||
@apply inline-flex items-center justify-center rounded-full border border-border px-7 py-3 text-sm font-semibold text-foreground backdrop-blur-md outline-none ring-2 ring-transparent ring-offset-2 ring-offset-background transition-colors focus-visible:ring-emerald-400/80;
|
||||
background-color: color-mix(in srgb, var(--background) 60%, transparent);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: color-mix(in srgb, var(--secondary) 40%, transparent);
|
||||
}
|
||||
|
||||
/* Small primary (e.g. navbar Get Started) */
|
||||
.btn-primary-sm {
|
||||
@apply inline-flex items-center justify-center rounded-full px-5 py-2.5 text-sm font-semibold text-white outline-none ring-2 ring-transparent ring-offset-2 ring-offset-background transition-all focus-visible:ring-emerald-400/80 active:scale-[0.98];
|
||||
background: linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end));
|
||||
box-shadow: 0 18px 60px var(--gradient-glow);
|
||||
}
|
||||
.btn-primary-sm:hover {
|
||||
box-shadow: 0 22px 70px var(--gradient-glow-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Pill / badge (hero "AI-native · Live predictions" style) */
|
||||
.badge-pill {
|
||||
@apply inline-flex items-center gap-2 rounded-full border px-3 py-1 text-[11px] font-medium text-muted-foreground backdrop-blur-md;
|
||||
border-color: color-mix(in srgb, var(--border) 60%, transparent);
|
||||
background-color: color-mix(in srgb, var(--background) 60%, transparent);
|
||||
box-shadow: 0 0 25px var(--accent-emerald-muted);
|
||||
}
|
||||
.badge-pill-dot {
|
||||
@apply h-1.5 w-1.5 rounded-full bg-emerald-400;
|
||||
}
|
||||
|
||||
/* Hero headline - gradient second line */
|
||||
.heading-hero {
|
||||
@apply text-balance text-4xl font-semibold tracking-tight text-foreground sm:text-5xl lg:text-6xl;
|
||||
}
|
||||
.heading-hero-accent {
|
||||
@apply block text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 via-sky-400 to-emerald-400;
|
||||
}
|
||||
|
||||
/* Section headings site-wide */
|
||||
.heading-section {
|
||||
@apply text-3xl font-semibold tracking-tight text-foreground sm:text-4xl;
|
||||
}
|
||||
.heading-section-sub {
|
||||
@apply mt-4 text-base text-muted-foreground sm:text-lg;
|
||||
}
|
||||
|
||||
/* Body lead (hero subtext) */
|
||||
.body-lead {
|
||||
@apply text-pretty text-base text-muted-foreground sm:text-lg;
|
||||
}
|
||||
|
||||
/* Feature bullets (Instant Plaid, Bank-grade, 14-day) */
|
||||
.feature-bullets {
|
||||
@apply flex flex-wrap items-center gap-6 text-xs text-muted-foreground;
|
||||
}
|
||||
.feature-bullet {
|
||||
@apply flex items-center gap-2;
|
||||
}
|
||||
.feature-bullet-dot {
|
||||
@apply h-1.5 w-1.5 rounded-full bg-emerald-400;
|
||||
}
|
||||
|
||||
/* Card shell (hero-style glass) */
|
||||
.card-glass {
|
||||
@apply rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 shadow-glass;
|
||||
}
|
||||
|
||||
/* Nav link - consistent with hero area */
|
||||
.nav-link {
|
||||
@apply text-sm font-medium text-muted-foreground transition-colors hover:text-foreground;
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ export const metadata: Metadata = {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${inter.variable} ${space.variable} font-sans`}>
|
||||
<body className={`${inter.variable} ${space.variable} font-sans min-h-screen bg-background text-foreground`}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Suspense, useState } from "react";
|
||||
import { Background } from "../../components/background";
|
||||
import { PageSchema } from "../../components/page-schema";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
@ -23,6 +24,9 @@ type AuthData = {
|
||||
requiresTwoFactor?: boolean;
|
||||
};
|
||||
|
||||
const socialButtonClass =
|
||||
"flex w-full items-center justify-center gap-3 rounded-xl border border-border bg-white py-3 px-4 text-sm font-medium text-foreground shadow-sm transition-all hover:shadow-md hover:border-primary/30 focus:outline-none focus:ring-2 focus:ring-primary/20";
|
||||
|
||||
function LoginForm() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
@ -30,6 +34,7 @@ function LoginForm() {
|
||||
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [staySignedIn, setStaySignedIn] = useState(true);
|
||||
const [totpToken, setTotpToken] = useState("");
|
||||
const [requiresTwoFactor, setRequiresTwoFactor] = useState(false);
|
||||
const [status, setStatus] = useState<string>("");
|
||||
@ -73,6 +78,42 @@ function LoginForm() {
|
||||
|
||||
return (
|
||||
<form className="space-y-6" onSubmit={onSubmit}>
|
||||
{/* Social sign in */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className={socialButtonClass}
|
||||
aria-label="Continue with Apple"
|
||||
>
|
||||
<svg className="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
|
||||
<path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" />
|
||||
</svg>
|
||||
Continue with Apple
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={socialButtonClass}
|
||||
aria-label="Continue with Google"
|
||||
>
|
||||
<svg className="h-5 w-5" viewBox="0 0 24 24" aria-hidden>
|
||||
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
|
||||
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
|
||||
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
|
||||
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
|
||||
</svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-border" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="bg-transparent px-3 text-muted-foreground">or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-foreground">
|
||||
Email address
|
||||
@ -81,7 +122,7 @@ function LoginForm() {
|
||||
<input
|
||||
id="email" name="email" type="email" autoComplete="email" required
|
||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -94,11 +135,25 @@ function LoginForm() {
|
||||
<input
|
||||
id="password" name="password" type="password" autoComplete="current-password" required
|
||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
id="staySignedIn"
|
||||
name="staySignedIn"
|
||||
type="checkbox"
|
||||
checked={staySignedIn}
|
||||
onChange={(e) => setStaySignedIn(e.target.checked)}
|
||||
className="h-4 w-4 rounded border-border bg-white text-primary focus:ring-2 focus:ring-primary/20 focus:ring-offset-0"
|
||||
/>
|
||||
<label htmlFor="staySignedIn" className="text-sm text-muted-foreground">
|
||||
Stay signed in
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{requiresTwoFactor && (
|
||||
<div>
|
||||
<label htmlFor="totp" className="block text-sm font-medium text-foreground">
|
||||
@ -109,17 +164,14 @@ function LoginForm() {
|
||||
id="totp" name="totp" type="text" inputMode="numeric" maxLength={6}
|
||||
placeholder="6-digit code" autoComplete="one-time-code" required
|
||||
value={totpToken} onChange={(e) => setTotpToken(e.target.value.replace(/\D/g, ""))}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all tracking-widest text-center"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all tracking-widest text-center"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full justify-center rounded-lg border border-transparent bg-primary py-2.5 px-4 text-sm font-bold text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<button type="submit" className="btn-primary w-full py-3">
|
||||
{requiresTwoFactor ? "Verify" : "Sign in"}
|
||||
</button>
|
||||
</div>
|
||||
@ -131,8 +183,8 @@ function LoginForm() {
|
||||
</div>
|
||||
|
||||
{status && (
|
||||
<div className={`mt-4 rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-accent/10 border border-accent/20"}`}>
|
||||
<p className="text-sm font-medium text-center">{status}</p>
|
||||
<div className={`mt-4 rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-primary/10 border border-primary/20"}`}>
|
||||
<p className={`text-sm font-medium text-center ${isError ? "text-red-600 dark:text-red-400" : "text-foreground"}`}>{status}</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
@ -160,35 +212,39 @@ export default function LoginPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex justify-center">
|
||||
<div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center text-primary-foreground font-bold text-xl shadow-glow-teal">
|
||||
<div
|
||||
className="h-12 w-12 rounded-full flex items-center justify-center text-white font-bold text-xl shadow-[0_12px_30px_var(--gradient-glow)]"
|
||||
style={{ background: "linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end))" }}
|
||||
>
|
||||
L1
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">
|
||||
Sign in to your account
|
||||
Sign In
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-muted-foreground">
|
||||
Or{" "}
|
||||
<Link href="/register" className="font-medium text-primary hover:text-primary/80 transition-colors">
|
||||
start your 14-day free trial
|
||||
Start your 14-day free trial
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="glass-panel py-8 px-4 shadow-xl sm:rounded-xl sm:px-10">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 py-8 px-4 shadow-glass backdrop-blur-sm sm:px-10">
|
||||
<Suspense fallback={null}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
225
app/page.tsx
225
app/page.tsx
@ -1,10 +1,12 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Background } from "../components/background";
|
||||
import { SiteFooter } from "../components/site-footer";
|
||||
import { SiteHeader } from "../components/site-header";
|
||||
import { PageSchema } from "../components/page-schema";
|
||||
import { GrowthSimulator } from "../components/growth-simulator";
|
||||
import { siteInfo } from "../data/site";
|
||||
import { LandingHero } from "@/components/landing-hero";
|
||||
import { LandingFeatures } from "../components/landing-features";
|
||||
import { LandingFuture } from "../components/landing-future";
|
||||
import { LandingCta } from "../components/landing-cta";
|
||||
|
||||
export const metadata = {
|
||||
title: "LedgerOne - The Financial Control Platform for Modern Business",
|
||||
@ -39,198 +41,53 @@ export default function LandingPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<main className="relative z-10 flex-1">
|
||||
<LandingHero />
|
||||
|
||||
<main className="flex-1">
|
||||
{/* Hero Section - Monarch Style Clean Split */}
|
||||
<section className="relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||
|
||||
{/* Left Column: Copy */}
|
||||
<div className="max-w-2xl animate-slide-up">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary/50 border border-border text-xs font-medium text-muted-foreground backdrop-blur-sm mb-8">
|
||||
<span className="flex h-2 w-2 rounded-full bg-primary"></span>
|
||||
Now available for US & Canadian businesses
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold tracking-tight text-foreground sm:text-7xl mb-6 leading-tight">
|
||||
Master your money <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-emerald-400">
|
||||
with total clarity.
|
||||
</span>
|
||||
</h1>
|
||||
<p className="mt-6 text-lg text-muted-foreground mb-10">
|
||||
LedgerOne connects all your financial accounts in one place. Automate bookkeeping, track cash flow, and stay audit-ready without the spreadsheet chaos.
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-full bg-primary px-8 py-4 text-base font-bold text-primary-foreground shadow-lg shadow-primary/25 hover:bg-primary/90 hover:-translate-y-1 transition-all"
|
||||
>
|
||||
Start your free trial
|
||||
</Link>
|
||||
<Link
|
||||
href="/demo"
|
||||
className="rounded-full bg-background border border-border px-8 py-4 text-base font-semibold text-foreground hover:bg-secondary transition-colors"
|
||||
>
|
||||
See how it works
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Composed Media Stack */}
|
||||
<div className="relative h-[600px] w-full flex items-center justify-center animate-fade-in delay-200">
|
||||
{/* 1. Base Layer: Video (Desktop View) */}
|
||||
<div className="absolute top-0 right-0 w-[90%] h-[80%] rounded-2xl overflow-hidden border border-border shadow-2xl bg-background/50 backdrop-blur-xl z-10">
|
||||
<video
|
||||
poster="/images/hero_celebration_video_poster_1769386269277.png"
|
||||
className="w-full h-full object-cover opacity-80"
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
>
|
||||
<source src="/videos/hero.mp4" type="video/mp4" />
|
||||
</video>
|
||||
{/* Overlay Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
{/* 2. Middle Layer: Interactive Simulator (Floating Card) */}
|
||||
<div className="absolute bottom-10 right-10 w-[320px] z-20 hidden xl:block">
|
||||
<div className="bg-background/80 backdrop-blur-md border border-border rounded-2xl p-1 shadow-glass transform hover:scale-105 transition-transform duration-500">
|
||||
<GrowthSimulator />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Social Proof */}
|
||||
<section className="py-12 border-y border-border bg-secondary/20">
|
||||
{/* TRUST SECTION */}
|
||||
<section className="bg-secondary/20 py-8">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8 text-center">
|
||||
<p className="text-sm font-semibold text-muted-foreground uppercase tracking-wider mb-8">
|
||||
Trusted by forward-thinking finance teams
|
||||
<p className="mb-4 text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
Trusted by teams who treat finance as a product
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-8 md:grid-cols-5 opacity-60 grayscale hover:grayscale-0 transition-all duration-500">
|
||||
{/* Placeholders for logos */}
|
||||
<div className="flex items-center justify-center font-bold text-xl">Acme Corp</div>
|
||||
<div className="flex items-center justify-center font-bold text-xl">GlobalTech</div>
|
||||
<div className="flex items-center justify-center font-bold text-xl">Nebula</div>
|
||||
<div className="flex items-center justify-center font-bold text-xl">Vertex</div>
|
||||
<div className="flex items-center justify-center font-bold text-xl">Horizon</div>
|
||||
<div className="mb-6 flex flex-wrap items-center justify-center gap-8 text-sm text-muted-foreground">
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-2xl font-semibold text-foreground">50,000+</span>
|
||||
<span>users</span>
|
||||
</div>
|
||||
<div className="h-4 w-px bg-border" />
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-2xl font-semibold text-foreground">$120M+</span>
|
||||
<span>tracked monthly</span>
|
||||
</div>
|
||||
<div className="h-4 w-px bg-border" />
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-2xl font-semibold text-foreground">4.9</span>
|
||||
<span>avg satisfaction</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-6 md:grid-cols-5 md:gap-10 opacity-70 grayscale transition hover:opacity-100 hover:grayscale-0">
|
||||
<div className="flex items-center justify-center text-sm font-semibold">Aurora Bank</div>
|
||||
<div className="flex items-center justify-center text-sm font-semibold">Northwind</div>
|
||||
<div className="flex items-center justify-center text-sm font-semibold">Summit Labs</div>
|
||||
<div className="flex items-center justify-center text-sm font-semibold">Horizon Co.</div>
|
||||
<div className="flex items-center justify-center text-sm font-semibold">Canvas Ventures</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Feature Grid */}
|
||||
<section className="py-24 lg:py-32">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-20">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||
Everything you need to manage your wealth.
|
||||
</h2>
|
||||
<p className="mt-4 text-lg text-muted-foreground">
|
||||
Stop logging into ten different sites. LedgerOne brings your entire financial life into a single, secure, and beautiful view.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-12 lg:grid-cols-3">
|
||||
{/* Feature 1: Connect */}
|
||||
<div className="glass-panel rounded-3xl p-8 hover:border-primary/50 transition-colors group">
|
||||
<div className="h-12 w-12 rounded-2xl bg-primary/10 flex items-center justify-center text-primary mb-6 group-hover:scale-110 transition-transform">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-foreground mb-3">Sync Everything</h3>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
Connect over 11,000 financial institutions. Banks, credit cards, loans, and investments update automatically.
|
||||
</p>
|
||||
<div className="rounded-xl overflow-hidden border border-border shadow-sm">
|
||||
<Image
|
||||
src="https://images.unsplash.com/photo-1563986768609-322da13575f3?q=80&w=1470&auto=format&fit=crop"
|
||||
alt="Bank connections"
|
||||
width={400}
|
||||
height={250}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Feature 2: Visualize */}
|
||||
<div className="glass-panel rounded-3xl p-8 hover:border-primary/50 transition-colors group">
|
||||
<div className="h-12 w-12 rounded-2xl bg-primary/10 flex items-center justify-center text-primary mb-6 group-hover:scale-110 transition-transform">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-foreground mb-3">Visualize Cash Flow</h3>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
See exactly where your money goes. Track income vs expenses with beautiful, interactive charts.
|
||||
</p>
|
||||
<div className="rounded-xl overflow-hidden border border-border shadow-sm">
|
||||
<Image
|
||||
src="/images/feature-cashflow.png"
|
||||
alt="Cash flow chart"
|
||||
width={400}
|
||||
height={250}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Feature 3: Report */}
|
||||
<div className="glass-panel rounded-3xl p-8 hover:border-primary/50 transition-colors group">
|
||||
<div className="h-12 w-12 rounded-2xl bg-primary/10 flex items-center justify-center text-primary mb-6 group-hover:scale-110 transition-transform">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-foreground mb-3">Custom Reports</h3>
|
||||
<p className="text-muted-foreground mb-6">
|
||||
Build the exact report you need. Filter by category, tag, or merchant and export to CSV for your accountant.
|
||||
</p>
|
||||
<div className="rounded-xl overflow-hidden border border-border shadow-sm">
|
||||
<Image
|
||||
src="/images/feature-reports.png"
|
||||
alt="Report builder"
|
||||
width={400}
|
||||
height={250}
|
||||
className="w-full h-48 object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-primary/5 -z-10" />
|
||||
<div className="max-w-4xl mx-auto px-6 lg:px-8 text-center">
|
||||
<h2 className="text-4xl font-bold tracking-tight text-foreground mb-6">
|
||||
Ready to take control?
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground mb-10">
|
||||
Join thousands of business owners who trust LedgerOne for their financial clarity.
|
||||
</p>
|
||||
<Link
|
||||
href="/register"
|
||||
className="inline-block rounded-full bg-primary px-10 py-4 text-lg font-bold text-primary-foreground shadow-xl shadow-primary/30 hover:bg-primary/90 hover:-translate-y-1 transition-all"
|
||||
>
|
||||
Start your 14-day free trial
|
||||
</Link>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
No credit card required. Cancel anytime.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<LandingFeatures />
|
||||
<LandingFuture />
|
||||
<LandingCta />
|
||||
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../components/background";
|
||||
import { ContactSection } from "../../components/contact-section";
|
||||
import { DemoCta } from "../../components/demo-cta";
|
||||
import { FaqSection } from "../../components/faq-section";
|
||||
@ -77,14 +78,12 @@ export default function PricingPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-16 animate-slide-up">
|
||||
<div className="text-center max-w-3xl mx-auto mb-10 animate-slide-up">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
Simple, transparent pricing.
|
||||
</h1>
|
||||
@ -132,10 +131,7 @@ export default function PricingPage() {
|
||||
|
||||
<Link
|
||||
href="/register"
|
||||
className={`mt-8 block w-full rounded-lg px-4 py-3 text-center text-sm font-semibold shadow-sm transition-all hover:-translate-y-0.5 ${plan.primary
|
||||
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
: "bg-background text-primary ring-1 ring-inset ring-primary/20 hover:ring-primary/40"
|
||||
}`}
|
||||
className={`mt-8 block w-full text-center ${plan.primary ? "btn-primary" : "btn-secondary"}`}
|
||||
>
|
||||
{plan.cta}
|
||||
</Link>
|
||||
@ -143,8 +139,8 @@ export default function PricingPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-24 max-w-4xl mx-auto glass-panel rounded-xl p-1">
|
||||
<div className="px-6 py-4 border-b border-border">
|
||||
<div className="mt-16 max-w-4xl mx-auto glass-panel rounded-xl p-1">
|
||||
<div className="px-6 py-4">
|
||||
<h2 className="text-xl font-bold text-foreground">Compare plans</h2>
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
@ -171,7 +167,9 @@ export default function PricingPage() {
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../components/background";
|
||||
import { PageSchema } from "../../components/page-schema";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
@ -9,6 +11,15 @@ export const metadata = {
|
||||
keywords: siteInfo.keywords
|
||||
};
|
||||
|
||||
const sections = [
|
||||
{ id: "collect", title: "1. Information We Collect" },
|
||||
{ id: "use", title: "2. How We Use Information" },
|
||||
{ id: "security", title: "3. Data Security" },
|
||||
{ id: "ccpa", title: "4. California Privacy Rights (CCPA)" },
|
||||
{ id: "pipeda", title: "5. Canadian Privacy Rights (PIPEDA)" },
|
||||
{ id: "contact", title: "6. Contact Us" }
|
||||
];
|
||||
|
||||
export default function PrivacyPage() {
|
||||
const schema = [
|
||||
{
|
||||
@ -21,58 +32,104 @@ export default function PrivacyPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-3xl mx-auto px-6 lg:px-8">
|
||||
<div className="glass-panel rounded-3xl p-8 sm:p-12 shadow-sm">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-8">
|
||||
Privacy Policy
|
||||
</h1>
|
||||
|
||||
<div className="prose prose-gray dark:prose-invert max-w-none text-muted-foreground">
|
||||
<p className="lead text-lg">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/95 via-background to-background/90 p-6 shadow-glass backdrop-blur-sm sm:p-10">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8 pb-6 border-b border-border/60">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||
Privacy Policy
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Last updated: October 24, 2023
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">1. Information We Collect</h3>
|
||||
<p>
|
||||
We collect information you provide directly to us, such as when you create an account, connect a bank account, or request customer support.
|
||||
</p>
|
||||
<nav className="mb-10 rounded-xl bg-secondary/20 border border-border/50 p-4" aria-label="On this page">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">On this page</p>
|
||||
<ul className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||
{sections.map(({ id, title }) => (
|
||||
<li key={id}>
|
||||
<a href={`#${id}`} className="text-primary hover:underline">
|
||||
{title.replace(/^\d+\.\s/, "")}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">2. How We Use Information</h3>
|
||||
<p>
|
||||
We use the information we collect to provide, maintain, and improve our services, such as to sync your transactions and generate reports.
|
||||
</p>
|
||||
<div className="space-y-10 text-muted-foreground leading-relaxed">
|
||||
<section id="collect">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">1. Information We Collect</h2>
|
||||
<p>
|
||||
We collect information you provide directly to us, such as when you create an account, connect a bank account, or request customer support.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">3. Data Security</h3>
|
||||
<p>
|
||||
We use industry-standard encryption and security measures to protect your data. We do not store your bank login credentials; they are handled by our secure partners (Plaid).
|
||||
</p>
|
||||
<section id="use">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">2. How We Use Information</h2>
|
||||
<p>
|
||||
We use the information we collect to provide, maintain, and improve our services, such as to sync your transactions and generate reports.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">4. California Privacy Rights (CCPA)</h3>
|
||||
<p>
|
||||
If you are a California resident, you have specific rights regarding your personal information, including the right to request access to and deletion of your data. We do not sell your personal information. To exercise your rights, please contact us.
|
||||
</p>
|
||||
<section id="security">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">3. Data Security</h2>
|
||||
<p>
|
||||
We use industry-standard encryption and security measures to protect your data. We do not store your bank login credentials; they are handled by our secure partners (Plaid).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">5. Canadian Privacy Rights (PIPEDA)</h3>
|
||||
<p>
|
||||
If you are a Canadian resident, you have the right to access your personal information and request corrections. We comply with the Personal Information Protection and Electronic Documents Act (PIPEDA) regarding the collection, use, and disclosure of personal information.
|
||||
</p>
|
||||
<section id="ccpa">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">4. California Privacy Rights (CCPA)</h2>
|
||||
<p>
|
||||
If you are a California resident, you have specific rights regarding your personal information, including the right to request access to and deletion of your data. We do not sell your personal information. To exercise your rights, please{" "}
|
||||
<Link href="/contact" className="text-primary hover:underline font-medium">
|
||||
contact us
|
||||
</Link>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">6. Contact Us</h3>
|
||||
<p>
|
||||
If you have any questions about this Privacy Policy, please contact us at privacy@ledgerone.com.
|
||||
</p>
|
||||
<section id="pipeda">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">5. Canadian Privacy Rights (PIPEDA)</h2>
|
||||
<p>
|
||||
If you are a Canadian resident, you have the right to access your personal information and request corrections. We comply with the Personal Information Protection and Electronic Documents Act (PIPEDA) regarding the collection, use, and disclosure of personal information.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="contact">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">6. Contact Us</h2>
|
||||
<p>
|
||||
If you have any questions about this Privacy Policy, please contact us at{" "}
|
||||
<a href="mailto:privacy@ledgerone.com" className="text-primary hover:underline font-medium">
|
||||
privacy@ledgerone.com
|
||||
</a>{" "}
|
||||
or visit our{" "}
|
||||
<Link href="/contact" className="text-primary hover:underline font-medium">
|
||||
contact page
|
||||
</Link>.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 pt-6 border-t border-border/60 flex flex-wrap gap-6 text-sm">
|
||||
<Link href="/terms" className="text-primary hover:underline font-medium">
|
||||
Terms of Service
|
||||
</Link>
|
||||
<Link href="/contact" className="text-primary hover:underline font-medium">
|
||||
Contact us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState, FormEvent } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Background } from "../../components/background";
|
||||
import { PageSchema } from "../../components/page-schema";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
@ -23,10 +25,20 @@ type AuthData = {
|
||||
message?: string;
|
||||
};
|
||||
|
||||
const inputClass =
|
||||
"block w-full appearance-none rounded-xl border border-border bg-white/90 px-4 py-3 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all";
|
||||
|
||||
const socialButtonClass =
|
||||
"flex w-full items-center justify-center gap-3 rounded-xl border border-border bg-white py-3 px-4 text-sm font-medium text-foreground shadow-sm transition-all hover:shadow-md hover:border-primary/30 focus:outline-none focus:ring-2 focus:ring-primary/20";
|
||||
|
||||
export default function RegisterPage() {
|
||||
const router = useRouter();
|
||||
const [fullName, setFullName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [agreeTerms, setAgreeTerms] = useState(false);
|
||||
const [status, setStatus] = useState<string>("");
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
@ -51,13 +63,18 @@ export default function RegisterPage() {
|
||||
|
||||
const onSubmit = async (event: FormEvent) => {
|
||||
event.preventDefault();
|
||||
if (password !== confirmPassword) {
|
||||
setStatus("Passwords do not match.");
|
||||
setIsError(true);
|
||||
return;
|
||||
}
|
||||
setStatus("Creating account...");
|
||||
setIsError(false);
|
||||
try {
|
||||
const res = await fetch("/api/auth/register", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
body: JSON.stringify({ email, password, fullName: fullName || undefined }),
|
||||
});
|
||||
const payload = (await res.json()) as ApiResponse<AuthData>;
|
||||
if (!res.ok || payload.error) {
|
||||
@ -70,7 +87,7 @@ export default function RegisterPage() {
|
||||
refreshToken: payload.data.refreshToken,
|
||||
user: payload.data.user,
|
||||
});
|
||||
setStatus("Account created! Please verify your email.");
|
||||
setStatus("Account created! Redirecting...");
|
||||
router.push("/app");
|
||||
} catch {
|
||||
setStatus("Registration failed. Please try again.");
|
||||
@ -79,89 +96,209 @@ export default function RegisterPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex justify-center">
|
||||
<div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center text-primary-foreground font-bold text-xl shadow-glow-teal">
|
||||
L1
|
||||
<main className="relative z-10 flex flex-1 flex-col justify-center px-6 py-12 lg:py-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 12 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="w-full max-w-md mx-auto text-center"
|
||||
>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
|
||||
Create your LedgerOne account
|
||||
</h1>
|
||||
<p className="mt-2 text-muted-foreground">
|
||||
Get AI-powered financial clarity in minutes.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 text-left">
|
||||
{/* Social sign up */}
|
||||
<div className="mt-8 flex flex-col gap-3">
|
||||
<button
|
||||
type="button"
|
||||
className={socialButtonClass}
|
||||
aria-label="Continue with Apple"
|
||||
>
|
||||
<svg className="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden>
|
||||
<path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" />
|
||||
</svg>
|
||||
Continue with Apple
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={socialButtonClass}
|
||||
aria-label="Continue with Google"
|
||||
>
|
||||
<svg className="h-5 w-5" viewBox="0 0 24 24" aria-hidden>
|
||||
<path
|
||||
fill="#4285F4"
|
||||
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
||||
/>
|
||||
<path
|
||||
fill="#34A853"
|
||||
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
||||
/>
|
||||
<path
|
||||
fill="#FBBC05"
|
||||
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
||||
/>
|
||||
<path
|
||||
fill="#EA4335"
|
||||
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
||||
/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="relative my-6">
|
||||
<div className="absolute inset-0 flex items-center">
|
||||
<div className="w-full border-t border-border" />
|
||||
</div>
|
||||
<div className="relative flex justify-center text-sm">
|
||||
<span className="bg-transparent px-3 text-muted-foreground">or</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form className="space-y-5" onSubmit={onSubmit}>
|
||||
<div>
|
||||
<label htmlFor="fullName" className="block text-sm font-medium text-foreground mb-1.5">
|
||||
Full name
|
||||
</label>
|
||||
<input
|
||||
id="fullName"
|
||||
name="fullName"
|
||||
type="text"
|
||||
autoComplete="name"
|
||||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
className={inputClass}
|
||||
placeholder="Jane Smith"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-foreground mb-1.5">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className={inputClass}
|
||||
placeholder="you@company.com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-foreground mb-1.5">
|
||||
Password
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
autoComplete="new-password"
|
||||
required
|
||||
minLength={8}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className={`${inputClass} pr-11`}
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 p-1.5 text-muted-foreground hover:text-foreground rounded-lg hover:bg-secondary/50 transition-colors"
|
||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? (
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878a4.5 4.5 0 106.262 6.262M4 4l16 16" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<p className="mt-1.5 text-xs text-muted-foreground">Minimum 8 characters</p>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="confirmPassword" className="block text-sm font-medium text-foreground mb-1.5">
|
||||
Confirm password
|
||||
</label>
|
||||
<input
|
||||
id="confirmPassword"
|
||||
name="confirmPassword"
|
||||
type={showPassword ? "text" : "password"}
|
||||
autoComplete="new-password"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
className={inputClass}
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<input
|
||||
id="terms"
|
||||
name="terms"
|
||||
type="checkbox"
|
||||
required
|
||||
checked={agreeTerms}
|
||||
onChange={(e) => setAgreeTerms(e.target.checked)}
|
||||
className="mt-1 h-4 w-4 rounded border-border bg-white text-primary focus:ring-2 focus:ring-primary/20 focus:ring-offset-0"
|
||||
/>
|
||||
<label htmlFor="terms" className="text-sm text-muted-foreground leading-tight">
|
||||
I agree to the{" "}
|
||||
<Link href="/terms" className="text-primary hover:underline font-medium">
|
||||
Terms of Use
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="/privacy-policy" className="text-primary hover:underline font-medium">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary w-full py-3.5 text-base font-semibold transition-all hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
Create account
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{status && (
|
||||
<div
|
||||
className={`mt-5 rounded-xl p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-primary/10 border border-primary/20"}`}
|
||||
>
|
||||
<p className={`text-sm font-medium text-center ${isError ? "text-red-600 dark:text-red-400" : "text-foreground"}`}>
|
||||
{status}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">
|
||||
Create your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-muted-foreground">
|
||||
|
||||
<p className="mt-6 text-center text-sm text-muted-foreground">
|
||||
Already have an account?{" "}
|
||||
<Link href="/login" className="font-medium text-primary hover:text-primary/80 transition-colors">
|
||||
<Link href="/login" className="font-semibold text-primary hover:underline">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="glass-panel py-8 px-4 shadow-xl sm:rounded-xl sm:px-10">
|
||||
<form className="space-y-6" onSubmit={onSubmit}>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-foreground">
|
||||
Email address
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
id="email" name="email" type="email" autoComplete="email" required
|
||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-foreground">
|
||||
Password
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
id="password" name="password" type="password" autoComplete="new-password" required
|
||||
minLength={8}
|
||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">Minimum 8 characters</p>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<input
|
||||
id="terms" name="terms" type="checkbox" required
|
||||
className="h-4 w-4 rounded border-border bg-background/50 text-primary focus:ring-primary"
|
||||
onChange={(e) => {
|
||||
const btn = document.getElementById("submit-btn") as HTMLButtonElement;
|
||||
if (btn) btn.disabled = !e.target.checked;
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="terms" className="text-sm text-muted-foreground">
|
||||
I agree to the{" "}
|
||||
<Link href="/terms" className="text-primary hover:underline">Terms of Service</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="/privacy-policy" className="text-primary hover:underline">Privacy Policy</Link>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
id="submit-btn" type="submit" disabled
|
||||
className="flex w-full justify-center rounded-lg border border-transparent bg-primary py-2.5 px-4 text-sm font-bold text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all hover:-translate-y-0.5 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0"
|
||||
>
|
||||
Create account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{status && (
|
||||
<div className={`mt-4 rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-accent/10 border border-accent/20"}`}>
|
||||
<p className="text-sm font-medium text-center">{status}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</main>
|
||||
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<SiteFooter />
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Suspense, useState } from "react";
|
||||
import { Background } from "../../components/background";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
|
||||
@ -80,7 +81,7 @@ function ResetPasswordForm() {
|
||||
<input
|
||||
id="password" name="password" type="password" autoComplete="new-password" required minLength={8}
|
||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-1 text-xs text-muted-foreground">Minimum 8 characters</p>
|
||||
@ -93,19 +94,16 @@ function ResetPasswordForm() {
|
||||
<input
|
||||
id="confirm" name="confirm" type="password" autoComplete="new-password" required
|
||||
value={confirm} onChange={(e) => setConfirm(e.target.value)}
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-primary sm:text-sm transition-all"
|
||||
className="block w-full appearance-none rounded-lg border border-border bg-background/50 px-3 py-2 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full justify-center rounded-lg border border-transparent bg-primary py-2.5 px-4 text-sm font-bold text-primary-foreground shadow-sm hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 transition-all hover:-translate-y-0.5"
|
||||
>
|
||||
<button type="submit" className="btn-primary w-full py-3">
|
||||
Reset Password
|
||||
</button>
|
||||
{status && (
|
||||
<div className={`rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-accent/10 border border-accent/20"}`}>
|
||||
<p className="text-sm text-center">{status}</p>
|
||||
<div className={`rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-primary/10 border border-primary/20"}`}>
|
||||
<p className={`text-sm text-center ${isError ? "text-red-600 dark:text-red-400" : "text-foreground"}`}>{status}</p>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
@ -114,13 +112,16 @@ function ResetPasswordForm() {
|
||||
|
||||
export default function ResetPasswordPage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex justify-center">
|
||||
<div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center text-primary-foreground font-bold text-xl shadow-glow-teal">
|
||||
<div
|
||||
className="h-12 w-12 rounded-full flex items-center justify-center text-white font-bold text-xl shadow-[0_12px_30px_var(--gradient-glow)]"
|
||||
style={{ background: "linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end))" }}
|
||||
>
|
||||
L1
|
||||
</div>
|
||||
</div>
|
||||
@ -134,14 +135,16 @@ export default function ResetPasswordPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="glass-panel py-8 px-4 shadow-xl sm:rounded-xl sm:px-10">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 py-8 px-4 shadow-glass backdrop-blur-sm sm:px-10">
|
||||
<Suspense fallback={<div className="text-center text-sm text-muted-foreground">Loading...</div>}>
|
||||
<ResetPasswordForm />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { Background } from "../../components/background";
|
||||
import { PageSchema } from "../../components/page-schema";
|
||||
import { SiteFooter } from "../../components/site-footer";
|
||||
import { SiteHeader } from "../../components/site-header";
|
||||
@ -9,6 +11,15 @@ export const metadata = {
|
||||
keywords: siteInfo.keywords
|
||||
};
|
||||
|
||||
const sections = [
|
||||
{ id: "acceptance", title: "1. Acceptance of Terms" },
|
||||
{ id: "service", title: "2. Service Description" },
|
||||
{ id: "accounts", title: "3. User Accounts" },
|
||||
{ id: "privacy", title: "4. Data Privacy" },
|
||||
{ id: "governing", title: "5. Governing Law" },
|
||||
{ id: "disputes", title: "6. Dispute Resolution" }
|
||||
];
|
||||
|
||||
export default function TermsPage() {
|
||||
const schema = [
|
||||
{
|
||||
@ -21,58 +32,98 @@ export default function TermsPage() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||
<Background />
|
||||
<SiteHeader />
|
||||
|
||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
|
||||
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||
<div className="max-w-3xl mx-auto px-6 lg:px-8">
|
||||
<div className="glass-panel rounded-3xl p-8 sm:p-12 shadow-sm">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-8">
|
||||
Terms of Service
|
||||
</h1>
|
||||
|
||||
<div className="prose prose-gray dark:prose-invert max-w-none text-muted-foreground">
|
||||
<p className="lead text-lg">
|
||||
<div className="rounded-3xl border border-border/60 bg-gradient-to-b from-background/95 via-background to-background/90 p-6 shadow-glass backdrop-blur-sm sm:p-10">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-8 pb-6 border-b border-border/60">
|
||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||
Terms of Service
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Last updated: October 24, 2023
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">1. Acceptance of Terms</h3>
|
||||
<p>
|
||||
By accessing and using LedgerOne, you accept and agree to be bound by the terms and provision of this agreement.
|
||||
</p>
|
||||
<nav className="mb-10 rounded-xl bg-secondary/20 border border-border/50 p-4" aria-label="On this page">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">On this page</p>
|
||||
<ul className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||
{sections.map(({ id, title }) => (
|
||||
<li key={id}>
|
||||
<a href={`#${id}`} className="text-primary hover:underline">
|
||||
{title.replace(/^\d+\.\s/, "")}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">2. Service Description</h3>
|
||||
<p>
|
||||
LedgerOne provides financial data aggregation, ledger management, and reporting tools. We are not a bank or financial advisor.
|
||||
</p>
|
||||
<div className="space-y-10 text-muted-foreground leading-relaxed">
|
||||
<section id="acceptance">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">1. Acceptance of Terms</h2>
|
||||
<p>
|
||||
By accessing and using LedgerOne, you accept and agree to be bound by the terms and provisions of this agreement.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">3. User Accounts</h3>
|
||||
<p>
|
||||
You are responsible for maintaining the security of your account and password. LedgerOne cannot and will not be liable for any loss or damage from your failure to comply with this security obligation.
|
||||
</p>
|
||||
<section id="service">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">2. Service Description</h2>
|
||||
<p>
|
||||
LedgerOne provides financial data aggregation, ledger management, and reporting tools. We are not a bank or financial advisor.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">4. Data Privacy</h3>
|
||||
<p>
|
||||
Your data is yours. We do not sell your financial data to third parties. See our Privacy Policy for details on how we protect your information.
|
||||
</p>
|
||||
<section id="accounts">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">3. User Accounts</h2>
|
||||
<p>
|
||||
You are responsible for maintaining the security of your account and password. LedgerOne cannot and will not be liable for any loss or damage from your failure to comply with this security obligation.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">5. Governing Law</h3>
|
||||
<p>
|
||||
These Terms shall be governed by and defined following the laws of the United States and Canada. LedgerOne Inc. and yourself irrevocably consent that the courts of the United States and Canada shall have exclusive jurisdiction to resolve any dispute which may arise in connection with these terms.
|
||||
</p>
|
||||
<section id="privacy">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">4. Data Privacy</h2>
|
||||
<p>
|
||||
Your data is yours. We do not sell your financial data to third parties. See our{" "}
|
||||
<Link href="/privacy-policy" className="text-primary hover:underline font-medium">
|
||||
Privacy Policy
|
||||
</Link>{" "}
|
||||
for details on how we protect your information.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">6. Dispute Resolution</h3>
|
||||
<p>
|
||||
Any dispute arising out of or in connection with this contract, including any question regarding its existence, validity, or termination, shall be referred to and finally resolved by arbitration under the rules of the American Arbitration Association (AAA) for US residents, or the ADR Institute of Canada for Canadian residents.
|
||||
</p>
|
||||
<section id="governing">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">5. Governing Law</h2>
|
||||
<p>
|
||||
These Terms shall be governed by and construed in accordance with the laws of the United States and Canada. LedgerOne Inc. and you irrevocably consent that the courts of the United States and Canada shall have exclusive jurisdiction to resolve any dispute which may arise in connection with these terms.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="disputes">
|
||||
<h2 className="text-foreground font-bold text-xl mb-3">6. Dispute Resolution</h2>
|
||||
<p>
|
||||
Any dispute arising out of or in connection with this contract, including any question regarding its existence, validity, or termination, shall be referred to and finally resolved by arbitration under the rules of the American Arbitration Association (AAA) for US residents, or the ADR Institute of Canada for Canadian residents.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 pt-6 border-t border-border/60 flex flex-wrap gap-6 text-sm">
|
||||
<Link href="/privacy-policy" className="text-primary hover:underline font-medium">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Link href="/contact" className="text-primary hover:underline font-medium">
|
||||
Contact us
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
<div className="relative z-10">
|
||||
<SiteFooter />
|
||||
</div>
|
||||
<PageSchema schema={schema} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -96,7 +96,10 @@ export default function VerifyEmailPage() {
|
||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex justify-center">
|
||||
<div className="h-12 w-12 rounded-xl bg-primary flex items-center justify-center text-primary-foreground font-bold text-xl shadow-glow-teal">
|
||||
<div
|
||||
className="h-12 w-12 rounded-full flex items-center justify-center text-white font-bold text-xl shadow-[0_12px_30px_var(--gradient-glow)]"
|
||||
style={{ background: "linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end))" }}
|
||||
>
|
||||
L1
|
||||
</div>
|
||||
</div>
|
||||
|
||||
44
components/background.tsx
Normal file
44
components/background.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Reusable soft gradient background (glassmorphism / ethereal).
|
||||
* Soft gradient base + blurred blobs (cyan, lavender, teal) for frosted glow.
|
||||
* Use with a parent that has page-soft-bg and relative overflow-hidden.
|
||||
* Wrap page content in a container with relative z-10.
|
||||
*/
|
||||
export function Background() {
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 z-0 overflow-hidden pointer-events-none"
|
||||
aria-hidden
|
||||
>
|
||||
{/* Top-left: light airy blue / cyan */}
|
||||
<div
|
||||
className="absolute -top-32 -left-32 h-[700px] w-[700px] rounded-full bg-[#7dd3fc] opacity-30 blur-[100px] animate-blob-float"
|
||||
style={{ animationDelay: "0s" }}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-0 left-1/4 h-[500px] w-[500px] rounded-full bg-[#38bdf8] opacity-20 blur-[80px] animate-blob-float"
|
||||
style={{ animationDelay: "-4s" }}
|
||||
/>
|
||||
{/* Mid / center-right: subtle lavender */}
|
||||
<div
|
||||
className="absolute top-1/3 right-0 w-[600px] h-[600px] rounded-full bg-[#c4b5fd] opacity-28 blur-[100px] animate-blob-float"
|
||||
style={{ animationDelay: "-8s" }}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[550px] h-[550px] rounded-full bg-[#a78bfa] opacity-18 blur-[90px] animate-blob-float"
|
||||
style={{ animationDelay: "-12s" }}
|
||||
/>
|
||||
{/* Bottom-right: soft green / teal */}
|
||||
<div
|
||||
className="absolute -bottom-40 -right-20 h-[600px] w-[600px] rounded-full bg-[#5eead4] opacity-25 blur-[100px] animate-blob-float"
|
||||
style={{ animationDelay: "-2s" }}
|
||||
/>
|
||||
<div
|
||||
className="absolute bottom-0 right-1/3 h-[450px] w-[450px] rounded-full bg-[#6ee7b7] opacity-20 blur-[80px] animate-blob-float"
|
||||
style={{ animationDelay: "-6s" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -11,44 +11,40 @@ export function ContactSection() {
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-ink/10 bg-white/80 p-8 shadow-soft">
|
||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">Contact us</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold">Let's talk about your ledger.</h2>
|
||||
<p className="mt-2 text-sm text-muted">
|
||||
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-border bg-background/80 backdrop-blur-sm p-6 shadow-sm">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">Contact us</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold text-foreground">Let's talk about your ledger.</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Tell us about your workflow and we will suggest the best LedgerOne setup.
|
||||
</p>
|
||||
<form className="mt-6 grid gap-4 md:grid-cols-2" onSubmit={onSubmit}>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
Full name
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-foreground">Full name</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">Work email</label>
|
||||
<label className="block text-sm font-medium text-foreground">Work email</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
type="email"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">Company</label>
|
||||
<label className="block text-sm font-medium text-foreground">Company</label>
|
||||
<input
|
||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
Monthly transactions
|
||||
</label>
|
||||
<select className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm">
|
||||
<label className="block text-sm font-medium text-foreground">Monthly transactions</label>
|
||||
<select className="block w-full rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all">
|
||||
<option>Under 1,000</option>
|
||||
<option>1,000 - 10,000</option>
|
||||
<option>10,000 - 50,000</option>
|
||||
@ -56,24 +52,19 @@ export function ContactSection() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
||||
What do you want to solve?
|
||||
</label>
|
||||
<label className="block text-sm font-medium text-foreground">What do you want to solve?</label>
|
||||
<textarea
|
||||
className="min-h-[120px] w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
||||
className="block min-h-[120px] w-full rounded-lg border border-border bg-background/50 px-3 py-2.5 text-foreground placeholder-muted-foreground shadow-sm focus:border-primary focus:outline-none focus:ring-2 focus:ring-primary/20 sm:text-sm transition-all resize-y"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full rounded-2xl bg-ink px-4 py-3 text-sm font-semibold text-haze"
|
||||
>
|
||||
<button type="submit" className="btn-primary w-full py-3">
|
||||
Send message
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{status ? <p className="mt-4 text-xs text-muted">{status}</p> : null}
|
||||
{status ? <p className="mt-4 text-sm text-muted-foreground">{status}</p> : null}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@ -2,22 +2,19 @@ import Link from "next/link";
|
||||
|
||||
export function DemoCta() {
|
||||
return (
|
||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-ink/10 bg-ink p-8 text-haze shadow-soft">
|
||||
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-border bg-secondary/30 backdrop-blur-sm p-6 shadow-sm">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<p className="text-xs uppercase tracking-[0.3em] text-haze/60">Book a demo</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">Book a demo</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold text-foreground">
|
||||
See LedgerOne in a live walkthrough.
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-haze/70">
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Talk through your workflow and learn how to keep a complete audit trail.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/book-demo"
|
||||
className="inline-flex rounded-full bg-haze px-5 py-2 text-sm font-semibold text-ink"
|
||||
>
|
||||
<Link href="/book-demo" className="btn-primary inline-flex shrink-0">
|
||||
Book a demo
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@ -9,23 +9,23 @@ type FaqSectionProps = {
|
||||
export function FaqSection({ title, subtitle, limit }: FaqSectionProps) {
|
||||
const items = limit ? defaultFaqs.slice(0, limit) : defaultFaqs;
|
||||
return (
|
||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-ink/10 bg-white/80 p-8 shadow-soft">
|
||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">FAQ</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold">
|
||||
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||
<div className="rounded-3xl border border-border bg-background/80 backdrop-blur-sm p-6 shadow-sm">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">FAQ</p>
|
||||
<h2 className="mt-3 text-2xl font-semibold text-foreground">
|
||||
{title ?? "Common questions, clear answers."}
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-muted">
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
{subtitle ??
|
||||
"Find pricing, account, export, and security answers for LedgerOne."}
|
||||
</p>
|
||||
<ol className="mt-6 space-y-5 text-sm text-muted">
|
||||
<ol className="mt-6 space-y-5 text-sm text-muted-foreground">
|
||||
{items.map((item, index) => (
|
||||
<li key={item.question} className="space-y-2">
|
||||
<p className="text-xs uppercase tracking-[0.2em] text-moss">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-foreground">
|
||||
{index + 1}. {item.question}
|
||||
</p>
|
||||
<p>{item.answer}</p>
|
||||
<p className="text-muted-foreground leading-relaxed">{item.answer}</p>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
@ -73,19 +73,25 @@ export function HeroActions() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={onStart}
|
||||
className="rounded-full bg-ink px-6 py-3 text-sm font-semibold text-haze shadow-glow"
|
||||
className="btn-primary inline-flex items-center gap-2 px-6 py-3 transition-all hover:-translate-y-0.5 active:translate-y-0"
|
||||
>
|
||||
<svg className="h-4 w-4 text-white/95" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
Start a private ledger
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onViewExport}
|
||||
className="rounded-full border border-ink/20 bg-white/70 px-6 py-3 text-sm font-semibold text-ink"
|
||||
className="btn-secondary inline-flex items-center gap-2 px-6 py-3"
|
||||
>
|
||||
<svg className="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
View export sample
|
||||
</button>
|
||||
</div>
|
||||
{status ? <p className="text-xs text-muted">{status}</p> : null}
|
||||
{status ? <p className="text-xs text-muted-foreground">{status}</p> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
30
components/landing-cta.tsx
Normal file
30
components/landing-cta.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export function LandingCta() {
|
||||
return (
|
||||
<section className="relative overflow-hidden py-12 lg:py-16">
|
||||
<div className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,_rgba(79,70,229,0.18),_transparent_60%),radial-gradient(circle_at_bottom,_rgba(45,212,191,0.2),_transparent_50%)]" />
|
||||
<div className="mx-auto max-w-3xl px-6 text-center lg:px-8">
|
||||
<h2 className="heading-section mb-4">
|
||||
Start Making Smarter Money Decisions Today
|
||||
</h2>
|
||||
<p className="body-lead mb-8">
|
||||
No spreadsheets. No guesswork. Just clarity. Connect your accounts, see your future, and
|
||||
get a real-time "safe to spend" number.
|
||||
</p>
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
<Link href="/register" className="btn-primary">
|
||||
Get started free
|
||||
</Link>
|
||||
<Link href="/pricing" className="btn-secondary">
|
||||
View pricing
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-4 text-xs text-muted-foreground">
|
||||
No credit card required. Cancel anytime. Your data stays encrypted at rest and in transit.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
167
components/landing-features.tsx
Normal file
167
components/landing-features.tsx
Normal file
@ -0,0 +1,167 @@
|
||||
"use client";
|
||||
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const sectionVariants = {
|
||||
hidden: { opacity: 1, y: 24 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.6, ease: [0.16, 1, 0.3, 1] as [number, number, number, number] },
|
||||
},
|
||||
};
|
||||
|
||||
export function LandingFeatures() {
|
||||
return (
|
||||
<motion.section
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.25 }}
|
||||
variants={sectionVariants}
|
||||
className="py-16 lg:py-20"
|
||||
>
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
||||
<div className="text-center max-w-3xl mx-auto mb-12">
|
||||
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||
Not Just Tracking. Thinking.
|
||||
</h2>
|
||||
<p className="mt-4 text-lg text-muted-foreground">
|
||||
LedgerOne turns your raw transaction firehose into AI-native context—what's
|
||||
happening, what's coming, and what you should do about it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 lg:grid-cols-3">
|
||||
{/* AI Financial Insights */}
|
||||
<motion.div
|
||||
whileHover={{ y: -6, scale: 1.02 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
className="group rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 p-6 shadow-glass shadow-[0_0_60px_rgba(34,197,94,0.25)]"
|
||||
>
|
||||
<div className="mb-5 flex h-10 w-10 items-center justify-center rounded-2xl bg-emerald-500/10 text-emerald-400">
|
||||
<span className="text-xs font-semibold">AI</span>
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-semibold text-foreground">AI Financial Insights</h3>
|
||||
<p className="mb-5 text-sm text-muted-foreground">
|
||||
Get plain-language explanations of what changed, why it matters, and what we recommend
|
||||
you do next.
|
||||
</p>
|
||||
<div className="overflow-hidden rounded-2xl border border-border/80 bg-background/80 text-left text-xs shadow-inner">
|
||||
{/* Preview of AI chat responding (image placeholder for now) */}
|
||||
<div className="relative aspect-video w-full overflow-hidden">
|
||||
<Image
|
||||
src="/images/feature-reports.png"
|
||||
alt="AI financial insights preview"
|
||||
fill
|
||||
sizes="(min-width: 1024px) 33vw, 100vw"
|
||||
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-slate-950/75 via-slate-950/15 to-transparent" />
|
||||
{/* AI prediction label */}
|
||||
<div className="pointer-events-none absolute left-3 top-3 inline-flex items-center gap-2 rounded-full bg-slate-950/80 px-2 py-1 text-[10px] text-emerald-200 ring-1 ring-emerald-400/60">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-emerald-300 animate-pulse" />
|
||||
<span>AI prediction · High confidence</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Predictive Cash Flow */}
|
||||
<motion.div
|
||||
whileHover={{ y: -6, scale: 1.02 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
className="group rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 p-6 shadow-glass shadow-[0_0_60px_rgba(56,189,248,0.22)]"
|
||||
>
|
||||
<div className="mb-5 flex h-10 w-10 items-center justify-center rounded-2xl bg-sky-500/10 text-sky-400">
|
||||
<svg
|
||||
className="h-4 w-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M4 19.5V10M4 10L8 6M4 10L0 6" />
|
||||
<path d="M12 19.5V4.5M12 4.5L16 8.5M12 4.5L8 8.5" />
|
||||
<path d="M20 4.5V14M20 14L16 18M20 14L24 18" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-semibold text-foreground">Predictive Cash Flow</h3>
|
||||
<p className="mb-5 text-sm text-muted-foreground">
|
||||
See your balance 30 days from now—before it happens. Every bill, subscription, and
|
||||
paycheck automatically mapped.
|
||||
</p>
|
||||
<div className="relative overflow-hidden rounded-2xl border border-border/80 bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 p-4">
|
||||
<div className="mb-3 flex items-center justify-between text-[11px] text-slate-300">
|
||||
<span>Projected net balance</span>
|
||||
<span className="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] text-emerald-300">
|
||||
94% on track
|
||||
</span>
|
||||
</div>
|
||||
{/* Cashflow line preview (image placeholder for now) */}
|
||||
<div className="relative aspect-video w-full overflow-hidden rounded-xl">
|
||||
<Image
|
||||
src="/images/feature-cashflow.png"
|
||||
alt="Predictive cash flow graph"
|
||||
fill
|
||||
sizes="(min-width: 1024px) 33vw, 100vw"
|
||||
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
{/* Dynamic highlight where the curve dips */}
|
||||
<div className="pointer-events-none absolute bottom-3 right-3 inline-flex items-center gap-1 rounded-full bg-slate-950/80 px-2 py-1 text-[10px] text-amber-200 ring-1 ring-amber-400/60">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-amber-300 animate-pulse" />
|
||||
<span>Risk window · next 7 days</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Smart Budgeting */}
|
||||
<motion.div
|
||||
whileHover={{ y: -6, scale: 1.02 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
className="group rounded-3xl border border-border/60 bg-gradient-to-b from-background/90 via-background to-background/60 p-6 shadow-glass shadow-[0_0_60px_rgba(139,92,246,0.25)]"
|
||||
>
|
||||
<div className="mb-5 flex h-10 w-10 items-center justify-center rounded-2xl bg-violet-500/10 text-violet-400">
|
||||
<svg
|
||||
className="h-4 w-4"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M12 1v22M5 5h14a2 2 0 012 2v4H3V7a2 2 0 012-2Z" />
|
||||
<path d="M3 13h18v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="mb-2 text-lg font-semibold text-foreground">Smart Budgeting</h3>
|
||||
<p className="mb-5 text-sm text-muted-foreground">
|
||||
Budgets that re-balance themselves based on your behavior—plus a real-time "safe
|
||||
to spend" number.
|
||||
</p>
|
||||
<div className="overflow-hidden rounded-2xl border border-border/80 bg-background/80 text-xs">
|
||||
{/* Progress rings + safe-to-spend preview (image placeholder for now) */}
|
||||
<div className="relative aspect-video w-full">
|
||||
<Image
|
||||
src="/images/feature-reports.png"
|
||||
alt="Smart budgeting preview"
|
||||
fill
|
||||
sizes="(min-width: 1024px) 33vw, 100vw"
|
||||
className="object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
{/* Safe-to-spend label */}
|
||||
<div className="pointer-events-none absolute left-3 bottom-3 inline-flex items-center gap-2 rounded-full bg-slate-950/75 px-2 py-1 text-[10px] text-emerald-200 ring-1 ring-emerald-400/60">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-emerald-300 animate-pulse" />
|
||||
<span>Safe to spend · Updated live</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
);
|
||||
}
|
||||
|
||||
105
components/landing-future.tsx
Normal file
105
components/landing-future.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const futureVariants = {
|
||||
hidden: { opacity: 1, y: 24 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: { duration: 0.7, ease: [0.16, 1, 0.3, 1] as const },
|
||||
},
|
||||
};
|
||||
|
||||
export function LandingFuture() {
|
||||
return (
|
||||
<motion.section
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.35 }}
|
||||
variants={futureVariants}
|
||||
className="bg-secondary/15 py-16 lg:py-20"
|
||||
>
|
||||
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
||||
<div className="mb-8 max-w-3xl space-y-4">
|
||||
<h2 className="text-3xl font-semibold tracking-tight text-foreground sm:text-4xl">
|
||||
See Your Financial Future
|
||||
</h2>
|
||||
<p className="text-base text-muted-foreground sm:text-lg">
|
||||
LedgerOne maps every recurring bill, subscription, and paycheck onto a single, living
|
||||
timeline—so you can see exactly when your balance bends.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-3xl border border-border/80 bg-gradient-to-b from-slate-950 via-slate-900 to-slate-950 p-5 shadow-[0_30px_120px_rgba(15,23,42,0.9)]">
|
||||
<div className="mb-4 flex flex-wrap items-center justify-between gap-3 text-xs text-slate-300">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="h-2 w-2 rounded-full bg-emerald-400" />
|
||||
<span>Projected balance · next 30 days</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-[11px]">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-emerald-400" />
|
||||
<span>Balance</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-sky-400" />
|
||||
<span>Income</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-4 rounded-full bg-rose-400" />
|
||||
<span>Bills</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative w-full overflow-hidden rounded-2xl bg-slate-900">
|
||||
{/* Cinematic future-balance preview (image placeholder for now) */}
|
||||
<div className="relative aspect-video w-full">
|
||||
<div className="absolute inset-0">
|
||||
<img
|
||||
src="/images/feature-reports.png"
|
||||
alt="Predicted balance over 30 days"
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-slate-950/85 via-slate-950/25 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* Overlay events + warning label on top of the video */}
|
||||
<div className="pointer-events-none absolute inset-4 flex items-end justify-between text-[10px] text-slate-200">
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="h-12 w-px bg-rose-400/70" />
|
||||
<span className="rounded-full bg-rose-500/20 px-2 py-0.5 text-rose-200">
|
||||
Rent · -$2,400
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="h-16 w-px bg-emerald-400/80" />
|
||||
<span className="rounded-full bg-emerald-500/20 px-2 py-0.5 text-emerald-200">
|
||||
Payroll · +$6,800
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="h-10 w-px bg-sky-400/80" />
|
||||
<span className="rounded-full bg-sky-500/20 px-2 py-0.5 text-sky-200">
|
||||
Subscriptions · -$480
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pointer-events-none absolute bottom-6 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full bg-amber-500/20 px-3 py-1.5 text-[11px] text-amber-100 ring-1 ring-amber-400/60 shadow-[0_0_40px_rgba(251,191,36,0.55)]">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-amber-300 animate-pulse" />
|
||||
<span>
|
||||
Your balance drops here ⚠️ — projected to dip below{" "}
|
||||
<span className="font-semibold">$1,000</span> on the 24th.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
);
|
||||
}
|
||||
175
components/landing-hero.tsx
Normal file
175
components/landing-hero.tsx
Normal file
@ -0,0 +1,175 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { HeroActions } from "./hero-actions";
|
||||
|
||||
const heroVariants = {
|
||||
hidden: { opacity: 0, y: 24 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: [0.16, 1, 0.3, 1] as const,
|
||||
when: "beforeChildren",
|
||||
staggerChildren: 0.08,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const heroChild = {
|
||||
hidden: { opacity: 0, y: 16 },
|
||||
visible: { opacity: 1, y: 0 },
|
||||
};
|
||||
|
||||
export function LandingHero() {
|
||||
const [range, setRange] = useState<"30" | "90">("30");
|
||||
|
||||
return (
|
||||
<motion.section
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={heroVariants}
|
||||
className="relative overflow-hidden pt-16 pb-12 lg:pt-20 lg:pb-16"
|
||||
>
|
||||
<div className="relative z-10 mx-auto flex max-w-6xl flex-col gap-12 px-6 lg:flex-row lg:items-center lg:px-8">
|
||||
{/* Left: Copy */}
|
||||
<div className="max-w-xl">
|
||||
<motion.div variants={heroChild} className="space-y-8">
|
||||
<motion.div variants={heroChild} className="badge-pill">
|
||||
<span className="badge-pill-dot" />
|
||||
<span className="uppercase tracking-[0.18em] text-[10px] text-emerald-500">
|
||||
AI-native · Live predictions
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={heroChild} className="space-y-5">
|
||||
<h1 className="heading-hero">
|
||||
Know Your Money{" "}
|
||||
<span className="heading-hero-accent">Before You Spend It.</span>
|
||||
</h1>
|
||||
<p className="body-lead">
|
||||
AI-powered insights, forecasts, and real-time financial control—without spreadsheets.
|
||||
LedgerOne watches every account, predicts risk, and tells you what's safe to
|
||||
spend before you click "checkout".
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={heroChild} className="flex flex-wrap items-center gap-4">
|
||||
<motion.div whileHover={{ y: -2, scale: 1.01 }} whileTap={{ scale: 0.98 }}>
|
||||
<Link href="/register" className="btn-primary">
|
||||
Get started in 2 minutes
|
||||
</Link>
|
||||
</motion.div>
|
||||
<motion.div whileHover={{ y: -1, scale: 1.01 }} whileTap={{ scale: 0.98 }}>
|
||||
<Link href="/book-demo" className="btn-secondary">
|
||||
Watch product walkthrough
|
||||
</Link>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={heroChild} className="feature-bullets">
|
||||
<div className="feature-bullet">
|
||||
<span className="feature-bullet-dot" />
|
||||
<span>Instant Plaid connections</span>
|
||||
</div>
|
||||
<div className="feature-bullet">
|
||||
<span className="feature-bullet-dot animate-pulse" />
|
||||
<span>Bank-grade security</span>
|
||||
</div>
|
||||
<div className="feature-bullet">
|
||||
<span className="feature-bullet-dot" />
|
||||
<span>14-day free trial</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div variants={heroChild} className="mt-4">
|
||||
<HeroActions />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Right: single focused product video with glow + AI insight */}
|
||||
<motion.div
|
||||
variants={heroChild}
|
||||
className="relative mx-auto flex w-full max-w-xl items-center justify-center lg:max-w-xl"
|
||||
>
|
||||
{/* Depth: gradient glow behind video */}
|
||||
<motion.div
|
||||
className="pointer-events-none absolute -inset-10 rounded-[36px] bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-green-400/20 blur-3xl"
|
||||
animate={{ opacity: [0.4, 0.6, 0.4] }}
|
||||
transition={{ duration: 8, repeat: Infinity, ease: "easeInOut" }}
|
||||
/>
|
||||
|
||||
{/* Primary product video card */}
|
||||
<motion.div
|
||||
className="relative z-10 w-full overflow-hidden rounded-2xl border border-white/10 shadow-2xl"
|
||||
initial={{ opacity: 0, scale: 0.96, y: 10 }}
|
||||
animate={{ opacity: 1, scale: 1.04, y: 0 }}
|
||||
transition={{ duration: 0.8, ease: [0.16, 1, 0.3, 1] as const }}
|
||||
whileHover={{ scale: 1.08 }}
|
||||
>
|
||||
<div className="relative aspect-[16/9] w-full">
|
||||
<video
|
||||
className="h-full w-full object-cover"
|
||||
autoPlay
|
||||
muted
|
||||
loop
|
||||
playsInline
|
||||
preload="none"
|
||||
poster="/images/dashboard-hero.png"
|
||||
>
|
||||
<source src="/videos/hero.mp4" type="video/mp4" />
|
||||
</video>
|
||||
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-slate-950/70 via-slate-950/10 to-transparent" />
|
||||
|
||||
{/* Forecast toggle inside video */}
|
||||
<div className="absolute left-4 top-4 inline-flex items-center gap-2 rounded-full bg-slate-950/80 px-2 py-1 text-[10px] text-slate-100 ring-1 ring-emerald-400/60 backdrop-blur-md">
|
||||
<span className="inline-flex items-center gap-1">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
|
||||
Cashflow outlook
|
||||
</span>
|
||||
<div className="inline-flex items-center gap-1 rounded-full bg-white/5 p-0.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setRange("30")}
|
||||
className={`px-2 py-0.5 rounded-full transition-colors ${
|
||||
range === "30" ? "bg-emerald-400/25 text-emerald-100" : "text-slate-200"
|
||||
}`}
|
||||
>
|
||||
30d
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setRange("90")}
|
||||
className={`px-2 py-0.5 rounded-full transition-colors ${
|
||||
range === "90" ? "bg-emerald-400/25 text-emerald-100" : "text-slate-200"
|
||||
}`}
|
||||
>
|
||||
90d
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Floating AI insight card */}
|
||||
<motion.div
|
||||
className="absolute right-4 top-4 mt-10 w-64 rounded-xl border border-emerald-300/50 bg-white/80 px-4 py-2 text-xs text-slate-900 shadow-lg backdrop-blur-md dark:border-emerald-400/60 dark:bg-slate-900/90 dark:text-slate-50"
|
||||
animate={{ y: [-2, 2, -2] }}
|
||||
transition={{ duration: 7, repeat: Infinity, ease: "easeInOut", delay: 0.4 }}
|
||||
>
|
||||
<p className="text-[11px] leading-relaxed">
|
||||
You're likely to overspend{" "}
|
||||
<span className="font-semibold text-emerald-600 dark:text-emerald-300">$120</span>{" "}
|
||||
this week based on upcoming bills and historical spending.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.section>
|
||||
);
|
||||
}
|
||||
|
||||
32
components/logo.tsx
Normal file
32
components/logo.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import Link from "next/link";
|
||||
|
||||
type LogoProps = {
|
||||
href?: string;
|
||||
className?: string;
|
||||
showWordmark?: boolean;
|
||||
};
|
||||
|
||||
/** Shared logo (mark + optional wordmark). Use in header, footer, auth. */
|
||||
export function Logo({ href = "/", className = "", showWordmark = true }: LogoProps) {
|
||||
const content = (
|
||||
<>
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-white shadow-[0_12px_30px_var(--gradient-glow)] transition-transform group-hover:scale-105"
|
||||
style={{ background: "linear-gradient(to right, var(--gradient-start), var(--gradient-mid), var(--gradient-end))" }}>
|
||||
<span className="font-bold text-sm">L1</span>
|
||||
</div>
|
||||
{showWordmark && (
|
||||
<span className="text-lg font-bold tracking-tight text-foreground">LedgerOne</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link href={href} className={`flex items-center gap-2 group ${className}`}>
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={`flex items-center gap-2 ${className}`}>{content}</div>;
|
||||
}
|
||||
83
components/signup-testimonial-panel.tsx
Normal file
83
components/signup-testimonial-panel.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
|
||||
const stars = Array.from({ length: 5 }, (_, i) => i);
|
||||
|
||||
export function SignupTestimonialPanel() {
|
||||
const [imgError, setImgError] = useState(false);
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex flex-col gap-8"
|
||||
>
|
||||
{/* Testimonial card */}
|
||||
<div className="rounded-2xl border border-border/60 bg-white/90 p-6 shadow-lg shadow-black/5 backdrop-blur-sm">
|
||||
<div className="flex gap-1 mb-4">
|
||||
{stars.map((i) => (
|
||||
<svg
|
||||
key={i}
|
||||
className="h-5 w-5 text-amber-400"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
aria-hidden
|
||||
>
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||||
</svg>
|
||||
))}
|
||||
</div>
|
||||
<blockquote className="text-foreground text-base leading-relaxed">
|
||||
“LedgerOne helped us understand cash flow before problems surfaced. The experience feels fast, clear, and built for modern finance teams.”
|
||||
</blockquote>
|
||||
<footer className="mt-4">
|
||||
<p className="text-sm font-semibold text-foreground">Sarah Chen</p>
|
||||
<p className="text-xs text-muted-foreground">Head of Finance, Northwind</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
{/* Dashboard preview */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.15, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="relative overflow-hidden rounded-2xl border border-border/60 bg-slate-900/95 shadow-2xl shadow-black/10"
|
||||
>
|
||||
<div className="aspect-[4/3] relative bg-slate-900">
|
||||
{!imgError ? (
|
||||
<Image
|
||||
src="/images/dashboard-hero.png"
|
||||
alt="LedgerOne dashboard preview"
|
||||
fill
|
||||
className="object-cover opacity-90"
|
||||
sizes="(max-width: 768px) 100vw, 480px"
|
||||
priority
|
||||
onError={() => setImgError(true)}
|
||||
/>
|
||||
) : (
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-slate-800 via-slate-900 to-primary/20 flex items-center justify-center">
|
||||
<div className="text-center text-white/80 text-sm px-6">
|
||||
<p className="font-medium">Your dashboard</p>
|
||||
<p className="mt-1 opacity-80">Cash flow, insights, and AI predictions in one place.</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-slate-900/80 via-transparent to-transparent pointer-events-none" />
|
||||
</div>
|
||||
{/* Floating insight card */}
|
||||
<div className="absolute bottom-4 left-4 right-4 rounded-xl border border-emerald-400/30 bg-white/95 px-4 py-3 shadow-lg backdrop-blur-sm">
|
||||
<p className="text-xs text-slate-700 leading-relaxed">
|
||||
<span className="font-semibold text-emerald-600">AI insight:</span> Your runway is healthy. Safe to spend up to $2,400 this week.
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
Join 50,000+ teams who trust LedgerOne for financial clarity.
|
||||
</p>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
@ -1,18 +1,14 @@
|
||||
import Link from "next/link";
|
||||
import { Logo } from "./logo";
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className="border-t border-border bg-background mt-auto">
|
||||
<div className="mx-auto max-w-7xl px-6 py-12 lg:px-8">
|
||||
<footer className="bg-transparent mt-auto">
|
||||
<div className="mx-auto max-w-7xl px-6 py-8 lg:px-8">
|
||||
<div className="flex flex-col items-start justify-between gap-8 sm:flex-row">
|
||||
<div className="max-w-xs">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<div className="h-8 w-8 rounded bg-primary flex items-center justify-center text-primary-foreground font-bold text-sm shadow-glow-teal">
|
||||
L1
|
||||
</div>
|
||||
<span className="text-lg font-bold tracking-tight text-foreground">
|
||||
LedgerOne
|
||||
</span>
|
||||
<div className="mb-4">
|
||||
<Logo />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
The financial control desk built for audit-ready teams. Unifying US and Canadian banking with immutable ledgers.
|
||||
@ -59,16 +55,10 @@ export function SiteFooter() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 border-t border-border pt-8 flex flex-col sm:flex-row justify-between items-center gap-4">
|
||||
<div className="mt-8 pt-6 flex flex-col sm:flex-row items-center gap-4">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
© {new Date().getFullYear()} LedgerOne Inc. All rights reserved.
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
{/* Social placeholders */}
|
||||
<div className="w-5 h-5 bg-muted rounded-full opacity-50 hover:opacity-100 transition-opacity cursor-pointer"></div>
|
||||
<div className="w-5 h-5 bg-muted rounded-full opacity-50 hover:opacity-100 transition-opacity cursor-pointer"></div>
|
||||
<div className="w-5 h-5 bg-muted rounded-full opacity-50 hover:opacity-100 transition-opacity cursor-pointer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { MoodToggle } from "./currency-toggle";
|
||||
import { Logo } from "./logo";
|
||||
|
||||
export function SiteHeader() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
@ -21,46 +21,23 @@ export function SiteHeader() {
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto flex max-w-7xl items-center justify-between px-6 lg:px-8">
|
||||
<Link href="/" className="flex items-center gap-2 group">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded bg-primary text-primary-foreground shadow-glow-teal transition-transform group-hover:scale-105">
|
||||
<span className="font-bold text-sm">L1</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold tracking-tight text-foreground">
|
||||
LedgerOne
|
||||
</span>
|
||||
</Link>
|
||||
<Logo />
|
||||
|
||||
<nav className="hidden items-center gap-8 md:flex">
|
||||
<Link href="/" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
Home
|
||||
</Link>
|
||||
<Link href="/about" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
About
|
||||
</Link>
|
||||
<Link href="/features/cash-flow" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
Cash Flow
|
||||
</Link>
|
||||
<Link href="/features/reports" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
Reports
|
||||
</Link>
|
||||
<Link href="/compare/vs-spreadsheets" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
Compare
|
||||
</Link>
|
||||
<Link href="/pricing" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
Pricing
|
||||
</Link>
|
||||
<Link href="/" className="nav-link">Home</Link>
|
||||
<Link href="/about" className="nav-link">About</Link>
|
||||
<Link href="/features/cash-flow" className="nav-link">Cash Flow</Link>
|
||||
<Link href="/features/reports" className="nav-link">Reports</Link>
|
||||
<Link href="/compare/vs-spreadsheets" className="nav-link">Compare</Link>
|
||||
<Link href="/pricing" className="nav-link">Pricing</Link>
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<MoodToggle />
|
||||
<div className="hidden md:flex items-center gap-4 border-l border-border pl-4">
|
||||
<Link href="/login" className="text-sm font-medium text-foreground hover:text-primary transition-colors">
|
||||
<Link href="/login" className="nav-link">
|
||||
Sign in
|
||||
</Link>
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground shadow-lg shadow-primary/20 transition-all hover:bg-primary/90 hover:-translate-y-0.5 active:scale-95"
|
||||
>
|
||||
<Link href="/register" className="btn-primary-sm">
|
||||
Get Started
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
31
lib/theme.ts
Normal file
31
lib/theme.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Global theme class names (hero = source of truth).
|
||||
* Use these when composing className for consistent look site-wide.
|
||||
*/
|
||||
export const theme = {
|
||||
/** Primary CTA: gradient, glow, rounded-full */
|
||||
btnPrimary: "btn-primary",
|
||||
/** Secondary: border, backdrop blur */
|
||||
btnSecondary: "btn-secondary",
|
||||
/** Nav/small primary (e.g. header Get Started) */
|
||||
btnPrimarySm: "btn-primary-sm",
|
||||
/** Pill badge with optional dot */
|
||||
badgePill: "badge-pill",
|
||||
badgePillDot: "badge-pill-dot",
|
||||
/** Hero headline */
|
||||
headingHero: "heading-hero",
|
||||
headingHeroAccent: "heading-hero-accent",
|
||||
/** Section title + subtitle */
|
||||
headingSection: "heading-section",
|
||||
headingSectionSub: "heading-section-sub",
|
||||
/** Lead paragraph */
|
||||
bodyLead: "body-lead",
|
||||
/** Feature bullets row + item + dot */
|
||||
featureBullets: "feature-bullets",
|
||||
featureBullet: "feature-bullet",
|
||||
featureBulletDot: "feature-bullet-dot",
|
||||
/** Glass card */
|
||||
cardGlass: "card-glass",
|
||||
/** Nav link */
|
||||
navLink: "nav-link",
|
||||
} as const;
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@ -1433,6 +1433,7 @@
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
|
||||
@ -46,6 +46,12 @@ const config: Config = {
|
||||
"glass": "0 8px 32px 0 rgba(31, 38, 135, 0.07)",
|
||||
"glow": "0 0 20px rgba(182, 255, 59, 0.3)",
|
||||
"glow-teal": "0 0 30px rgba(49, 98, 99, 0.2)",
|
||||
"cta": "0 18px 60px rgba(88, 80, 236, 0.45)",
|
||||
"cta-hover": "0 22px 70px rgba(56, 189, 248, 0.5)",
|
||||
},
|
||||
backgroundImage: {
|
||||
"gradient-hero": "linear-gradient(to right, #6366f1, #a855f7, #34d399)",
|
||||
"gradient-hero-text": "linear-gradient(to right, #818cf8, #38bdf8, #34d399)",
|
||||
},
|
||||
animation: {
|
||||
"pulse-slow": "pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user