This commit is contained in:
parent
fb5a3b8093
commit
58d8b661a8
1055 changed files with 116254 additions and 89 deletions
190
sites/poetry/src/components/sections/about-section.tsx
Normal file
190
sites/poetry/src/components/sections/about-section.tsx
Normal 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'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'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'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'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're focused on
|
||||
delivering for developers. If you'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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
80
sites/poetry/src/components/sections/ai-chatbot.tsx
Normal file
80
sites/poetry/src/components/sections/ai-chatbot.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
57
sites/poetry/src/components/sections/all-blogs.tsx
Normal file
57
sites/poetry/src/components/sections/all-blogs.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
88
sites/poetry/src/components/sections/blog-post.tsx
Normal file
88
sites/poetry/src/components/sections/blog-post.tsx
Normal 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 };
|
||||
135
sites/poetry/src/components/sections/blog-posts.tsx
Normal file
135
sites/poetry/src/components/sections/blog-posts.tsx
Normal 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 };
|
||||
100
sites/poetry/src/components/sections/code-security.tsx
Normal file
100
sites/poetry/src/components/sections/code-security.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
84
sites/poetry/src/components/sections/contact-page.tsx
Normal file
84
sites/poetry/src/components/sections/contact-page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
120
sites/poetry/src/components/sections/faq-page.tsx
Normal file
120
sites/poetry/src/components/sections/faq-page.tsx
Normal 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 />
|
||||
</>
|
||||
);
|
||||
}
|
||||
157
sites/poetry/src/components/sections/featured-posts.tsx
Normal file
157
sites/poetry/src/components/sections/featured-posts.tsx
Normal 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;
|
||||
86
sites/poetry/src/components/sections/footer.tsx
Normal file
86
sites/poetry/src/components/sections/footer.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
130
sites/poetry/src/components/sections/hero.tsx
Normal file
130
sites/poetry/src/components/sections/hero.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
61
sites/poetry/src/components/sections/join-us.tsx
Normal file
61
sites/poetry/src/components/sections/join-us.tsx
Normal 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;
|
||||
110
sites/poetry/src/components/sections/latest-posts.tsx
Normal file
110
sites/poetry/src/components/sections/latest-posts.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
86
sites/poetry/src/components/sections/launch-pricing.tsx
Normal file
86
sites/poetry/src/components/sections/launch-pricing.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
75
sites/poetry/src/components/sections/login-section.tsx
Normal file
75
sites/poetry/src/components/sections/login-section.tsx
Normal 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'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;
|
||||
111
sites/poetry/src/components/sections/logos.tsx
Normal file
111
sites/poetry/src/components/sections/logos.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
133
sites/poetry/src/components/sections/navbar.tsx
Normal file
133
sites/poetry/src/components/sections/navbar.tsx
Normal 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;
|
||||
64
sites/poetry/src/components/sections/newsletter-signup.tsx
Normal file
64
sites/poetry/src/components/sections/newsletter-signup.tsx
Normal 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;
|
||||
187
sites/poetry/src/components/sections/pricing-cards.tsx
Normal file
187
sites/poetry/src/components/sections/pricing-cards.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
304
sites/poetry/src/components/sections/pricing-table.tsx
Normal file
304
sites/poetry/src/components/sections/pricing-table.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
61
sites/poetry/src/components/sections/signup-section.tsx
Normal file
61
sites/poetry/src/components/sections/signup-section.tsx
Normal 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;
|
||||
148
sites/poetry/src/components/sections/testimonials.tsx
Normal file
148
sites/poetry/src/components/sections/testimonials.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
157
sites/poetry/src/components/sections/why-charter.tsx
Normal file
157
sites/poetry/src/components/sections/why-charter.tsx
Normal 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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue