Compare commits
2 Commits
d731551986
...
7a702d6d2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a702d6d2c | ||
|
|
28cefdc7e7 |
@ -1,5 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { ContactSection } from "../../components/contact-section";
|
import { ContactSection } from "../../components/contact-section";
|
||||||
import { DemoCta } from "../../components/demo-cta";
|
import { DemoCta } from "../../components/demo-cta";
|
||||||
import { FaqSection } from "../../components/faq-section";
|
import { FaqSection } from "../../components/faq-section";
|
||||||
@ -68,12 +69,10 @@ export default function AboutPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24 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="max-w-7xl mx-auto px-6 lg:px-8">
|
||||||
<section className="grid gap-12 lg:grid-cols-[1fr_1fr] items-center">
|
<section className="grid gap-12 lg:grid-cols-[1fr_1fr] items-center">
|
||||||
<div className="space-y-8 animate-slide-up">
|
<div className="space-y-8 animate-slide-up">
|
||||||
@ -81,7 +80,8 @@ export default function AboutPage() {
|
|||||||
Our Story
|
Our Story
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl leading-tight">
|
<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>
|
</h1>
|
||||||
<div className="space-y-4 text-lg text-muted-foreground">
|
<div className="space-y-4 text-lg text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
@ -96,16 +96,10 @@ export default function AboutPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-4">
|
<div className="flex flex-wrap gap-4">
|
||||||
<Link
|
<Link href="/pricing" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
View pricing
|
View pricing
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link href="/faq" className="btn-secondary">
|
||||||
href="/faq"
|
|
||||||
className="px-6 py-3 rounded-lg bg-background border border-border text-foreground font-medium hover:bg-secondary transition-colors"
|
|
||||||
>
|
|
||||||
Explore FAQs
|
Explore FAQs
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -120,7 +114,7 @@ export default function AboutPage() {
|
|||||||
height={900}
|
height={900}
|
||||||
className="h-full w-full object-cover opacity-90"
|
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">
|
<p className="text-xs font-bold uppercase tracking-wider text-primary">
|
||||||
Built for US operators
|
Built for US operators
|
||||||
</p>
|
</p>
|
||||||
@ -133,7 +127,7 @@ export default function AboutPage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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) => (
|
{values.map((value) => (
|
||||||
<div
|
<div
|
||||||
key={value.title}
|
key={value.title}
|
||||||
@ -148,7 +142,7 @@ export default function AboutPage() {
|
|||||||
))}
|
))}
|
||||||
</section>
|
</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">
|
<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
|
Leadership
|
||||||
</div>
|
</div>
|
||||||
@ -165,7 +159,7 @@ export default function AboutPage() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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="grid gap-12 lg:grid-cols-[0.9fr_1.1fr]">
|
||||||
<div className="space-y-6">
|
<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">
|
<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>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Suspense, useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { apiFetch } from "@/lib/api";
|
import { apiFetch } from "@/lib/api";
|
||||||
|
|
||||||
function GoogleCallbackContent() {
|
export default function GoogleCallbackPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
|
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
|
||||||
@ -83,22 +83,3 @@ function GoogleCallbackContent() {
|
|||||||
</div>
|
</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">
|
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||||
<SiteHeader />
|
<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="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||||
Insights for modern finance teams.
|
Insights for modern finance teams.
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { ContactSection } from "../../components/contact-section";
|
import { ContactSection } from "../../components/contact-section";
|
||||||
import { DemoCta } from "../../components/demo-cta";
|
import { DemoCta } from "../../components/demo-cta";
|
||||||
import { FaqSection } from "../../components/faq-section";
|
import { FaqSection } from "../../components/faq-section";
|
||||||
@ -8,6 +9,9 @@ import { SiteHeader } from "../../components/site-header";
|
|||||||
import { defaultFaqs } from "../../data/faq";
|
import { defaultFaqs } from "../../data/faq";
|
||||||
import { siteInfo } from "../../data/site";
|
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 = {
|
export const metadata = {
|
||||||
title: "Book a Demo",
|
title: "Book a Demo",
|
||||||
description:
|
description:
|
||||||
@ -37,60 +41,60 @@ export default function BookDemoPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="marketing min-h-screen">
|
<div className="page-soft-bg min-h-screen font-sans text-foreground flex flex-col relative overflow-hidden">
|
||||||
<div className="relative overflow-hidden">
|
<Background />
|
||||||
<div className="halo absolute inset-0" />
|
<SiteHeader />
|
||||||
<div className="grid-dots absolute inset-0" />
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<SiteHeader />
|
<div className="mx-auto max-w-6xl px-6 lg:px-8">
|
||||||
|
|
||||||
<main className="relative mx-auto max-w-6xl px-6 pb-12 pt-12">
|
|
||||||
<section className="grid gap-10 lg:grid-cols-[1.1fr_0.9fr]">
|
<section className="grid gap-10 lg:grid-cols-[1.1fr_0.9fr]">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<p className="text-xs uppercase tracking-[0.4em] text-muted">Book a demo</p>
|
<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">
|
||||||
<h1 className="text-4xl font-semibold leading-tight">
|
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.
|
Schedule time with the LedgerOne team.
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm text-muted">
|
<p className="text-lg text-muted-foreground">
|
||||||
We will walk you through account connections, rule automation, and
|
We will walk you through account connections, rule automation, and
|
||||||
audit-ready exports based on your workflow.
|
audit-ready exports based on your workflow.
|
||||||
</p>
|
</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">
|
<form className="grid gap-4 md:grid-cols-2">
|
||||||
<div className="space-y-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
|
Full name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
className={inputClass}
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-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">
|
||||||
Work email
|
Work email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
className={inputClass}
|
||||||
type="email"
|
type="email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-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">
|
||||||
Preferred date
|
Preferred date
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
|
className={inputClass}
|
||||||
type="date"
|
type="date"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-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">
|
||||||
Team size
|
Team size
|
||||||
</label>
|
</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>1-5</option>
|
||||||
<option>6-20</option>
|
<option>6-20</option>
|
||||||
<option>21-50</option>
|
<option>21-50</option>
|
||||||
@ -98,45 +102,58 @@ export default function BookDemoPage() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 md:col-span-2">
|
<div className="space-y-2 md:col-span-2">
|
||||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
<label className="block text-sm font-medium text-foreground">
|
||||||
What should we focus oni
|
What should we focus on?
|
||||||
</label>
|
</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>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<button
|
<button type="submit" className="btn-primary w-full py-3">
|
||||||
type="submit"
|
|
||||||
className="w-full rounded-2xl bg-ink px-4 py-3 text-sm font-semibold text-haze"
|
|
||||||
>
|
|
||||||
Request demo
|
Request demo
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p className="mt-4 text-xs text-muted">
|
<p className="mt-4 text-xs text-muted-foreground">
|
||||||
Prefer emaili Reach us at{" "}
|
Prefer email? Reach us at{" "}
|
||||||
<Link className="underline" href="/contact">
|
<Link className="text-primary hover:underline" href="/contact">
|
||||||
the contact page
|
the contact page
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4 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 space-y-4">
|
||||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">What you'll see</p>
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
<ul className="space-y-3 text-sm text-muted">
|
What you'll see
|
||||||
<li>Connect accounts and review the raw ledger flow.</li>
|
</p>
|
||||||
<li>Watch rule automation run and inspect the audit trail.</li>
|
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||||
<li>Export a complete ledger package ready for review.</li>
|
<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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</div>
|
||||||
|
</main>
|
||||||
|
<div className="relative z-10">
|
||||||
|
<ContactSection />
|
||||||
|
<DemoCta />
|
||||||
|
<FaqSection limit={8} />
|
||||||
|
<SiteFooter />
|
||||||
</div>
|
</div>
|
||||||
<ContactSection />
|
|
||||||
<DemoCta />
|
|
||||||
<FaqSection limit={8} />
|
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
<SiteFooter />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -22,12 +23,12 @@ export default function CompareCopilotPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<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
|
Comparison
|
||||||
</div>
|
</div>
|
||||||
@ -69,17 +70,16 @@ export default function CompareCopilotPage() {
|
|||||||
|
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6">Work from anywhere.</h2>
|
<h2 className="text-2xl font-bold text-foreground mb-6">Work from anywhere.</h2>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start your free trial
|
Start your free trial
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -22,12 +23,12 @@ export default function CompareQuickenPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<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
|
Comparison
|
||||||
</div>
|
</div>
|
||||||
@ -69,17 +70,16 @@ export default function CompareQuickenPage() {
|
|||||||
|
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6">Upgrade your finance stack.</h2>
|
<h2 className="text-2xl font-bold text-foreground mb-6">Upgrade your finance stack.</h2>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start your free trial
|
Start your free trial
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -22,12 +23,12 @@ export default function CompareSpreadsheetsPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<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
|
Comparison
|
||||||
</div>
|
</div>
|
||||||
@ -70,17 +71,16 @@ export default function CompareSpreadsheetsPage() {
|
|||||||
|
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to upgrade?</h2>
|
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to upgrade?</h2>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start your free trial
|
Start your free trial
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -22,12 +23,12 @@ export default function CompareYnabPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<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
|
Comparison
|
||||||
</div>
|
</div>
|
||||||
@ -69,17 +70,16 @@ export default function CompareYnabPage() {
|
|||||||
|
|
||||||
<div className="mt-16 text-center">
|
<div className="mt-16 text-center">
|
||||||
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to scale?</h2>
|
<h2 className="text-2xl font-bold text-foreground mb-6">Ready to scale?</h2>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start your free trial
|
Start your free trial
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -37,11 +37,11 @@ export default function ContactPage() {
|
|||||||
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||||
<SiteHeader />
|
<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="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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">
|
<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
|
Support & Sales
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<div className="border-t border-border" />
|
<div className="mt-6" />
|
||||||
|
|
||||||
{/* Export cards */}
|
{/* Export cards */}
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<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">
|
<div className="min-h-screen bg-background font-sans text-foreground flex flex-col">
|
||||||
<SiteHeader />
|
<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="absolute inset-0 mesh-gradient -z-10 opacity-40" />
|
||||||
|
|
||||||
<div className="max-w-4xl mx-auto px-6 lg:px-8">
|
<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="glass-panel rounded-3xl p-6 sm:p-10 shadow-sm">
|
||||||
<div className="mb-12">
|
<div className="mb-8">
|
||||||
<p className="text-xs uppercase tracking-[0.2em] text-primary font-bold mb-4">FAQ</p>
|
<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">
|
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||||
Common questions, clear answers.
|
Common questions, clear answers.
|
||||||
@ -50,9 +50,9 @@ export default function FaqPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-12">
|
<div className="space-y-8">
|
||||||
{defaultFaqs.map((faq, index) => (
|
{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">
|
<h3 className="text-lg font-bold text-foreground uppercase tracking-wide mb-3">
|
||||||
{index + 1}. {faq.question}
|
{index + 1}. {faq.question}
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -23,18 +24,19 @@ export default function CashFlowPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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="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">
|
<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
|
Cash Flow
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl mb-6 leading-tight">
|
<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>
|
</h1>
|
||||||
<p className="text-lg text-muted-foreground mb-8">
|
<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.
|
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>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start tracking cash flow
|
Start tracking cash flow
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +76,9 @@ export default function CashFlowPage() {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../../components/background";
|
||||||
import { SiteFooter } from "../../../components/site-footer";
|
import { SiteFooter } from "../../../components/site-footer";
|
||||||
import { SiteHeader } from "../../../components/site-header";
|
import { SiteHeader } from "../../../components/site-header";
|
||||||
import { PageSchema } from "../../../components/page-schema";
|
import { PageSchema } from "../../../components/page-schema";
|
||||||
@ -23,12 +24,12 @@ export default function ReportsPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<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="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="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">
|
<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
|
Reporting
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl mb-6 leading-tight">
|
<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>
|
</h1>
|
||||||
<p className="text-lg text-muted-foreground mb-8">
|
<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.
|
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>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<Link
|
<Link href="/register" className="btn-primary">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Start building reports
|
Start building reports
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -77,7 +76,9 @@ export default function ReportsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
|
|
||||||
@ -42,13 +43,16 @@ export default function ForgotPasswordPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||||
<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="sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<div className="flex justify-center">
|
<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
|
L1
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +66,7 @@ export default function ForgotPasswordPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
<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 ? (
|
{sent ? (
|
||||||
<div className="text-center space-y-4">
|
<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">
|
<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>
|
</div>
|
||||||
<p className="text-sm text-foreground font-medium">{status}</p>
|
<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>
|
<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
|
Back to sign in
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -89,19 +93,16 @@ export default function ForgotPasswordPage() {
|
|||||||
<input
|
<input
|
||||||
id="email" name="email" type="email" autoComplete="email" required
|
id="email" name="email" type="email" autoComplete="email" required
|
||||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
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>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button type="submit" className="btn-primary w-full py-3">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Send reset link
|
Send reset link
|
||||||
</button>
|
</button>
|
||||||
{isError && status && (
|
{isError && status && (
|
||||||
<div className="rounded-lg bg-red-500/10 border border-red-500/20 p-4">
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
@ -109,7 +110,9 @@ export default function ForgotPasswordPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
157
app/globals.css
157
app/globals.css
@ -6,7 +6,7 @@
|
|||||||
--font-inter: 'Inter', sans-serif;
|
--font-inter: 'Inter', sans-serif;
|
||||||
--font-space: 'Space Grotesk', sans-serif;
|
--font-space: 'Space Grotesk', sans-serif;
|
||||||
|
|
||||||
/* Light Mode (Cloud Dancer) */
|
/* Light Mode (hero / site-wide) */
|
||||||
--background: #F0EEE9;
|
--background: #F0EEE9;
|
||||||
--foreground: #121212;
|
--foreground: #121212;
|
||||||
--primary: #316263;
|
--primary: #316263;
|
||||||
@ -18,6 +18,15 @@
|
|||||||
--accent: #B6FF3B;
|
--accent: #B6FF3B;
|
||||||
--accent-foreground: #121212;
|
--accent-foreground: #121212;
|
||||||
--border: #D6D4D0;
|
--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 {
|
.dark {
|
||||||
@ -61,6 +70,57 @@ body {
|
|||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
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 */
|
||||||
.mesh-gradient {
|
.mesh-gradient {
|
||||||
background-color: #F0EEE9;
|
background-color: #F0EEE9;
|
||||||
@ -100,4 +160,99 @@ body {
|
|||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--muted-foreground);
|
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 }) {
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<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}
|
{children}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { Suspense, useState } from "react";
|
import { Suspense, useState } from "react";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { PageSchema } from "../../components/page-schema";
|
import { PageSchema } from "../../components/page-schema";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
@ -23,6 +24,9 @@ type AuthData = {
|
|||||||
requiresTwoFactor?: boolean;
|
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() {
|
function LoginForm() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@ -30,6 +34,7 @@ function LoginForm() {
|
|||||||
|
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
|
const [staySignedIn, setStaySignedIn] = useState(true);
|
||||||
const [totpToken, setTotpToken] = useState("");
|
const [totpToken, setTotpToken] = useState("");
|
||||||
const [requiresTwoFactor, setRequiresTwoFactor] = useState(false);
|
const [requiresTwoFactor, setRequiresTwoFactor] = useState(false);
|
||||||
const [status, setStatus] = useState<string>("");
|
const [status, setStatus] = useState<string>("");
|
||||||
@ -73,6 +78,42 @@ function LoginForm() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-6" onSubmit={onSubmit}>
|
<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>
|
<div>
|
||||||
<label htmlFor="email" className="block text-sm font-medium text-foreground">
|
<label htmlFor="email" className="block text-sm font-medium text-foreground">
|
||||||
Email address
|
Email address
|
||||||
@ -81,7 +122,7 @@ function LoginForm() {
|
|||||||
<input
|
<input
|
||||||
id="email" name="email" type="email" autoComplete="email" required
|
id="email" name="email" type="email" autoComplete="email" required
|
||||||
value={email} onChange={(e) => setEmail(e.target.value)}
|
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>
|
||||||
</div>
|
</div>
|
||||||
@ -94,11 +135,25 @@ function LoginForm() {
|
|||||||
<input
|
<input
|
||||||
id="password" name="password" type="password" autoComplete="current-password" required
|
id="password" name="password" type="password" autoComplete="current-password" required
|
||||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
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>
|
</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 && (
|
{requiresTwoFactor && (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="totp" className="block text-sm font-medium text-foreground">
|
<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}
|
id="totp" name="totp" type="text" inputMode="numeric" maxLength={6}
|
||||||
placeholder="6-digit code" autoComplete="one-time-code" required
|
placeholder="6-digit code" autoComplete="one-time-code" required
|
||||||
value={totpToken} onChange={(e) => setTotpToken(e.target.value.replace(/\D/g, ""))}
|
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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button type="submit" className="btn-primary w-full py-3">
|
||||||
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"
|
|
||||||
>
|
|
||||||
{requiresTwoFactor ? "Verify" : "Sign in"}
|
{requiresTwoFactor ? "Verify" : "Sign in"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -131,8 +183,8 @@ function LoginForm() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{status && (
|
{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"}`}>
|
<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">{status}</p>
|
<p className={`text-sm font-medium text-center ${isError ? "text-red-600 dark:text-red-400" : "text-foreground"}`}>{status}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
@ -160,35 +212,39 @@ export default function LoginPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||||
<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="sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<div className="flex justify-center">
|
<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
|
L1
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">
|
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">
|
||||||
Sign in to your account
|
Sign In
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-center text-sm text-muted-foreground">
|
<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">
|
<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>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
<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}>
|
<Suspense fallback={null}>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
225
app/page.tsx
225
app/page.tsx
@ -1,10 +1,12 @@
|
|||||||
import Image from "next/image";
|
import { Background } from "../components/background";
|
||||||
import Link from "next/link";
|
|
||||||
import { SiteFooter } from "../components/site-footer";
|
import { SiteFooter } from "../components/site-footer";
|
||||||
import { SiteHeader } from "../components/site-header";
|
import { SiteHeader } from "../components/site-header";
|
||||||
import { PageSchema } from "../components/page-schema";
|
import { PageSchema } from "../components/page-schema";
|
||||||
import { GrowthSimulator } from "../components/growth-simulator";
|
|
||||||
import { siteInfo } from "../data/site";
|
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 = {
|
export const metadata = {
|
||||||
title: "LedgerOne - The Financial Control Platform for Modern Business",
|
title: "LedgerOne - The Financial Control Platform for Modern Business",
|
||||||
@ -39,198 +41,53 @@ export default function LandingPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1">
|
||||||
|
<LandingHero />
|
||||||
|
|
||||||
<main className="flex-1">
|
{/* TRUST SECTION */}
|
||||||
{/* Hero Section - Monarch Style Clean Split */}
|
<section className="bg-secondary/20 py-8">
|
||||||
<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">
|
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8 text-center">
|
<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">
|
<p className="mb-4 text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
||||||
Trusted by forward-thinking finance teams
|
Trusted by teams who treat finance as a product
|
||||||
</p>
|
</p>
|
||||||
<div className="grid grid-cols-2 gap-8 md:grid-cols-5 opacity-60 grayscale hover:grayscale-0 transition-all duration-500">
|
<div className="mb-6 flex flex-wrap items-center justify-center gap-8 text-sm text-muted-foreground">
|
||||||
{/* Placeholders for logos */}
|
<div className="flex items-baseline gap-2">
|
||||||
<div className="flex items-center justify-center font-bold text-xl">Acme Corp</div>
|
<span className="text-2xl font-semibold text-foreground">50,000+</span>
|
||||||
<div className="flex items-center justify-center font-bold text-xl">GlobalTech</div>
|
<span>users</span>
|
||||||
<div className="flex items-center justify-center font-bold text-xl">Nebula</div>
|
</div>
|
||||||
<div className="flex items-center justify-center font-bold text-xl">Vertex</div>
|
<div className="h-4 w-px bg-border" />
|
||||||
<div className="flex items-center justify-center font-bold text-xl">Horizon</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Feature Grid */}
|
<LandingFeatures />
|
||||||
<section className="py-24 lg:py-32">
|
<LandingFuture />
|
||||||
<div className="max-w-7xl mx-auto px-6 lg:px-8">
|
<LandingCta />
|
||||||
<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>
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { ContactSection } from "../../components/contact-section";
|
import { ContactSection } from "../../components/contact-section";
|
||||||
import { DemoCta } from "../../components/demo-cta";
|
import { DemoCta } from "../../components/demo-cta";
|
||||||
import { FaqSection } from "../../components/faq-section";
|
import { FaqSection } from "../../components/faq-section";
|
||||||
@ -77,14 +78,12 @@ export default function PricingPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<main className="flex-1 pt-32 pb-24 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="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">
|
<h1 className="text-4xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||||
Simple, transparent pricing.
|
Simple, transparent pricing.
|
||||||
</h1>
|
</h1>
|
||||||
@ -132,10 +131,7 @@ export default function PricingPage() {
|
|||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/register"
|
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
|
className={`mt-8 block w-full text-center ${plan.primary ? "btn-primary" : "btn-secondary"}`}
|
||||||
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
||||||
: "bg-background text-primary ring-1 ring-inset ring-primary/20 hover:ring-primary/40"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{plan.cta}
|
{plan.cta}
|
||||||
</Link>
|
</Link>
|
||||||
@ -143,8 +139,8 @@ export default function PricingPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-24 max-w-4xl mx-auto glass-panel rounded-xl p-1">
|
<div className="mt-16 max-w-4xl mx-auto glass-panel rounded-xl p-1">
|
||||||
<div className="px-6 py-4 border-b border-border">
|
<div className="px-6 py-4">
|
||||||
<h2 className="text-xl font-bold text-foreground">Compare plans</h2>
|
<h2 className="text-xl font-bold text-foreground">Compare plans</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
@ -171,7 +167,9 @@ export default function PricingPage() {
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { PageSchema } from "../../components/page-schema";
|
import { PageSchema } from "../../components/page-schema";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
@ -9,6 +11,15 @@ export const metadata = {
|
|||||||
keywords: siteInfo.keywords
|
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() {
|
export default function PrivacyPage() {
|
||||||
const schema = [
|
const schema = [
|
||||||
{
|
{
|
||||||
@ -21,58 +32,104 @@ export default function PrivacyPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
|
||||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
|
||||||
|
|
||||||
<div className="max-w-3xl mx-auto px-6 lg:px-8">
|
<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">
|
<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">
|
||||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-8">
|
<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">
|
||||||
Privacy Policy
|
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||||
</h1>
|
Privacy Policy
|
||||||
|
</h1>
|
||||||
<div className="prose prose-gray dark:prose-invert max-w-none text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<p className="lead text-lg">
|
|
||||||
Last updated: October 24, 2023
|
Last updated: October 24, 2023
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">1. Information We Collect</h3>
|
<nav className="mb-10 rounded-xl bg-secondary/20 border border-border/50 p-4" aria-label="On this page">
|
||||||
<p>
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">On this page</p>
|
||||||
We collect information you provide directly to us, such as when you create an account, connect a bank account, or request customer support.
|
<ul className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||||
</p>
|
{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>
|
<div className="space-y-10 text-muted-foreground leading-relaxed">
|
||||||
<p>
|
<section id="collect">
|
||||||
We use the information we collect to provide, maintain, and improve our services, such as to sync your transactions and generate reports.
|
<h2 className="text-foreground font-bold text-xl mb-3">1. Information We Collect</h2>
|
||||||
</p>
|
<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>
|
<section id="use">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">2. How We Use Information</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="security">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">3. Data Security</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="ccpa">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">4. California Privacy Rights (CCPA)</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="pipeda">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">5. Canadian Privacy Rights (PIPEDA)</h2>
|
||||||
If you have any questions about this Privacy Policy, please contact us at privacy@ledgerone.com.
|
<p>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useState, FormEvent } from "react";
|
import { useState, FormEvent } from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { PageSchema } from "../../components/page-schema";
|
import { PageSchema } from "../../components/page-schema";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
@ -23,10 +25,20 @@ type AuthData = {
|
|||||||
message?: string;
|
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() {
|
export default function RegisterPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [fullName, setFullName] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = 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 [status, setStatus] = useState<string>("");
|
||||||
const [isError, setIsError] = useState(false);
|
const [isError, setIsError] = useState(false);
|
||||||
|
|
||||||
@ -51,13 +63,18 @@ export default function RegisterPage() {
|
|||||||
|
|
||||||
const onSubmit = async (event: FormEvent) => {
|
const onSubmit = async (event: FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (password !== confirmPassword) {
|
||||||
|
setStatus("Passwords do not match.");
|
||||||
|
setIsError(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setStatus("Creating account...");
|
setStatus("Creating account...");
|
||||||
setIsError(false);
|
setIsError(false);
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/api/auth/register", {
|
const res = await fetch("/api/auth/register", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
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>;
|
const payload = (await res.json()) as ApiResponse<AuthData>;
|
||||||
if (!res.ok || payload.error) {
|
if (!res.ok || payload.error) {
|
||||||
@ -70,7 +87,7 @@ export default function RegisterPage() {
|
|||||||
refreshToken: payload.data.refreshToken,
|
refreshToken: payload.data.refreshToken,
|
||||||
user: payload.data.user,
|
user: payload.data.user,
|
||||||
});
|
});
|
||||||
setStatus("Account created! Please verify your email.");
|
setStatus("Account created! Redirecting...");
|
||||||
router.push("/app");
|
router.push("/app");
|
||||||
} catch {
|
} catch {
|
||||||
setStatus("Registration failed. Please try again.");
|
setStatus("Registration failed. Please try again.");
|
||||||
@ -79,89 +96,209 @@ export default function RegisterPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
<main className="relative z-10 flex flex-1 flex-col justify-center px-6 py-12 lg:py-16">
|
||||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
<motion.div
|
||||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
initial={{ opacity: 0, y: 12 }}
|
||||||
<div className="flex justify-center">
|
animate={{ opacity: 1, y: 0 }}
|
||||||
<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">
|
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
|
||||||
L1
|
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>
|
||||||
|
|
||||||
|
<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>
|
</div>
|
||||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-foreground">
|
|
||||||
Create your account
|
<p className="mt-6 text-center text-sm text-muted-foreground">
|
||||||
</h2>
|
|
||||||
<p className="mt-2 text-center text-sm text-muted-foreground">
|
|
||||||
Already have an account?{" "}
|
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
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</motion.div>
|
||||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
</main>
|
||||||
<div className="glass-panel py-8 px-4 shadow-xl sm:rounded-xl sm:px-10">
|
|
||||||
<form className="space-y-6" onSubmit={onSubmit}>
|
<div className="relative z-10">
|
||||||
<div>
|
<SiteFooter />
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
<SiteFooter />
|
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { Suspense, useState } from "react";
|
import { Suspense, useState } from "react";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
|
|
||||||
@ -80,7 +81,7 @@ function ResetPasswordForm() {
|
|||||||
<input
|
<input
|
||||||
id="password" name="password" type="password" autoComplete="new-password" required minLength={8}
|
id="password" name="password" type="password" autoComplete="new-password" required minLength={8}
|
||||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
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>
|
||||||
<p className="mt-1 text-xs text-muted-foreground">Minimum 8 characters</p>
|
<p className="mt-1 text-xs text-muted-foreground">Minimum 8 characters</p>
|
||||||
@ -93,19 +94,16 @@ function ResetPasswordForm() {
|
|||||||
<input
|
<input
|
||||||
id="confirm" name="confirm" type="password" autoComplete="new-password" required
|
id="confirm" name="confirm" type="password" autoComplete="new-password" required
|
||||||
value={confirm} onChange={(e) => setConfirm(e.target.value)}
|
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>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button type="submit" className="btn-primary w-full py-3">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Reset Password
|
Reset Password
|
||||||
</button>
|
</button>
|
||||||
{status && (
|
{status && (
|
||||||
<div className={`rounded-lg p-4 ${isError ? "bg-red-500/10 border border-red-500/20" : "bg-accent/10 border border-accent/20"}`}>
|
<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">{status}</p>
|
<p className={`text-sm text-center ${isError ? "text-red-600 dark:text-red-400" : "text-foreground"}`}>{status}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
@ -114,13 +112,16 @@ function ResetPasswordForm() {
|
|||||||
|
|
||||||
export default function ResetPasswordPage() {
|
export default function ResetPasswordPage() {
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8 mt-16 relative">
|
<div className="relative z-10 flex flex-1 flex-col justify-center pt-24 pb-16 sm:px-6 lg:px-8">
|
||||||
<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="sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<div className="flex justify-center">
|
<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
|
L1
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -134,14 +135,16 @@ export default function ResetPasswordPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
<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>}>
|
<Suspense fallback={<div className="text-center text-sm text-muted-foreground">Loading...</div>}>
|
||||||
<ResetPasswordForm />
|
<ResetPasswordForm />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { Background } from "../../components/background";
|
||||||
import { PageSchema } from "../../components/page-schema";
|
import { PageSchema } from "../../components/page-schema";
|
||||||
import { SiteFooter } from "../../components/site-footer";
|
import { SiteFooter } from "../../components/site-footer";
|
||||||
import { SiteHeader } from "../../components/site-header";
|
import { SiteHeader } from "../../components/site-header";
|
||||||
@ -9,6 +11,15 @@ export const metadata = {
|
|||||||
keywords: siteInfo.keywords
|
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() {
|
export default function TermsPage() {
|
||||||
const schema = [
|
const schema = [
|
||||||
{
|
{
|
||||||
@ -21,58 +32,98 @@ export default function TermsPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
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 />
|
<SiteHeader />
|
||||||
|
|
||||||
<main className="flex-1 pt-32 pb-24 relative overflow-hidden">
|
<main className="relative z-10 flex-1 pt-24 pb-16">
|
||||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
|
||||||
|
|
||||||
<div className="max-w-3xl mx-auto px-6 lg:px-8">
|
<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">
|
<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">
|
||||||
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-8">
|
<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">
|
||||||
Terms of Service
|
<h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl">
|
||||||
</h1>
|
Terms of Service
|
||||||
|
</h1>
|
||||||
<div className="prose prose-gray dark:prose-invert max-w-none text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<p className="lead text-lg">
|
|
||||||
Last updated: October 24, 2023
|
Last updated: October 24, 2023
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 className="text-foreground font-bold mt-8 mb-4 text-xl">1. Acceptance of Terms</h3>
|
<nav className="mb-10 rounded-xl bg-secondary/20 border border-border/50 p-4" aria-label="On this page">
|
||||||
<p>
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">On this page</p>
|
||||||
By accessing and using LedgerOne, you accept and agree to be bound by the terms and provision of this agreement.
|
<ul className="flex flex-wrap gap-x-6 gap-y-2 text-sm">
|
||||||
</p>
|
{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>
|
<div className="space-y-10 text-muted-foreground leading-relaxed">
|
||||||
<p>
|
<section id="acceptance">
|
||||||
LedgerOne provides financial data aggregation, ledger management, and reporting tools. We are not a bank or financial advisor.
|
<h2 className="text-foreground font-bold text-xl mb-3">1. Acceptance of Terms</h2>
|
||||||
</p>
|
<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>
|
<section id="service">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">2. Service Description</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="accounts">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">3. User Accounts</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="privacy">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">4. Data Privacy</h2>
|
||||||
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>
|
||||||
</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>
|
<section id="governing">
|
||||||
<p>
|
<h2 className="text-foreground font-bold text-xl mb-3">5. Governing Law</h2>
|
||||||
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>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<SiteFooter />
|
<div className="relative z-10">
|
||||||
|
<SiteFooter />
|
||||||
|
</div>
|
||||||
<PageSchema schema={schema} />
|
<PageSchema schema={schema} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -96,7 +96,10 @@ export default function VerifyEmailPage() {
|
|||||||
<div className="absolute inset-0 mesh-gradient -z-10 opacity-30" />
|
<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="sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
<div className="flex justify-center">
|
<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
|
L1
|
||||||
</div>
|
</div>
|
||||||
</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 (
|
return (
|
||||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||||
<div className="rounded-3xl border border-ink/10 bg-white/80 p-8 shadow-soft">
|
<div className="rounded-3xl border border-border bg-background/80 backdrop-blur-sm p-6 shadow-sm">
|
||||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">Contact us</p>
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">Contact us</p>
|
||||||
<h2 className="mt-3 text-2xl font-semibold">Let's talk about your ledger.</h2>
|
<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">
|
<p className="mt-2 text-sm text-muted-foreground">
|
||||||
Tell us about your workflow and we will suggest the best LedgerOne setup.
|
Tell us about your workflow and we will suggest the best LedgerOne setup.
|
||||||
</p>
|
</p>
|
||||||
<form className="mt-6 grid gap-4 md:grid-cols-2" onSubmit={onSubmit}>
|
<form className="mt-6 grid gap-4 md:grid-cols-2" onSubmit={onSubmit}>
|
||||||
<div className="space-y-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>
|
||||||
Full name
|
|
||||||
</label>
|
|
||||||
<input
|
<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"
|
type="text"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<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"
|
type="email"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<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
|
<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"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-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">Monthly transactions</label>
|
||||||
Monthly transactions
|
<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">
|
||||||
</label>
|
|
||||||
<select className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm">
|
|
||||||
<option>Under 1,000</option>
|
<option>Under 1,000</option>
|
||||||
<option>1,000 - 10,000</option>
|
<option>1,000 - 10,000</option>
|
||||||
<option>10,000 - 50,000</option>
|
<option>10,000 - 50,000</option>
|
||||||
@ -56,24 +52,19 @@ export function ContactSection() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 md:col-span-2">
|
<div className="space-y-2 md:col-span-2">
|
||||||
<label className="text-xs uppercase tracking-[0.2em] text-muted">
|
<label className="block text-sm font-medium text-foreground">What do you want to solve?</label>
|
||||||
What do you want to solve?
|
|
||||||
</label>
|
|
||||||
<textarea
|
<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
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<button
|
<button type="submit" className="btn-primary w-full py-3">
|
||||||
type="submit"
|
|
||||||
className="w-full rounded-2xl bg-ink px-4 py-3 text-sm font-semibold text-haze"
|
|
||||||
>
|
|
||||||
Send message
|
Send message
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,22 +2,19 @@ import Link from "next/link";
|
|||||||
|
|
||||||
export function DemoCta() {
|
export function DemoCta() {
|
||||||
return (
|
return (
|
||||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||||
<div className="rounded-3xl border border-ink/10 bg-ink p-8 text-haze shadow-soft">
|
<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 className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs uppercase tracking-[0.3em] text-haze/60">Book a demo</p>
|
<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">
|
<h2 className="mt-3 text-2xl font-semibold text-foreground">
|
||||||
See LedgerOne in a live walkthrough.
|
See LedgerOne in a live walkthrough.
|
||||||
</h2>
|
</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.
|
Talk through your workflow and learn how to keep a complete audit trail.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link href="/book-demo" className="btn-primary inline-flex shrink-0">
|
||||||
href="/book-demo"
|
|
||||||
className="inline-flex rounded-full bg-haze px-5 py-2 text-sm font-semibold text-ink"
|
|
||||||
>
|
|
||||||
Book a demo
|
Book a demo
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,23 +9,23 @@ type FaqSectionProps = {
|
|||||||
export function FaqSection({ title, subtitle, limit }: FaqSectionProps) {
|
export function FaqSection({ title, subtitle, limit }: FaqSectionProps) {
|
||||||
const items = limit ? defaultFaqs.slice(0, limit) : defaultFaqs;
|
const items = limit ? defaultFaqs.slice(0, limit) : defaultFaqs;
|
||||||
return (
|
return (
|
||||||
<section className="mx-auto mt-16 max-w-6xl px-6">
|
<section className="mx-auto mt-10 max-w-6xl px-6">
|
||||||
<div className="rounded-3xl border border-ink/10 bg-white/80 p-8 shadow-soft">
|
<div className="rounded-3xl border border-border bg-background/80 backdrop-blur-sm p-6 shadow-sm">
|
||||||
<p className="text-xs uppercase tracking-[0.3em] text-muted">FAQ</p>
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-muted-foreground">FAQ</p>
|
||||||
<h2 className="mt-3 text-2xl font-semibold">
|
<h2 className="mt-3 text-2xl font-semibold text-foreground">
|
||||||
{title ?? "Common questions, clear answers."}
|
{title ?? "Common questions, clear answers."}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-sm text-muted">
|
<p className="mt-2 text-sm text-muted-foreground">
|
||||||
{subtitle ??
|
{subtitle ??
|
||||||
"Find pricing, account, export, and security answers for LedgerOne."}
|
"Find pricing, account, export, and security answers for LedgerOne."}
|
||||||
</p>
|
</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) => (
|
{items.map((item, index) => (
|
||||||
<li key={item.question} className="space-y-2">
|
<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}
|
{index + 1}. {item.question}
|
||||||
</p>
|
</p>
|
||||||
<p>{item.answer}</p>
|
<p className="text-muted-foreground leading-relaxed">{item.answer}</p>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
@ -73,19 +73,25 @@ export function HeroActions() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onStart}
|
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
|
Start a private ledger
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onViewExport}
|
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
|
View export sample
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{status ? <p className="text-xs text-muted">{status}</p> : null}
|
{status ? <p className="text-xs text-muted-foreground">{status}</p> : null}
|
||||||
</div>
|
</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 Link from "next/link";
|
||||||
|
import { Logo } from "./logo";
|
||||||
|
|
||||||
export function SiteFooter() {
|
export function SiteFooter() {
|
||||||
return (
|
return (
|
||||||
<footer className="border-t border-border bg-background mt-auto">
|
<footer className="bg-transparent mt-auto">
|
||||||
<div className="mx-auto max-w-7xl px-6 py-12 lg:px-8">
|
<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="flex flex-col items-start justify-between gap-8 sm:flex-row">
|
||||||
<div className="max-w-xs">
|
<div className="max-w-xs">
|
||||||
<div className="flex items-center gap-2 mb-4">
|
<div className="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">
|
<Logo />
|
||||||
L1
|
|
||||||
</div>
|
|
||||||
<span className="text-lg font-bold tracking-tight text-foreground">
|
|
||||||
LedgerOne
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
<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.
|
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>
|
</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">
|
<p className="text-xs text-muted-foreground">
|
||||||
© {new Date().getFullYear()} LedgerOne Inc. All rights reserved.
|
© {new Date().getFullYear()} LedgerOne Inc. All rights reserved.
|
||||||
</p>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { MoodToggle } from "./currency-toggle";
|
import { Logo } from "./logo";
|
||||||
|
|
||||||
export function SiteHeader() {
|
export function SiteHeader() {
|
||||||
const [scrolled, setScrolled] = useState(false);
|
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">
|
<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">
|
<Logo />
|
||||||
<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>
|
|
||||||
|
|
||||||
<nav className="hidden items-center gap-8 md:flex">
|
<nav className="hidden items-center gap-8 md:flex">
|
||||||
<Link href="/" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
<Link href="/" className="nav-link">Home</Link>
|
||||||
Home
|
<Link href="/about" className="nav-link">About</Link>
|
||||||
</Link>
|
<Link href="/features/cash-flow" className="nav-link">Cash Flow</Link>
|
||||||
<Link href="/about" className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors">
|
<Link href="/features/reports" className="nav-link">Reports</Link>
|
||||||
About
|
<Link href="/compare/vs-spreadsheets" className="nav-link">Compare</Link>
|
||||||
</Link>
|
<Link href="/pricing" className="nav-link">Pricing</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>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<MoodToggle />
|
|
||||||
<div className="hidden md:flex items-center gap-4 border-l border-border pl-4">
|
<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
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link href="/register" className="btn-primary-sm">
|
||||||
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"
|
|
||||||
>
|
|
||||||
Get Started
|
Get Started
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</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",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
|||||||
@ -46,6 +46,12 @@ const config: Config = {
|
|||||||
"glass": "0 8px 32px 0 rgba(31, 38, 135, 0.07)",
|
"glass": "0 8px 32px 0 rgba(31, 38, 135, 0.07)",
|
||||||
"glow": "0 0 20px rgba(182, 255, 59, 0.3)",
|
"glow": "0 0 20px rgba(182, 255, 59, 0.3)",
|
||||||
"glow-teal": "0 0 30px rgba(49, 98, 99, 0.2)",
|
"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: {
|
animation: {
|
||||||
"pulse-slow": "pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
"pulse-slow": "pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user