Restructure blogs as Astro monorepo
Some checks are pending
Build / build (push) Waiting to run

This commit is contained in:
Neeldhara Misra 2026-06-13 21:15:16 +02:00
parent fb5a3b8093
commit 58d8b661a8
1055 changed files with 116254 additions and 89 deletions

View file

@ -0,0 +1,190 @@
import { PlusSigns } from "@/components/icons/plus-signs";
import JoinUs from "@/components/sections/join-us";
import {
Carousel,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
export function AboutSection() {
return (
<>
{/* Hero Section */}
<section className="lg:py-15 container relative max-w-5xl py-10 md:py-12">
<div className="">
<h1 className="text-4xl font-semibold tracking-tighter md:text-5xl lg:text-6xl">
A different
<br />
kind of bank.
</h1>
<p className="text-muted-foreground font-mona mt-4 max-w-xl text-2xl md:text-3xl">
We&apos;re on a mission to transform financial services by
harnessing vast amounts of untapped financial data.
</p>
</div>
{/* Background decoration */}
<>
<div className="absolute inset-0 z-[-1] -translate-y-1/2 translate-x-1/4 blur-[100px] will-change-transform">
<div className="bg-primary-gradient/25 -translate-x-1/5 absolute right-0 top-0 h-[250px] w-[800px] rounded-full md:h-[300px]" />
<div className="bg-secondary-gradient/15 absolute right-0 top-0 size-[250px] -translate-x-1/3 -translate-y-1/3 rounded-full md:size-[400px]" />
</div>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_80%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
</>
</section>
{/* Stats Section */}
<section className="container max-w-5xl border-y py-5">
<h2 className="mini-title">By the numbers</h2>
<div className="mt-6 grid grid-cols-2 gap-8 md:grid-cols-4">
<div>
<h3 className="text-4xl font-medium tracking-tight md:text-5xl lg:text-6xl">
$150M
</h3>
<p className="text-muted-foreground mt-1 font-medium">Raised</p>
</div>
<div>
<h3 className="text-4xl font-medium tracking-tight md:text-5xl lg:text-6xl">
20K
</h3>
<p className="text-muted-foreground mt-1 font-medium">Companies</p>
</div>
<div>
<h3 className="text-4xl font-medium tracking-tight md:text-5xl lg:text-6xl">
1.3B
</h3>
<p className="text-muted-foreground mt-1 font-medium">
Monthly transactions
</p>
</div>
<div>
<h3 className="text-4xl font-medium tracking-tight md:text-5xl lg:text-6xl">
1.5K
</h3>
<p className="text-muted-foreground mt-1 font-medium">
Connections per minute
</p>
</div>
</div>
</section>
{/* Mission Section */}
<section className="lg:py-15 container max-w-5xl py-10 md:py-12">
<div className="max-w-xl space-y-5 md:space-y-8 lg:space-y-10">
<p className="text-lg">
Financial services have changed, are changing, and will continue to
change for the better. Now is the time for finance to be
developer-first and API-driven. But in order to do this it needs a
new foundation.
</p>
<h2 className="font-mona text-2xl font-medium tracking-tight md:text-3xl">
We were always told that banks can&apos;t be platforms.
</h2>
<p className="text-lg">
Everyone tried fixing the problem by layering APIs on legacy
systems, creating abstractions and inefficiencies. We have spent
years building and scaling companies like Plaid, Stripe, and Affirm,
confronting these limitations firsthand. The current solutions
aren&apos;t good enough. We believe that banking infrastructure must
be reimagined as an API platform. But we had to start from ground
zero.
</p>
</div>
</section>
{/* Image Grid Section */}
<section className="lg:pb-15 my-5 pb-10 md:my-8 md:pb-12 lg:my-12">
<Carousel
opts={{
align: "start",
}}
>
<CarouselContent className="-ml-4">
<CarouselItem className="basis-[80%] lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px]">
<img
src="/images/about/4.webp"
alt="Charter team member working"
className="size-full object-cover"
/>
</div>
</CarouselItem>
<CarouselItem className="basis-[80%] lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px]">
<img
src="/images/about/2.webp"
alt="Modern workspace setup"
className="size-full object-cover"
/>
</div>
</CarouselItem>
<CarouselItem className="basis-[80%] lg:basis-1/3 xl:basis-[40%]">
<div className="relative h-[330px] lg:h-[440px]">
<img
src="/images/about/3.webp"
alt="Team collaboration"
className="size-full object-cover"
/>
</div>
</CarouselItem>
</CarouselContent>
</Carousel>
</section>
{/* CoreAPI Section */}
<section className="container max-w-5xl pb-10 md:pb-20">
<div className="ml-auto mr-0 max-w-xl space-y-5 md:space-y-8 lg:-translate-x-10 lg:space-y-10">
<p className="text-lg">
We started building CoreAPI in 2019 and launched in 2022. Every
endpoint has been designed from the ground up with no technical
debt or legacy systems. We are purpose-built to power financial
innovation for the next hundred years.
</p>
<h2 className="font-mona text-2xl font-medium tracking-tight md:text-3xl">
We are a bit of a unique company not your standard tech or fintech
company.
</h2>
<p className="text-lg">
We are 100% founder and team-owned, profitable, and we keep our team
lean. Over time, this page will become more polished, but for now,
we&apos;re focused on delivering for developers.
</p>
</div>
</section>
{/* Founding Team Section */}
<section className="lg:py-15 container max-w-5xl py-10 md:py-12">
<div className="grid gap-5 md:grid-cols-2 md:gap-10 lg:gap-16">
<div className="order-2 md:order-1">
<h2 className="font-mona text-3xl font-semibold md:text-4xl">
The founding team
</h2>
<p className="mt-5 text-lg md:mt-6">
We started building CoreAPI in 2019 and launched in 2022. Every
endpoint has been designed from the ground up with no technical
debt or legacy systems. We are purpose-built to power financial
innovation for the next hundred years. We are 100% founder and
team-owned, profitable, and we keep our team lean. Over time, this
page will become more polished, but for now, we&apos;re focused on
delivering for developers. If you&apos;re interested in building
the future of financial APIs, check out our open roles below.
</p>
</div>
<div className="relative order-1 h-[300px] w-full md:order-2 md:h-[400px]">
<img
src="/images/about/1.webp"
alt="Founding team collaboration"
className="size-full object-cover"
/>
</div>
</div>
</section>
<JoinUs />
</>
);
}

View file

@ -0,0 +1,80 @@
import { ChevronRight, Timer, Wallet, Terminal, Calendar } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
} from "@/components/ui/card";
const features = [
{
description: "Faster than Wire, and more affordable, too.",
icon: Timer,
href: "/pricing",
},
{
description: "Send or request funds. FedNow does both.",
icon: Wallet,
href: "/pricing",
},
{
description: "API request to creation in seven minutes.",
icon: Terminal,
href: "/pricing",
},
{
description: "Settle any time, any day. What Bank holiday?",
icon: Calendar,
href: "/pricing",
},
];
export default function AIChatbot() {
return (
<section id="ai-chatbot" className="relative py-16 md:py-28 lg:py-32">
<div className="container max-w-5xl">
<div className="text-center">
<h3 className="mini-title">MORE COMPUTER FUGAZI</h3>
<h2 className="mt-4 text-4xl font-semibold tracking-tight md:text-5xl lg:text-6xl">
And we have an AI chatbot
</h2>
<p className="text-muted-foreground mt-3 text-xl font-medium md:text-2xl">
{`We're betting on agents replacing our staff next year.`}
</p>
</div>
<div className="mt-8 grid grid-cols-2 gap-2.5 md:mt-12 lg:mt-20 lg:grid-cols-4 lg:gap-6">
{features.map((feature, index) => (
<Card key={index} className="flex flex-col">
<CardHeader className="max-md:p-3">
<feature.icon className="text-primary size-8" />
<CardDescription className="text-foreground mt-4 font-medium">
{feature.description}
</CardDescription>
</CardHeader>
<CardContent className="mt-auto max-md:p-3">
<Button
variant="outline"
asChild
className="border-border group w-[min(100%,300px)]"
>
<a href={feature.href}>
Learn more
<span className="sr-only">
{" "}
about {feature.description.toLowerCase()}
</span>
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
</CardContent>
</Card>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,57 @@
import { ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
} from "@/components/ui/card";
import { BLOG_SITES } from "@/blog-sites.js";
const features = BLOG_SITES.map((site) => ({
title: site.title,
description: site.description,
href: site.url,
}));
export default function AllBlogs() {
return (
<section id="ai-chatbot" className="relative py-16 md:py-28 lg:py-32">
<div className="container max-w-5xl">
<div className="text-center">
<h3 className="mini-title">CURRENTLY ACTIVE BLOGS</h3>
</div>
<div className="mt-8 grid grid-cols-1 gap-2.5 sm:grid-cols-2 md:mt-12 lg:mt-20 lg:grid-cols-3 lg:gap-6">
{features.map((feature, index) => (
<Card key={index} className="flex flex-col">
<CardHeader className="max-md:p-3">
<h3 className="text-lg font-semibold">{feature.title}</h3>
<CardDescription className="text-foreground mt-4 font-medium">
{feature.description}
</CardDescription>
</CardHeader>
<CardContent className="mt-auto max-md:p-3">
<Button
variant="outline"
asChild
className="border-border group w-[min(100%,300px)]"
>
<a href={feature.href}>
Read On
<span className="sr-only">
{" "}
about {feature.description.toLowerCase()}
</span>
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
</CardContent>
</Card>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,88 @@
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { format } from "date-fns";
import { Calendar, Clock, ArrowLeft, User } from "lucide-react";
const BlogPost = ({
post,
children,
}: {
post: any;
children: React.ReactNode;
}) => {
const { title, authorName, image, pubDate, description, authorImage } =
post.data;
return (
<>
{/* Hero section with gradient background and post info */}
<section className="pb-8 pt-4">
<div className="container max-w-4xl">
<div className="space-y-2 text-center">
<h1 className="text-2xl font-bold tracking-tight md:text-4xl lg:text-5xl">
{title}
</h1>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg">
{description}
</p>
</div>
<div className="mx-auto mt-6 flex max-w-md flex-wrap items-center justify-center gap-6">
{/* Author info */}
{authorImage ? (
<div className="flex items-center gap-3">
</div>
) : (
<div className="flex items-center gap-3">
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full shadow-sm">
<User className="h-5 w-5" />
</div>
<div className="flex flex-col text-sm">
<span className="font-medium">
{authorName || "Anonymous"}
</span>
<span className="text-muted-foreground">Author</span>
</div>
</div>
)}
{/* Date info */}
<div className="flex items-center gap-4">
<div className="text-muted-foreground flex items-center gap-1 text-sm">
<Calendar className="h-4 w-4" />
<span>{format(pubDate, "MMMM d, yyyy")}</span>
</div>
<div className="text-muted-foreground flex items-center gap-1 text-sm">
<Clock className="h-4 w-4" />
<span>5 min read</span>
</div>
</div>
</div>
</div>
</section>
{/* Featured image */}
{image && (
<section className="container max-w-5xl py-4">
<div className="aspect-video w-full overflow-hidden rounded-xl border">
<img
src={image}
alt={title}
className="h-full w-full object-cover"
/>
</div>
</section>
)}
{/* Article content */}
<section className="container my-10 max-w-3xl">
<article className="prose prose-lg dark:prose-invert prose-headings:font-semibold prose-a:text-primary mx-auto">
{children}
</article>
</section>
</>
);
};
export { BlogPost };

View file

@ -0,0 +1,135 @@
import { Button } from "@/components/ui/button";
import { ArrowRight } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { Calendar, Clock, User } from "lucide-react";
import { PlusSigns } from "../icons/plus-signs";
const BlogPosts = ({
posts,
collection = "",
}: {
posts: any[];
collection?: string;
}) => {
// Get the first post as the featured post
const featuredPost = posts[0];
const remainingPosts = posts.slice(1);
const hrefFor = (post: any) =>
collection ? `/${collection}/${post.id}/` : `/${post.id}/`;
return (
<div className="relative py-16 md:py-28 lg:py-32">
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
{/* Featured Post */}
<section className="mt-8">
<div className="container max-w-6xl">
<a
href={hrefFor(featuredPost)}
className="bg-card group relative mb-16 overflow-hidden rounded-xl border shadow-md"
>
<div className="flex flex-col gap-6 lg:flex-row">
<div className="lg:w-1/2">
<div className="p-2 lg:p-4">
<img
src={featuredPost.data.image}
alt={featuredPost.data.title}
className="aspect-video w-full rounded-lg object-cover transition-transform group-hover:scale-[1.02]"
/>
</div>
</div>
<div className="flex flex-col justify-center p-4 pb-8 lg:w-1/2 lg:pr-8">
<Badge variant="outline" className="mb-3 w-fit">
Featured Post
</Badge>
<h2 className="mb-3 text-2xl font-bold group-hover:underline md:text-3xl">
{featuredPost.data.title}
</h2>
<p className="text-muted-foreground mb-4 line-clamp-3 text-base">
{featuredPost.data.description}
</p>
<div className="mb-6 flex gap-4">
<div className="text-muted-foreground flex items-center text-sm">
<Calendar className="mr-1 h-4 w-4" />
{new Date(featuredPost.data.pubDate).toLocaleDateString(
"en-US",
{
month: "long",
day: "numeric",
year: "numeric",
},
)}
</div>
<div className="text-muted-foreground flex items-center text-sm">
<Clock className="mr-1 h-4 w-4" />5 min read
</div>
</div>
</div>
</div>
</a>
</div>
</section>
{/* Regular Posts Grid */}
<section className="mt-8">
<div className="container max-w-6xl">
<h2 className="mb-8 text-xl font-semibold md:text-2xl">
Recent Articles
</h2>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{remainingPosts.map((post) => (
<a
key={post.id}
className="bg-card group rounded-xl border shadow-sm transition-all hover:shadow-md"
href={hrefFor(post)}
>
<div className="p-2">
<img
src={post.data.image}
alt={post.data.title}
className="aspect-video w-full rounded-lg object-cover transition-transform group-hover:scale-[1.01]"
/>
</div>
<div className="px-4 pb-5 pt-2">
<h2 className="mb-2 text-xl font-semibold group-hover:underline">
{post.data.title}
</h2>
<p className="text-muted-foreground line-clamp-2 text-sm">
{post.data.description}
</p>
<div className="text-muted-foreground mt-3 flex items-center gap-3 text-xs">
<div className="flex items-center gap-1">
<Calendar className="h-3.5 w-3.5" />
<span>
{new Date(post.data.pubDate).toLocaleDateString(
"en-US",
{
month: "short",
day: "numeric",
year: "numeric",
},
)}
</span>
</div>
<div className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5" />
<span>5 min read</span>
</div>
</div>
</div>
</a>
))}
</div>
</div>
</section>
</div>
);
};
export { BlogPosts };

View file

@ -0,0 +1,100 @@
import { useState } from "react";
import { PlusSigns } from "../icons/plus-signs";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
const FEATURES = [
{
id: "move-money",
title: "Move and hold money",
description:
"Previously, emerging financial companies were forced to navigate costly middleware solutions that connected them to outdated sponsor bank systems.",
image: "/images/homepage/code-snippet.webp",
},
{
id: "card-program",
title: "Build a modern card program",
description:
"Create and manage virtual and physical cards with complete control over spending limits, merchant categories, and real-time transaction monitoring.",
image: "/images/homepage/code-snippet.webp",
},
{
id: "lend-money",
title: "Lend money",
description:
"Offer various lending products through our platform with automated underwriting, loan servicing, and compliance management.",
image: "/images/homepage/code-snippet.webp",
},
];
export default function CodeSecurity() {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<section
id="code-security"
className="container max-w-5xl py-16 md:py-28 lg:py-32"
>
<div className="from-primary-gradient/20 bg-linear-to-bl relative overflow-hidden rounded-3xl border to-transparent py-5 md:py-6 lg:py-8">
<div className="absolute inset-0 z-[-1]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
<div className="md:px-6 lg:px-8">
<div className="max-md:px-5">
<h3 className="mini-title">WHY CHARTER?</h3>
<h2 className="mt-3 text-3xl font-semibold tracking-tight md:text-4xl lg:text-5xl">
Code security
</h2>
</div>
<div className="mt-10 flex gap-12 overflow-hidden max-md:flex-col md:mt-16 lg:mt-20">
<Accordion
type="single"
className="flex-1"
defaultValue="0"
onValueChange={(value) => setSelectedIndex(Number(value))}
>
{FEATURES.map((feature, index) => (
<AccordionItem
key={feature.id}
value={index.toString()}
className="border-black/20 last:border-none dark:border-white/20"
>
<AccordionTrigger className="hover:no-underline max-md:px-5">
<h3 className="text-input font-inter text-xl font-bold">
{feature.title}
</h3>
</AccordionTrigger>
<AccordionContent className="">
<p className="text-muted-foreground font-medium leading-relaxed max-md:px-5">
{feature.description}
</p>
<div className="relative mt-4 h-[280px] translate-x-5 md:hidden">
<img
src={feature.image}
alt={feature.title}
className="size-full rounded-2xl object-cover object-left-top shadow-xl"
/>
</div>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
<div className="relative h-[240px] max-md:hidden max-md:translate-x-6 md:flex-1">
<img
src={FEATURES[selectedIndex].image}
alt={FEATURES[selectedIndex].title}
className="size-full rounded-2xl object-cover object-left-top shadow-xl"
/>
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,84 @@
import { PlusSigns } from "@/components/icons/plus-signs";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
export function ContactPage() {
return (
<section className="relative py-16 md:py-28 lg:py-32">
<div className="container max-w-2xl">
<div className="text-center">
<h1 className="text-4xl font-semibold tracking-tight md:text-5xl lg:text-6xl">
Book a demo
</h1>
<p className="text-muted-foreground mt-4 text-2xl md:text-3xl">
Learn how Charter can work for you
</p>
</div>
<form className="mt-8 space-y-5 md:mt-12 lg:mt-20">
{/* First Name */}
<div className="flex flex-col gap-2">
<Label htmlFor="firstName">First name</Label>
<Input id="firstName" placeholder="Enter your first name" />
</div>
{/* Last Name */}
<div className="flex flex-col gap-2">
<Label htmlFor="lastName">Last name</Label>
<Input id="lastName" placeholder="Enter your last name" />
</div>
{/* Email */}
<div className="flex flex-col gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="Enter your email address"
/>
</div>
{/* Company */}
<div className="flex flex-col gap-2">
<Label htmlFor="company">
Company name{" "}
<span className="text-muted-foreground">(optional)</span>
</Label>
<Input id="company" placeholder="Enter your company name" />
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="employees">Number of employees</Label>
<Input
id="employees"
placeholder="Enter number of employees"
type="number"
/>
</div>
{/* Message */}
<div className="flex flex-col gap-2">
<Label htmlFor="message">Message</Label>
<Textarea
id="message"
rows={5}
placeholder="Enter your message"
className="resize-none"
/>
</div>
<div className="flex justify-end">
<Button type="submit" size="lg">
Submit
</Button>
</div>
</form>
</div>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
</section>
);
}

View file

@ -0,0 +1,120 @@
import { PlusSigns } from "@/components/icons/plus-signs";
import Testimonials from "@/components/sections/testimonials";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
const FAQ_ITEMS = [
{
category: "SUPPORT",
items: [
{
question: "Is there a free version?",
answer:
"Yes! We offer a generous free plan with just enough features except that one feature you really want! Our strategy is to get your credit card details on file then steadily double our prices against inflation rates.",
},
{
question: "How do I update my account without breaking my laptop?",
answer:
"Our platform is designed with safety in mind. You can update your account settings through our intuitive dashboard without any risk to your hardware. We have multiple safeguards in place to prevent any system conflicts.",
},
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
{
category: "YOUR ACCOUNT",
items: [
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
{
category: "OTHER QUESTIONS",
items: [
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
];
export default function FAQPage() {
return (
<>
<section className="relative py-16 md:py-28 lg:py-32">
<div className="container">
<div className="text-center">
<h1 className="text-4xl font-semibold tracking-tight md:text-5xl lg:text-6xl">
Frequently Asked Questions
</h1>
<p className="text-muted-foreground mt-4 text-2xl md:text-3xl">
Everything you need to know about Charter
</p>
</div>
<div className="mx-auto mt-8 max-w-2xl space-y-12 md:mt-12 lg:mt-20">
{FAQ_ITEMS.map((category) => (
<Card key={category.category} className="border-hidden">
<CardHeader className="pb-0">
<h3 className="text-accent-foreground border-b pb-4 font-mono text-sm font-medium uppercase tracking-widest">
{category.category}
</h3>
</CardHeader>
<CardContent>
<Accordion type="single" collapsible className="w-full">
{category.items.map((item, i) => (
<AccordionItem
key={i}
value={`${category.category}-${i}`}
className="border-muted border-b last:border-0"
>
<AccordionTrigger className="text-base font-medium hover:no-underline">
{item.question}
</AccordionTrigger>
<AccordionContent className="text-muted-foreground text-base font-medium">
{item.answer}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</CardContent>
</Card>
))}
</div>
</div>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
</section>
<Testimonials />
</>
);
}

View file

@ -0,0 +1,157 @@
import { PlusSigns } from "../icons/plus-signs";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
const items = [
{
title: (
<>
Unparalleled
<br />
VAR flexibility
</>
),
description: [
"VAR is often known for its lengthy processing times. At Charter, we focus on achieving the fastest VAR transfers—often completed within hours.",
"Unlike traditional banks and middleware, we connect directly with the Federal Reserve to ensure the fastest, most transparent transfers possible.",
],
image: {
src: "/images/homepage/why-charter/1.webp",
alt: "Code snippet",
type: "fill",
},
className:
"flex flex-col pl-6 py-6 overflow-hidden md:col-span-3 md:flex-row gap-6 md:gap-12",
headerClassName: "flex-2 p-0",
contentClassName:
"relative h-[320px] w-full p-0 self-center overflow-hidden rounded-l-xl border md:flex-1",
},
{
title: "Unparalleled VAR flexibility",
description: [
"VAR has a reputation for taking too long. At Charter, we optimise for the fastest VAR transfers possible — often in a matter of hours.",
"Unlike legacy banks and middleware providers, we have a direct connection to the Federal Reserve to facilitate the quickest transfers.",
],
image: {
src: "/images/homepage/why-charter/2.svg",
alt: "VAR Process Flow",
width: 283,
height: 45,
},
className: "md:col-span-2 flex flex-col justify-center",
contentClassName:
"flex items-center justify-center p-6 max-md:mt-4 max-md:mb-8",
imagePosition: "content",
},
{
title: "Unparalleled VAR flexibility",
description: [
"VAR has a reputation for taking too long. At Charter, we optimise for the fastest VAR transfers possible — often in a matter of hours.",
],
image: {
src: "/images/homepage/why-charter/3.svg",
alt: "VAR Process Diagram",
width: 283,
height: 45,
},
className: "md:col-span-2",
headerClassName: "h-full",
imagePosition: "header",
},
{
title: "Unparalleled VAR flexibility",
description: [
"Unlike traditional banks and middleware, we connect directly with the Federal Reserve to ensure the fastest, most transparent transfers possible.",
],
image: {
src: "/images/homepage/code-snippet.webp",
alt: "Code snippet",
type: "fill",
},
className: "overflow-hidden md:col-span-3 ",
headerClassName: "",
contentClassName:
"relative h-[242px] mt-2 p-0 ml-8 w-full md:max-w-[400px] lg:max-w-[500px] overflow-hidden md:mx-auto shadow-xl rounded-t-2xl",
},
];
const FeaturedPosts = () => {
return (
<section
id="featured-posts"
className="container relative py-16 md:py-28 lg:py-32"
>
<h3 className="mini-title">Featured Posts</h3>
<div className="relative z-10 mt-8 grid grid-cols-1 gap-6 md:grid-cols-5">
{items.map((item, index) => (
<Card
key={index}
className={cn("col-span-1 shadow-xl", item.className)}
>
<CardHeader className={item.headerClassName}>
{item.imagePosition === "header" && (
<img
src={item.image.src}
alt={item.image.alt}
width={item.image.width}
height={item.image.height}
className="flex-1 self-center max-md:mb-8 max-md:mt-4 dark:[filter:brightness(0)_saturate(100%)_invert(100%)]"
/>
)}
<CardTitle className="text-3xl">{item.title}</CardTitle>
{item.description.map((desc, i) => (
<CardDescription
key={i}
className="mt-2 text-base font-medium leading-snug"
>
{desc}
</CardDescription>
))}
</CardHeader>
<CardContent className={item.contentClassName}>
{item.image.type === "fill" ? (
<img
src={item.image.src}
alt={item.image.alt}
className="size-full object-cover object-left-top"
/>
) : (
item.imagePosition === "content" && (
<img
src={item.image.src}
alt={item.image.alt}
width={item.image.width}
height={item.image.height}
className="self-center dark:[filter:brightness(0)_saturate(100%)_invert(100%)]"
/>
)
)}
</CardContent>
</Card>
))}
</div>
{/* Background decoration */}
<>
<div className="absolute inset-0 isolate will-change-transform">
<div className="bg-primary-gradient/28 absolute top-1/2 size-[700px] -translate-y-1/2 rounded-full blur-[300px]" />
<div className="bg-secondary-gradient/16 absolute right-0 top-1/2 size-[700px] -translate-y-1/2 -rotate-12 rounded-full blur-[300px]" />
<div className="bg-tertiary-gradient/6 absolute bottom-1/4 right-20 z-[1] h-[500px] w-[800px] -rotate-12 rounded-full blur-[100px] md:bottom-10" />
</div>
<div className="absolute -inset-x-20 top-0 [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_85%)]">
<PlusSigns className="text-foreground/[0.075] h-full w-full" />
</div>
</>
</section>
);
};
export default FeaturedPosts;

View file

@ -0,0 +1,86 @@
import { FaXTwitter, FaLinkedin, FaFacebook } from "react-icons/fa6";
import { Home } from "lucide-react";
const navigation = [
{
title: "Products",
links: [
{ name: "VAR", href: "/#code-security" },
{ name: "Credit Transfers", href: "/#why-charter" },
{ name: "Credit Accounts", href: "/#ai-chatbot" },
{ name: "Loan Origination", href: "/#ai-chatbot" },
{ name: "Loan Purchase", href: "/#ai-chatbot" },
],
},
{
title: "Support",
links: [
{ name: "Pricing", href: "/pricing" },
{ name: "FAQ", href: "/faq" },
{ name: "Demo", href: "/contact" },
{ name: "Contact", href: "/contact" },
],
},
{
title: "Company",
links: [
{ name: "About", href: "/about" },
{ name: "Terms of Service", href: "/terms" },
{ name: "Privacy Policy", href: "/privacy" },
],
},
];
const socialLinks = [
{ icon: FaXTwitter, href: "https://twitter.com", label: "Twitter" },
{ icon: FaFacebook, href: "https://facebook.com", label: "Facebook" },
{ icon: FaLinkedin, href: "https://linkedin.com", label: "LinkedIn" },
];
export default function Footer() {
return (
<footer className="pt-16 md:pt-28 lg:pt-32">
<div className="w-full px-4 md:px-6 lg:px-8">
<div className="mx-auto max-w-5xl">
{/* Navigation Section */}
<nav className="flex flex-wrap justify-between gap-x-32 gap-y-20 border-b pb-14 lg:pb-20">
</nav>
{/* Bottom Section */}
<div className="py-8">
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex items-center gap-4">
<Home className="h-6 w-6" />
<p className="text-sm font-medium">
© {new Date().getFullYear()} Charter -{" "}
<a
href="https://shadcnblocks.com"
className="underline transition-opacity hover:opacity-80"
target="_blank"
>
Shadcnblocks.com
</a>
</p>
</div>
<div className="flex items-center gap-6">
{socialLinks.map((link) => (
<a
aria-label={link.label}
key={link.href}
href={link.href}
className="hover:text-muted-foreground"
target="_blank"
rel="noopener noreferrer"
>
<link.icon />
</a>
))}
</div>
</div>
</div>
</div>
</div>
</footer>
);
}

View file

@ -0,0 +1,130 @@
import {
ChevronRight,
Wallet,
Waypoints,
Building2,
ArrowLeftRight,
} from "lucide-react";
import { PlusSigns } from "../icons/plus-signs";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
const features = [
{
id: "var",
title: "Virtual Account",
description: "Sed do eiusmod tempor incididunt ut labore",
icon: Wallet,
image: "/images/homepage/hero/1.webp",
},
{
id: "wire",
title: "Wire",
description: "Sed do eiusmod tempor incididunt ut labore",
icon: Waypoints,
image: "/images/homepage/hero/2.webp",
},
{
id: "bank-accounts",
title: "Bank Accounts",
description: "Sed do eiusmod tempor incididunt ut labore",
icon: Building2,
image: "/images/homepage/hero/3.webp",
},
{
id: "bank-transfers",
title: "Bank Transfers",
description: "Sed do eiusmod tempor incididunt ut labore",
icon: ArrowLeftRight,
image: "/images/homepage/hero/4.webp",
},
];
export default function Hero() {
return (
<section className="bg-background relative overflow-hidden pt-16 md:pt-28 lg:pt-32">
<div className="relative z-10">
<div className="container max-w-5xl text-center">
<h1 className="text-4xl font-semibold tracking-tighter md:text-5xl lg:text-6xl">
Secure. Composable. Bankable.
</h1>
<p className="text-muted-foreground font-mona mt-4 text-balance text-2xl md:text-3xl">
Charter is the fit-for-purpose developer API for building robust,
encrypted finance products.
</p>
<div className="mt-7">
<Button asChild size="lg">
<a href="/signup">
Start building for free
<ChevronRight className="size-4" />
</a>
</Button>
</div>
</div>
{features.map((feature) => (
<img
key={feature.id}
src={feature.image}
alt={feature.title}
className="lg:translate-y-15 hidden size-full translate-y-8 object-contain"
/>
))}
<Tabs defaultValue="var" className="mt-8 md:mt-12 lg:mt-20">
{/* Tab Content */}
<div className="container">
{features.map((feature) => (
<TabsContent
key={feature.id}
value={feature.id}
className="relative aspect-[2.116/1] overflow-hidden"
>
<img
src={feature.image}
alt={feature.title}
className="lg:translate-y-15 size-full translate-y-8 object-contain"
/>
</TabsContent>
))}
</div>
{/* Tab Triggers */}
<div className="bg-background pb-16 pt-12 md:pb-28 lg:pb-32">
<TabsList className="mx-auto flex h-auto max-w-4xl justify-start gap-4 overflow-x-auto bg-transparent max-lg:px-5">
{features.map((feature) => (
<TabsTrigger
key={feature.id}
value={feature.id}
className="ring-secondary-foreground group min-w-[200px] flex-1 justify-start whitespace-normal rounded-lg px-4 py-3 text-start transition-colors duration-300 data-[state=active]:bg-transparent data-[state=active]:ring lg:px-6 lg:py-4"
>
<div className="flex flex-col">
<div className="bg-muted-foreground/40 group-data-[state=active]:bg-secondary-foreground flex size-8 items-center justify-center rounded-md p-1.5">
<feature.icon className="stroke-background" />
</div>
<h2 className="group-data-[state=active]:text-primary font-inter text-foreground mt-2 text-lg font-bold">
{feature.title}
</h2>
<p className="text-muted-foreground mt-1 text-sm">
{feature.description}
</p>
</div>
</TabsTrigger>
))}
</TabsList>
</div>
</Tabs>
</div>
{/* Background decoration */}
<div className="absolute inset-0 aspect-square [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
<div className="">
{/* will-change-transform improves performance on scroll on safari because of the high blur */}
<div className="bg-primary-gradient/11 absolute inset-x-[0%] bottom-0 left-0 h-[500px] rounded-full blur-[100px] will-change-transform md:h-[950px]" />
<div className="bg-secondary-gradient/9 absolute inset-x-[30%] bottom-0 right-0 h-[500px] rounded-full blur-[100px] will-change-transform md:h-[950px]" />
</div>
</section>
);
}

View file

@ -0,0 +1,61 @@
import { Button } from "../ui/button";
const JoinUs = () => {
const jobCategories = [
{
name: "Engineering",
jobs: [
{ title: "iOS Developer", location: "Remote" },
{ title: "Backend Engineer", location: "Remote" },
{ title: "Frontend Engineer", location: "Remote" },
],
},
{
name: "Design",
jobs: [
{ title: "Senior Designer", location: "Remote" },
{ title: "Staff Designer", location: "Remote" },
{ title: "Designer", location: "Remote" },
],
},
];
return (
<section className="lg:py-15 container max-w-5xl py-10 md:py-12">
<div className="border-t pt-5">
<div className="max-w-2xl">
<h2 className="text-4xl font-semibold tracking-tight md:text-4xl">
Join us
</h2>
<p className="text-muted-foreground font-mona lg:pb-15 mt-4 max-w-2xl pb-10 text-2xl md:pb-12 md:text-3xl">
We work together from all over the world.
</p>
{jobCategories.map((category, categoryIndex) => (
<div key={categoryIndex}>
<h3 className="border-foreground border-b py-6 text-lg font-semibold">
{category.name}
</h3>
<div className="">
{category.jobs.map((job, jobIndex) => (
<div
key={`${categoryIndex}-${jobIndex}`}
className="flex items-center justify-between gap-10 border-b py-3 md:gap-16 lg:gap-28"
>
<h4 className="flex-1 font-medium">{job.title}</h4>
<p className="text-muted-foreground">{job.location}</p>
<Button variant="outline" asChild>
<a href="#">View listing</a>
</Button>
</div>
))}
</div>
</div>
))}
</div>
</div>
</section>
);
};
export default JoinUs;

View file

@ -0,0 +1,110 @@
import { PlusSigns } from "@/components/icons/plus-signs";
import Testimonials from "@/components/sections/testimonials";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
const FAQ_ITEMS = [
{
category: "SUPPORT",
items: [
{
question: "Is there a free version?",
answer:
"Yes! We offer a generous free plan with just enough features except that one feature you really want! Our strategy is to get your credit card details on file then steadily double our prices against inflation rates.",
},
{
question: "How do I update my account without breaking my laptop?",
answer:
"Our platform is designed with safety in mind. You can update your account settings through our intuitive dashboard without any risk to your hardware. We have multiple safeguards in place to prevent any system conflicts.",
},
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
{
category: "YOUR ACCOUNT",
items: [
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
{
category: "OTHER QUESTIONS",
items: [
{
question: "Is support free, or do I need to Google everything?",
answer:
"We provide comprehensive support at no additional cost. Our dedicated support team is available 24/7 to help you with any questions or issues you might encounter. No need to rely on Google - we're here to help!",
},
{
question: "Are you going to be subsumed by AI?",
answer:
"While we embrace AI technology to enhance our services, we maintain a strong human element in our operations. Our team works alongside AI to provide the best possible service while ensuring human oversight and decision-making remain central to our operations.",
},
],
},
];
export default function LatestPosts() {
return (
<>
<section className="relative py-16 md:py-28 lg:py-32">
<div className="container">
<div className="mx-auto mt-8 max-w-2xl space-y-12 md:mt-12 lg:mt-20">
{FAQ_ITEMS.map((category) => (
<Card key={category.category} className="border-hidden">
<CardHeader className="pb-0">
<h3 className="text-accent-foreground border-b pb-4 font-mono text-sm font-medium uppercase tracking-widest">
{category.category}
</h3>
</CardHeader>
<CardContent>
<Accordion type="single" collapsible className="w-full">
{category.items.map((item, i) => (
<AccordionItem
key={i}
value={`${category.category}-${i}`}
className="border-muted border-b last:border-0"
>
<AccordionTrigger className="text-base font-medium hover:no-underline">
{item.question}
</AccordionTrigger>
<AccordionContent className="text-muted-foreground text-base font-medium">
{item.answer}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</CardContent>
</Card>
))}
</div>
</div>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
</section>
</>
);
}

View file

@ -0,0 +1,86 @@
import { ChevronRight, Check } from "lucide-react";
import { PlusSigns } from "../icons/plus-signs";
import { Button } from "@/components/ui/button";
export default function LaunchPricing() {
return (
<section
id="launch-today"
className="text-background py-16 md:container md:max-w-5xl md:py-28 lg:py-32"
>
<div className="from-primary-900 to-primary/90 divide-background/20 bg-linear-to-r container relative isolate grid items-center overflow-hidden py-8 max-lg:gap-10 max-md:gap-6 md:rounded-3xl lg:grid-cols-2 lg:divide-x lg:px-8">
<div className="absolute inset-0 -z-10 [mask-image:linear-gradient(to_left,black_50%,transparent_100%)]">
<PlusSigns className="text-background/[0.05] h-full w-full" />
</div>
<div className="lg:py-16 lg:pr-20">
<h2 className="text-3xl font-semibold tracking-tight md:text-4xl lg:text-5xl">
Launch today
</h2>
<p className="text-background/70 mt-3 text-sm font-medium">
In the past, new financial companies had to rely on expensive
middleware that linked them to outdated sponsor bank systems,
restricting their potential. Our API solves this today.
</p>
<div className="mt-8 flex flex-wrap gap-4 max-md:hidden">
<Button size="lg" variant="secondary" className="group" asChild>
<a href="/signup">
Start for free
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
<Button size="lg" className="bg-secondary-foreground group" asChild>
<a href="/contact">
Get a demo
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
</div>
</div>
<div className="space-y-6 lg:py-10 lg:pl-20">
<div>
<h3 className="text-background text-3xl font-semibold md:text-4xl lg:text-5xl">
$29.99
</h3>
<p className="text-background/70 mt-1 text-xl font-medium">
per user per month
</p>
</div>
<ul className="text-background/70 space-y-3 text-sm">
<li className="flex items-center gap-2">
<Check className="size-4" />
All free plan features and...
</li>
<li className="flex items-center gap-2">
<Check className="size-4" />
Mainline AI
</li>
<li className="flex items-center gap-2">
<Check className="size-4" />
Unlimited teams
</li>
</ul>
<div className="mt-10 flex flex-wrap gap-4 md:hidden">
<Button size="lg" variant="secondary" className="group w-full">
<a href="/signup" className="flex items-center gap-2">
Start building for free
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
<Button
size="lg"
className="bg-secondary-foreground border-background/20 group w-full border"
>
<a href="/contact" className="flex items-center gap-2">
Get a demo
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,75 @@
import { Card, CardContent, CardHeader } from "../ui/card";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import { FcGoogle } from "react-icons/fc";
import { Checkbox } from "../ui/checkbox";
const LoginSection = () => {
return (
<section className="bg-sand-100 py-16 md:py-28 lg:py-32">
<div className="container">
<div className="flex flex-col gap-4">
<Card className="mx-auto w-full max-w-sm">
<CardHeader className="flex flex-col items-center space-y-0">
<img
src="/images/layout/logo.svg"
alt="logo"
width={94}
height={18}
className="mb-7 dark:invert"
/>
<p className="mb-2 text-2xl font-bold">Welcome back</p>
<p className="text-muted-foreground">
Please enter your details.
</p>
</CardHeader>
<CardContent>
<div className="grid gap-4">
<Input type="email" placeholder="Enter your email" required />
<div>
<Input
type="password"
placeholder="Enter your password"
required
/>
</div>
<div className="flex justify-between">
<div className="flex items-center space-x-2">
<Checkbox
id="remember"
className="border-muted-foreground"
/>
<label
htmlFor="remember"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Remember me
</label>
</div>
<a href="#" className="text-primary text-sm font-medium">
Forgot password
</a>
</div>
<Button type="submit" className="mt-2 w-full">
Create an account
</Button>
<Button variant="outline" className="w-full">
<FcGoogle className="mr-2 size-5" />
Sign up with Google
</Button>
</div>
<div className="text-muted-foreground mx-auto mt-8 flex justify-center gap-1 text-sm">
<p>Don&apos;t have an account?</p>
<a href="/signup" className="text-primary font-medium">
Sign up
</a>
</div>
</CardContent>
</Card>
</div>
</div>
</section>
);
};
export default LoginSection;

View file

@ -0,0 +1,111 @@
"use client";
const ITEMS = [
{
name: "Mercury",
src: "/images/logos/mercury.svg",
width: 143,
height: 26,
href: "https://mercury.com",
},
{
name: "Watershed",
src: "/images/logos/watershed.svg",
width: 154,
height: 31,
href: "https://watershed.com",
},
{
name: "Retool",
src: "/images/logos/retool.svg",
width: 113,
height: 22,
href: "https://retool.com",
},
{
name: "Descript",
src: "/images/logos/descript.svg",
width: 112,
height: 27,
href: "https://descript.com",
},
{
name: "Perplexity",
src: "/images/logos/perplexity.svg",
width: 141,
height: 32,
href: "https://perplexity.ai",
},
{
name: "Monzo",
src: "/images/logos/monzo.svg",
width: 104,
height: 18,
href: "https://monzo.com",
},
{
name: "Ramp",
src: "/images/logos/ramp.svg",
width: 105,
height: 28,
href: "https://ramp.com",
},
{
name: "Raycast",
src: "/images/logos/raycast.svg",
width: 128,
height: 33,
href: "https://raycast.com",
},
{
name: "Arc",
src: "/images/logos/arc.svg",
width: 90,
height: 28,
href: "https://arc.com",
},
];
export default function Logos() {
return (
<section className="overflow-hidden">
<h2 className="text-muted-foreground text-center text-2xl">
From next-gen startups to established enterprises.
</h2>
<div className="relative mt-10 flex w-full">
<div className="from-background bg-linear-to-r absolute left-0 z-20 h-full w-10 to-transparent" />
<div className="from-background bg-linear-to-l absolute right-0 z-20 h-full w-10 to-transparent" />
{/* First marquee group */}
<div className="animate-marquee flex shrink-0 items-center gap-12">
{ITEMS.map((logo, index) => (
<a href={logo.href} target="_blank" key={index} className="p-6">
<img
src={logo.src}
alt={logo.name}
width={logo.width}
height={logo.height}
className="object-contain transition-opacity hover:opacity-70"
/>
</a>
))}
</div>
{/* Second marquee group */}
<div className="animate-marquee flex shrink-0 items-center gap-12">
{ITEMS.map((logo, index) => (
<a href={logo.href} target="_blank" key={index} className="p-6">
<img
src={logo.src}
alt={logo.name}
width={logo.width}
height={logo.height}
className="object-contain transition-opacity hover:opacity-70"
/>
</a>
))}
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,133 @@
import React, { useState, useEffect } from "react";
import { ChevronRight, Menu, X, Home } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
} from "@/components/ui/navigation-menu";
import { cn } from "@/lib/utils";
import { ThemeToggle } from "../elements/theme-toggle";
const Navbar = ({ currentPage }: { currentPage: string }) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const pathname = currentPage;
useEffect(() => {
if (isMenuOpen) {
document.body.classList.add("overflow-hidden");
} else {
document.body.classList.remove("overflow-hidden");
}
// Cleanup on unmount
return () => {
document.body.classList.remove("overflow-hidden");
};
}, [isMenuOpen]);
const ITEMS = [
{ label: "About", href: "/about" },
{ label: "Latest", href: "/latest" },
];
return (
<header className={"relative z-50"}>
<div className="container max-w-5xl lg:pt-10">
<div className="flex items-center justify-between py-3">
{/* Logo */}
<a href="/" className="flex items-center gap-2">
<Home className="h-8 w-8" />
</a>
{/* Desktop Navigation */}
<NavigationMenu className="hidden items-center gap-8 lg:flex">
<NavigationMenuList>
{ITEMS.map((link) =>
<NavigationMenuItem key={link.label}>
<a
href={link.href}
className={cn(
"hover:text-accent-foreground p-2 lg:text-base",
pathname === link.href && "text-accent-foreground",
)}
>
{link.label}
</a>
</NavigationMenuItem>
)}
</NavigationMenuList>
</NavigationMenu>
{/* Auth Buttons */}
<div className="flex items-center gap-2.5">
<div
className={`transition-opacity duration-300 ${isMenuOpen ? "max-lg:pointer-events-none max-lg:opacity-0" : "opacity-100"}`}
>
<ThemeToggle className="dark:bg-foreground dark:text-background" />
</div>
{/* Hamburger Menu Button (Mobile Only) */}
<button
className="relative flex size-8 lg:hidden"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
<span className="sr-only">Open main menu</span>
<div className="absolute left-1/2 top-1/2 block w-[18px] -translate-x-1/2 -translate-y-1/2">
<span
aria-hidden="true"
className={`absolute block h-0.5 w-full rounded-full bg-current transition duration-500 ease-in-out ${isMenuOpen ? "rotate-45" : "-translate-y-1.5"}`}
></span>
<span
aria-hidden="true"
className={`absolute block h-0.5 w-full rounded-full bg-current transition duration-500 ease-in-out ${isMenuOpen ? "opacity-0" : ""}`}
></span>
<span
aria-hidden="true"
className={`absolute block h-0.5 w-full rounded-full bg-current transition duration-500 ease-in-out ${isMenuOpen ? "-rotate-45" : "translate-y-1.5"}`}
></span>
</div>
</button>
</div>
</div>
</div>
{/* Mobile Menu Overlay */}
<div
className={cn(
"bg-background container absolute inset-0 top-full flex h-[calc(100vh-64px)] flex-col transition-all duration-300 ease-in-out lg:hidden",
isMenuOpen
? "visible translate-x-0 opacity-100"
: "invisible translate-x-full opacity-0",
)}
>
<nav className="mt-3 flex flex-1 flex-col gap-6">
{ITEMS.map((link) =>
<a
key={link.label}
href={link.href}
className={cn(
"text-lg tracking-[-0.36px]",
pathname === link.href && "text-muted-foreground",
)}
onClick={() => setIsMenuOpen(false)}
>
{link.label}
</a>
)
}
</nav>
</div>
</header>
);
};
export default Navbar;

View file

@ -0,0 +1,64 @@
import React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Mail } from "lucide-react";
const NewsletterSignup = () => {
const [email, setEmail] = React.useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// This would be connected to your newsletter service
console.log("Submitting email:", email);
alert("Thanks for subscribing!");
setEmail("");
};
return (
<section className="bg-muted/50 border-t py-16 md:py-24">
<div className="container max-w-5xl">
<div className="bg-card flex flex-col items-center justify-between gap-8 rounded-2xl border p-8 shadow-sm md:p-12 lg:flex-row">
<div className="max-w-md text-center lg:text-left">
<div className="mb-3 flex justify-center lg:justify-start">
<div className="bg-primary/10 text-primary flex h-10 w-10 items-center justify-center rounded-full">
<Mail className="h-5 w-5" />
</div>
</div>
<h2 className="mb-3 text-2xl font-bold md:text-3xl">
Subscribe to our newsletter
</h2>
<p className="text-muted-foreground mb-0 text-base">
Stay updated with the latest articles, tutorials, and insights
from our team. We'll never spam your inbox.
</p>
</div>
<div className="w-full max-w-md">
<form
onSubmit={handleSubmit}
className="flex flex-col gap-2 sm:flex-row"
>
<Input
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="h-12"
/>
<Button type="submit" className="h-12">
Subscribe
</Button>
</form>
<p className="text-muted-foreground mt-2 text-xs">
By subscribing, you agree to our Privacy Policy and consent to
receive updates from our company.
</p>
</div>
</div>
</div>
</section>
);
};
export default NewsletterSignup;

View file

@ -0,0 +1,187 @@
import { Check, ChevronRight } from "lucide-react";
import { PlusSigns } from "../icons/plus-signs";
import { Button } from "../ui/button";
import { cn } from "@/lib/utils";
type PricingTier = {
name: string;
price: string;
description: string;
features: string[];
cta: {
text: string;
href: string;
};
};
const ITEMS: PricingTier[] = [
{
name: "STARTER",
price: "$0",
description: "Free for everyone",
features: ["Unlimited members", "250 transactions", "No support"],
cta: {
text: "Start for free",
href: "/signup",
},
},
{
name: "BASIC",
price: "$29.99",
description: "per user per month",
features: [
"All free plan features and...",
"Mainline AI",
"Unlimited teams",
],
cta: {
text: "7 days free",
href: "/signup",
},
},
{
name: "ENTERPRISE",
price: "$ENT",
description: "Custom pricing",
features: [
"All basic plan features and...",
"Advanced security controls",
"Migration support",
],
cta: {
text: "Book a demo",
href: "/contact",
},
},
];
const PricingCards = () => {
return (
<section className="relative py-16 md:py-28 lg:py-32">
<div className="container">
<h1 className="text-center text-4xl font-semibold tracking-tight md:text-5xl lg:text-6xl">
Pricing
</h1>
<div className="mx-auto mt-4 max-w-[45rem] space-y-2 text-center">
<p className="text-muted-foreground text-2xl md:text-3xl">
Use Charter for free with your whole team. Upgrade to enable
enhanced features.
</p>
</div>
<div className="relative mt-8 md:mt-12 lg:mt-20">
{/* Background and layout wrapper */}
<div className="from-primary-900 to-primary/90 bg-linear-to-r absolute inset-0 isolate hidden rounded-3xl md:block">
<div className="absolute inset-0 -z-10 [mask-image:linear-gradient(to_left,black_50%,transparent_100%)]">
<PlusSigns className="h-full w-full text-white/[0.05]" />
</div>
</div>
<div className="md:divide-background/20 relative space-y-6 md:grid md:grid-cols-3 md:space-y-0 md:divide-x md:p-6 lg:p-8">
{ITEMS.map((tier, index) => (
<PricingCard
key={tier.name}
tier={tier}
isHighlighted={index === 1}
/>
))}
</div>
</div>
<div className="absolute -inset-40 z-[-1] [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_75%)]">
<PlusSigns className="text-foreground/[0.05] h-full w-full" />
</div>
</div>
</section>
);
};
export default PricingCards;
function PricingCard({
tier,
isHighlighted,
}: {
tier: PricingTier;
isHighlighted: boolean;
}) {
const styles = {
card: cn(
"flex flex-col gap-6 rounded-xl p-6 sm:rounded-2xl md:rounded-none lg:p-8",
// Mobile styles
isHighlighted
? "max-md:from-primary-900 max-md:to-primary/90 max-md:bg-linear-to-r"
: "bg-background max-md:border",
// Desktop styles
"md:bg-transparent",
),
title: cn(
"font-mono text-sm tracking-widest",
// Mobile styles
isHighlighted ? "text-background/70" : "text-foreground/70",
// Desktop styles
"md:text-background/70",
),
price: cn(
"text-5xl font-semibold tracking-tight",
// Mobile styles
isHighlighted ? "text-background" : "text-foreground",
// Desktop styles
"md:text-background",
),
description: cn(
"mt-2 text-xl font-medium",
// Mobile styles
isHighlighted ? "text-background/70" : "text-foreground/70",
// Desktop styles
"md:text-background/70",
),
features: cn(
"space-y-3 text-sm",
// Mobile styles
isHighlighted ? "text-background/70" : "text-foreground/70",
// Desktop styles
"md:text-background/70",
),
button: cn(
"group border-foreground/20 relative w-full",
// inset shadow
"after:from-border after:via-border after:absolute after:inset-0 after:bg-linear-to-t after:to-transparent after:content-[''] after:group-hover:opacity-100",
// Desktop styles
"md:border-background/40 md:text-background md:bg-transparent",
isHighlighted &&
"md:bg-background md:text-primary hover:md:bg-background/90",
),
};
return (
<div className={styles.card}>
<h3 className={styles.title}>{tier.name}</h3>
<div>
<p className={styles.price}>{tier.price}</p>
<p className={styles.description}>{tier.description}</p>
</div>
<ul className={styles.features}>
{tier.features.map((feature) => (
<li key={feature} className="flex items-center gap-2">
<Check className="size-4 shrink-0" />
<span>{feature}</span>
</li>
))}
</ul>
<div className="flex flex-1 items-end">
<Button
asChild
variant={isHighlighted ? "secondary" : "outline"}
size="lg"
className={styles.button}
>
<a href={tier.cta.href}>
{tier.cta.text}
<ChevronRight className="ml-1 size-4 transition-transform group-hover:translate-x-0.5" />
</a>
</Button>
</div>
</div>
);
}

View file

@ -0,0 +1,304 @@
"use client";
import { useState } from "react";
import { Check, ChevronsUpDown } from "lucide-react";
import { Button } from "../ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../ui/collapsible";
import { cn } from "@/lib/utils";
type PlanType = "free" | "startup" | "enterprise";
interface Plan {
name: string;
type: PlanType;
button: {
text: string;
variant: "outline";
href: string;
};
features: {
[Category in "usage" | "features" | "support"]: {
name: string;
value: string | boolean;
}[];
};
}
const plans: Plan[] = [
{
name: "Free",
type: "free",
button: {
text: "Get started",
variant: "outline",
href: "/signup",
},
features: {
usage: [
{ name: "Members", value: "Unlimited" },
{ name: "Transactions", value: "250" },
{ name: "Teams", value: "2" },
],
features: [
{ name: "Reporting", value: true },
{ name: "Analytics", value: true },
{ name: "Import and export", value: true },
{ name: "Integrations", value: true },
{ name: "Mainline AI", value: false },
{ name: "Admin roles", value: false },
{ name: "Audit log", value: false },
],
support: [
{ name: "Priority Support", value: true },
{ name: "Account Manager", value: false },
{ name: "Uptime SLA", value: false },
],
},
},
{
name: "Basic",
type: "startup",
button: {
text: "Get started",
variant: "outline",
href: "/signup",
},
features: {
usage: [
{ name: "Members", value: "Unlimited" },
{ name: "Transactions", value: "Unlimited" },
{ name: "Teams", value: "Unlimited" },
],
features: [
{ name: "Reporting", value: true },
{ name: "Analytics", value: true },
{ name: "Import and export", value: true },
{ name: "Integrations", value: true },
{ name: "Mainline AI", value: true },
{ name: "Admin roles", value: false },
{ name: "Audit log", value: false },
],
support: [
{ name: "Priority Support", value: true },
{ name: "Account Manager", value: false },
{ name: "Uptime SLA", value: false },
],
},
},
{
name: "Ent",
type: "enterprise",
button: {
text: "Get a demo",
variant: "outline",
href: "/contact",
},
features: {
usage: [
{ name: "Members", value: "Unlimited" },
{ name: "Transactions", value: "Unlimited" },
{ name: "Teams", value: "Unlimited" },
],
features: [
{ name: "Reporting", value: true },
{ name: "Analytics", value: true },
{ name: "Import and export", value: true },
{ name: "Integrations", value: true },
{ name: "Mainline AI", value: true },
{ name: "Admin roles", value: false },
{ name: "Audit log", value: false },
],
support: [
{ name: "Priority Support", value: true },
{ name: "Account Manager", value: true },
{ name: "Uptime SLA", value: true },
],
},
},
];
export default function PricingTable() {
const [selectedPlan, setSelectedPlan] = useState(1);
return (
<section className="py-28 lg:py-32">
<div className="container font-medium">
<MobilePricingTable
selectedPlan={selectedPlan}
onPlanChange={setSelectedPlan}
/>
<DesktopPricingTable />
</div>
</section>
);
}
const MobilePricingTable = ({
selectedPlan,
onPlanChange,
}: {
selectedPlan: number;
onPlanChange: (index: number) => void;
}) => {
const [isOpen, setIsOpen] = useState(false);
const plan = plans[selectedPlan];
return (
<div className="md:hidden">
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="flex items-center justify-between border-b py-4">
<CollapsibleTrigger className="flex items-center gap-2">
<h3 className="text-2xl font-semibold">{plan.name}</h3>
<ChevronsUpDown
className={`size-5 transition-transform ${isOpen ? "rotate-180" : ""}`}
/>
</CollapsibleTrigger>
<Button variant={plan.button.variant} className="w-fit">
{plan.button.text}
</Button>
</div>
<CollapsibleContent className="flex flex-col space-y-2 p-2">
{plans.map(
(p, index) =>
index !== selectedPlan && (
<Button
size="lg"
variant="secondary"
key={index}
onClick={() => {
onPlanChange(index);
setIsOpen(false);
}}
>
{p.name}
</Button>
),
)}
</CollapsibleContent>
</Collapsible>
{/* Features List */}
<div className="mt-8">
{Object.entries(plan.features).map(
([category, features], sectionIndex) => (
<div key={sectionIndex} className="mb-8 space-y-2">
<h3 className="mb-4 text-lg font-semibold capitalize">
{category}
</h3>
{features.map((feature, featureIndex) => (
<div
key={featureIndex}
className="grid grid-cols-2 items-center gap-8"
>
<span className="border-b py-2">{feature.name}</span>
<div className="flex items-center gap-1 border-b py-2">
{typeof feature.value === "boolean" ? (
feature.value ? (
<Check className="size-5" />
) : (
<span className="size-5" />
)
) : (
<div className="flex items-center gap-1">
<Check className="size-5" />
<span>{feature.value}</span>
</div>
)}
</div>
</div>
))}
</div>
),
)}
</div>
</div>
);
};
const DesktopPricingTable = () => {
return (
<div className="hidden md:grid md:grid-cols-4">
<FeaturesColumn />
{plans.map((plan, index) => (
<PricingColumn
key={plan.name}
plan={plan}
isHighlighted={index === 1}
/>
))}
</div>
);
};
const FeaturesColumn = () => (
<div>
<div className="h-[140px]" /> {/* Spacer for plan header alignment */}
{Object.entries(plans[0].features).map(([category, features], index) => (
<div key={index}>
<h3 className="flex h-20 items-center text-lg font-semibold capitalize">
{category}
</h3>
{features.map((feature, featureIndex) => (
<div key={featureIndex} className="py-4">
{feature.name}
</div>
))}
</div>
))}
</div>
);
const PricingColumn = ({
plan,
isHighlighted,
}: {
plan: Plan;
isHighlighted: boolean;
}) => {
const columnClass = cn("px-6", isHighlighted && "bg-card border rounded-xl");
return (
<div className={columnClass}>
{/* Plan Header */}
<div className="py-8">
<h3 className="mb-3 text-2xl font-semibold">{plan.name}</h3>
<Button variant={plan.button.variant} asChild>
<a href={plan.button.href}>{plan.button.text}</a>
</Button>
</div>
{/* Features */}
{Object.entries(plan.features).map(([, features], sectionIndex) => (
<div key={sectionIndex}>
<div className="flex h-20 items-center"></div> {/* space */}
{features.map((feature, featureIndex) => (
<div
key={featureIndex}
className="flex items-center gap-1 border-b py-4"
>
{typeof feature.value === "boolean" ? (
feature.value ? (
<Check className="size-5" />
) : (
<span className="size-5" />
)
) : (
<div className="flex items-center gap-1">
<Check className="size-4" />
<span>{feature.value}</span>
</div>
)}
</div>
))}
</div>
))}
</div>
);
};

View file

@ -0,0 +1,61 @@
import { Card, CardContent, CardHeader } from "../ui/card";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import { FcGoogle } from "react-icons/fc";
const SignupSection = () => {
return (
<section className="bg-sand-100 py-16 md:py-28 lg:py-32">
<div className="container">
<div className="flex flex-col gap-4">
<Card className="mx-auto w-full max-w-sm">
<CardHeader className="flex flex-col items-center space-y-0">
<img
src="/images/layout/logo.svg"
alt="logo"
width={94}
height={18}
className="mb-7 dark:invert"
/>
<p className="mb-2 text-2xl font-bold">Start your free trial</p>
<p className="text-muted-foreground">
Sign up in less than 2 minutes.
</p>
</CardHeader>
<CardContent>
<div className="grid gap-4">
<Input type="text" placeholder="Enter your name" required />
<Input type="email" placeholder="Enter your email" required />
<div>
<Input
type="password"
placeholder="Enter your password"
required
/>
<p className="text-muted-foreground mt-1 text-sm">
Must be at least 8 characters.
</p>
</div>
<Button type="submit" className="mt-2 w-full">
Create an account
</Button>
<Button variant="outline" className="w-full">
<FcGoogle className="mr-2 size-5" />
Sign up with Google
</Button>
</div>
<div className="text-muted-foreground mx-auto mt-8 flex justify-center gap-1 text-sm">
<p>Already have an account?</p>
<a href="/login" className="text-primary font-medium">
Log in
</a>
</div>
</CardContent>
</Card>
</div>
</div>
</section>
);
};
export default SignupSection;

View file

@ -0,0 +1,148 @@
import { useEffect, useState } from "react";
import { PlusSigns } from "../icons/plus-signs";
import {
Carousel,
type CarouselApi,
CarouselContent,
CarouselItem,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
const TESTIMONIALS = [
{
id: 1,
quote:
"Charter's platform delivers unmatched speed, a flexible account framework, and an API-first design. Their deep understanding of the ecosystem has been crucial for scaling our financial operations effectively.",
author: "Henry Francis",
role: "Founder, Ramp",
logo: "/images/logos/ramp.svg",
},
{
id: 2,
quote:
"We have a deep understanding of the ecosystem, and Charter delivers unmatched speed, a flexible account framework, and an API-first design. These features are crucial for creating revenue that grows our office plants.",
author: "David Chen",
role: "Head of Engineering, Notion",
logo: "/images/logos/notion.svg",
},
{
id: 3,
quote:
"Charter's platform has exceeded our expectations with its seamless integration process and exceptional support team. Their powerful tools and robust infrastructure have been instrumental in our scaling journey.",
author: "Sarah Williams",
role: "CTO, Mercury",
logo: "/images/logos/mercury.svg",
},
{
id: 4,
quote:
"Charter has revolutionized how we manage our financial infrastructure effectively. Their API-first approach and robust platform capabilities have given us the flexibility to build custom solutions at scale seamlessly and efficiently.",
author: "Michael Ross",
role: "CEO, Raycast",
logo: "/images/logos/raycast.svg",
},
{
id: 5,
quote:
"The combination of Charter's flexible platform architecture and exceptional support team has been transformative for our operations. Their speed, reliability, and scalability are unmatched in the industry.",
author: "Emily Chen",
role: "Product Lead, Asana",
logo: "/images/logos/asana.svg",
},
];
export default function Testimonials() {
const [api, setApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
useEffect(() => {
if (!api) {
return;
}
api.on("select", () => {
setCurrent(api.selectedScrollSnap());
});
}, [api]);
return (
<section
className="relative py-16 md:py-28 lg:py-32"
aria-label="Customer Testimonials"
>
<div className="container max-w-4xl">
<Carousel
opts={{
align: "center",
loop: true,
}}
setApi={setApi}
>
<CarouselContent>
{TESTIMONIALS.map((testimonial) => (
<CarouselItem
key={testimonial.id}
className="flex cursor-grab flex-col gap-6 lg:gap-8"
>
<blockquote className="pointer-events-none select-none text-balance font-sans text-2xl font-semibold leading-tight tracking-tight md:text-3xl lg:text-4xl">
{testimonial.quote}
</blockquote>
<div className="flex items-center gap-4">
{testimonial.logo && (
<div className="relative h-8 w-24">
<img
src={testimonial.logo}
alt={`${testimonial.author}'s company logo`}
className="size-full object-contain dark:invert"
aria-hidden="true"
/>
</div>
)}
<div className="bg-border h-8 w-[1px]" aria-hidden="true" />
<div>
<cite className="font-semibold not-italic">
{testimonial.author}
</cite>
<p className="text-muted-foreground text-sm font-medium">
{testimonial.role}
</p>
</div>
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div
className="mt-10 flex gap-2 lg:mt-16"
role="tablist"
aria-label="Testimonials navigation"
>
{TESTIMONIALS.map((_, index) => (
<button
key={index}
className={cn(
"size-4 cursor-pointer rounded-full transition-colors duration-300",
index === current
? "bg-muted-foreground"
: "bg-muted-foreground/20 hover:bg-muted-foreground/50",
)}
onClick={() => api?.scrollTo(index)}
aria-label={`Go to testimonial ${index + 1}`}
aria-selected={index === current}
role="tab"
/>
))}
</div>
</div>
<div className="absolute inset-x-0 top-16 isolate z-[-1] h-[300px] md:top-28 lg:top-32">
<div className="from-background via-background/40 absolute inset-x-0 bottom-0 z-10 h-40 bg-gradient-to-t to-transparent" />
<div className="from-background via-background/40 absolute inset-x-0 top-0 z-10 h-40 bg-gradient-to-b to-transparent" />
<PlusSigns className="text-foreground/[0.05] size-full" />
</div>
</section>
);
}

View file

@ -0,0 +1,157 @@
import { PlusSigns } from "../icons/plus-signs";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
const items = [
{
title: (
<>
Unparalleled
<br />
VAR flexibility
</>
),
description: [
"VAR is often known for its lengthy processing times. At Charter, we focus on achieving the fastest VAR transfers—often completed within hours.",
"Unlike traditional banks and middleware, we connect directly with the Federal Reserve to ensure the fastest, most transparent transfers possible.",
],
image: {
src: "/images/homepage/why-charter/1.webp",
alt: "Code snippet",
type: "fill",
},
className:
"flex flex-col pl-6 py-6 overflow-hidden md:col-span-3 md:flex-row gap-6 md:gap-12",
headerClassName: "flex-2 p-0",
contentClassName:
"relative h-[320px] w-full p-0 self-center overflow-hidden rounded-l-xl border md:flex-1",
},
{
title: "Unparalleled VAR flexibility",
description: [
"VAR has a reputation for taking too long. At Charter, we optimise for the fastest VAR transfers possible — often in a matter of hours.",
"Unlike legacy banks and middleware providers, we have a direct connection to the Federal Reserve to facilitate the quickest transfers.",
],
image: {
src: "/images/homepage/why-charter/2.svg",
alt: "VAR Process Flow",
width: 283,
height: 45,
},
className: "md:col-span-2 flex flex-col justify-center",
contentClassName:
"flex items-center justify-center p-6 max-md:mt-4 max-md:mb-8",
imagePosition: "content",
},
{
title: "Unparalleled VAR flexibility",
description: [
"VAR has a reputation for taking too long. At Charter, we optimise for the fastest VAR transfers possible — often in a matter of hours.",
],
image: {
src: "/images/homepage/why-charter/3.svg",
alt: "VAR Process Diagram",
width: 283,
height: 45,
},
className: "md:col-span-2",
headerClassName: "h-full",
imagePosition: "header",
},
{
title: "Unparalleled VAR flexibility",
description: [
"Unlike traditional banks and middleware, we connect directly with the Federal Reserve to ensure the fastest, most transparent transfers possible.",
],
image: {
src: "/images/homepage/code-snippet.webp",
alt: "Code snippet",
type: "fill",
},
className: "overflow-hidden md:col-span-3 ",
headerClassName: "",
contentClassName:
"relative h-[242px] mt-2 p-0 ml-8 w-full md:max-w-[400px] lg:max-w-[500px] overflow-hidden md:mx-auto shadow-xl rounded-t-2xl",
},
];
const WhyCharter = () => {
return (
<section
id="why-charter"
className="container relative py-16 md:py-28 lg:py-32"
>
<h3 className="mini-title">WHY CHARTER?</h3>
<div className="relative z-10 mt-8 grid grid-cols-1 gap-6 md:grid-cols-5">
{items.map((item, index) => (
<Card
key={index}
className={cn("col-span-1 shadow-xl", item.className)}
>
<CardHeader className={item.headerClassName}>
{item.imagePosition === "header" && (
<img
src={item.image.src}
alt={item.image.alt}
width={item.image.width}
height={item.image.height}
className="flex-1 self-center max-md:mb-8 max-md:mt-4 dark:[filter:brightness(0)_saturate(100%)_invert(100%)]"
/>
)}
<CardTitle className="text-3xl">{item.title}</CardTitle>
{item.description.map((desc, i) => (
<CardDescription
key={i}
className="mt-2 text-base font-medium leading-snug"
>
{desc}
</CardDescription>
))}
</CardHeader>
<CardContent className={item.contentClassName}>
{item.image.type === "fill" ? (
<img
src={item.image.src}
alt={item.image.alt}
className="size-full object-cover object-left-top"
/>
) : (
item.imagePosition === "content" && (
<img
src={item.image.src}
alt={item.image.alt}
width={item.image.width}
height={item.image.height}
className="self-center dark:[filter:brightness(0)_saturate(100%)_invert(100%)]"
/>
)
)}
</CardContent>
</Card>
))}
</div>
{/* Background decoration */}
<>
<div className="absolute inset-0 isolate will-change-transform">
<div className="bg-primary-gradient/28 absolute top-1/2 size-[700px] -translate-y-1/2 rounded-full blur-[300px]" />
<div className="bg-secondary-gradient/16 absolute right-0 top-1/2 size-[700px] -translate-y-1/2 -rotate-12 rounded-full blur-[300px]" />
<div className="bg-tertiary-gradient/6 absolute bottom-1/4 right-20 z-[1] h-[500px] w-[800px] -rotate-12 rounded-full blur-[100px] md:bottom-10" />
</div>
<div className="absolute -inset-x-20 top-0 [mask-image:radial-gradient(circle_at_center,black_0%,black_20%,transparent_85%)]">
<PlusSigns className="text-foreground/[0.075] h-full w-full" />
</div>
</>
</section>
);
};
export default WhyCharter;