forked from AkkomaGang/landing-site
270 lines
7.9 KiB
React
270 lines
7.9 KiB
React
|
import { useState } from 'react'
|
|||
|
import { RadioGroup } from '@headlessui/react'
|
|||
|
import clsx from 'clsx'
|
|||
|
|
|||
|
import { Button } from '@/components/Button'
|
|||
|
import { Container } from '@/components/Container'
|
|||
|
import { Logomark } from '@/components/Logo'
|
|||
|
|
|||
|
const plans = [
|
|||
|
{
|
|||
|
name: 'Starter',
|
|||
|
featured: false,
|
|||
|
price: { Monthly: '$0', Annually: '$0' },
|
|||
|
description:
|
|||
|
'You’re new to investing but want to do it right. Get started for free.',
|
|||
|
button: {
|
|||
|
label: 'Get started for free',
|
|||
|
href: '/register',
|
|||
|
},
|
|||
|
features: [
|
|||
|
'Commission-free trading',
|
|||
|
'Multi-layered encryption',
|
|||
|
'One tip every day',
|
|||
|
'Invest up to $1,500 each month',
|
|||
|
],
|
|||
|
logomarkClassName: 'fill-gray-300',
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'Investor',
|
|||
|
featured: false,
|
|||
|
price: { Monthly: '$7', Annually: '$70' },
|
|||
|
description:
|
|||
|
'You’ve been investing for a while. Invest more and grow your wealth faster.',
|
|||
|
button: {
|
|||
|
label: 'Subscribe',
|
|||
|
href: '/register',
|
|||
|
},
|
|||
|
features: [
|
|||
|
'Commission-free trading',
|
|||
|
'Multi-layered encryption',
|
|||
|
'One tip every hour',
|
|||
|
'Invest up to $15,000 each month',
|
|||
|
'Basic transaction anonymization',
|
|||
|
],
|
|||
|
logomarkClassName: 'fill-gray-500',
|
|||
|
},
|
|||
|
{
|
|||
|
name: 'VIP',
|
|||
|
featured: true,
|
|||
|
price: { Monthly: '$199', Annually: '$1,990' },
|
|||
|
description:
|
|||
|
'You’ve got a huge amount of assets but it’s not enough. To the moon.',
|
|||
|
button: {
|
|||
|
label: 'Subscribe',
|
|||
|
href: '/register',
|
|||
|
},
|
|||
|
features: [
|
|||
|
'Commission-free trading',
|
|||
|
'Multi-layered encryption',
|
|||
|
'Real-time tip notifications',
|
|||
|
'No investment limits',
|
|||
|
'Advanced transaction anonymization',
|
|||
|
'Automated tax-loss harvesting',
|
|||
|
],
|
|||
|
logomarkClassName: 'fill-cyan-500',
|
|||
|
},
|
|||
|
]
|
|||
|
|
|||
|
function CheckIcon(props) {
|
|||
|
return (
|
|||
|
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
|||
|
<path
|
|||
|
d="M9.307 12.248a.75.75 0 1 0-1.114 1.004l1.114-1.004ZM11 15.25l-.557.502a.75.75 0 0 0 1.15-.043L11 15.25Zm4.844-5.041a.75.75 0 0 0-1.188-.918l1.188.918Zm-7.651 3.043 2.25 2.5 1.114-1.004-2.25-2.5-1.114 1.004Zm3.4 2.457 4.25-5.5-1.187-.918-4.25 5.5 1.188.918Z"
|
|||
|
fill="currentColor"
|
|||
|
/>
|
|||
|
<circle
|
|||
|
cx="12"
|
|||
|
cy="12"
|
|||
|
r="8.25"
|
|||
|
fill="none"
|
|||
|
stroke="currentColor"
|
|||
|
strokeWidth="1.5"
|
|||
|
strokeLinecap="round"
|
|||
|
strokeLinejoin="round"
|
|||
|
/>
|
|||
|
</svg>
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
function Plan({
|
|||
|
name,
|
|||
|
price,
|
|||
|
description,
|
|||
|
button,
|
|||
|
features,
|
|||
|
featured = false,
|
|||
|
activePeriod,
|
|||
|
logomarkClassName,
|
|||
|
}) {
|
|||
|
return (
|
|||
|
<section
|
|||
|
className={clsx(
|
|||
|
'flex flex-col overflow-hidden rounded-3xl p-6 shadow-lg shadow-gray-900/5',
|
|||
|
featured ? 'order-first bg-gray-900 lg:order-none' : 'bg-white'
|
|||
|
)}
|
|||
|
>
|
|||
|
<h3
|
|||
|
className={clsx(
|
|||
|
'flex items-center text-sm font-semibold',
|
|||
|
featured ? 'text-white' : 'text-gray-900'
|
|||
|
)}
|
|||
|
>
|
|||
|
<Logomark className={clsx('h-6 w-6 flex-none', logomarkClassName)} />
|
|||
|
<span className="ml-4">{name}</span>
|
|||
|
</h3>
|
|||
|
<p
|
|||
|
className={clsx(
|
|||
|
'relative mt-5 flex text-3xl tracking-tight',
|
|||
|
featured ? 'text-white' : 'text-gray-900'
|
|||
|
)}
|
|||
|
>
|
|||
|
{price.Monthly === price.Annually ? (
|
|||
|
price.Monthly
|
|||
|
) : (
|
|||
|
<>
|
|||
|
<span
|
|||
|
aria-hidden={activePeriod === 'Annually'}
|
|||
|
className={clsx(
|
|||
|
'transition duration-300',
|
|||
|
activePeriod === 'Annually' &&
|
|||
|
'pointer-events-none translate-x-6 select-none opacity-0'
|
|||
|
)}
|
|||
|
>
|
|||
|
{price.Monthly}
|
|||
|
</span>
|
|||
|
<span
|
|||
|
aria-hidden={activePeriod === 'Monthly'}
|
|||
|
className={clsx(
|
|||
|
'absolute left-0 top-0 transition duration-300',
|
|||
|
activePeriod === 'Monthly' &&
|
|||
|
'pointer-events-none -translate-x-6 select-none opacity-0'
|
|||
|
)}
|
|||
|
>
|
|||
|
{price.Annually}
|
|||
|
</span>
|
|||
|
</>
|
|||
|
)}
|
|||
|
</p>
|
|||
|
<p
|
|||
|
className={clsx(
|
|||
|
'mt-3 text-sm',
|
|||
|
featured ? 'text-gray-300' : 'text-gray-700'
|
|||
|
)}
|
|||
|
>
|
|||
|
{description}
|
|||
|
</p>
|
|||
|
<div className="order-last mt-6">
|
|||
|
<ul
|
|||
|
role="list"
|
|||
|
className={clsx(
|
|||
|
'-my-2 divide-y text-sm',
|
|||
|
featured
|
|||
|
? 'divide-gray-800 text-gray-300'
|
|||
|
: 'divide-gray-200 text-gray-700'
|
|||
|
)}
|
|||
|
>
|
|||
|
{features.map((feature) => (
|
|||
|
<li key={feature} className="flex py-2">
|
|||
|
<CheckIcon
|
|||
|
className={clsx(
|
|||
|
'h-6 w-6 flex-none',
|
|||
|
featured ? 'text-white' : 'text-cyan-500'
|
|||
|
)}
|
|||
|
/>
|
|||
|
<span className="ml-4">{feature}</span>
|
|||
|
</li>
|
|||
|
))}
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<Button
|
|||
|
href={button.href}
|
|||
|
color={featured ? 'cyan' : 'gray'}
|
|||
|
className="mt-6"
|
|||
|
aria-label={`Get started with the ${name} plan for ${price}`}
|
|||
|
>
|
|||
|
{button.label}
|
|||
|
</Button>
|
|||
|
</section>
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
export function Pricing() {
|
|||
|
let [activePeriod, setActivePeriod] = useState('Monthly')
|
|||
|
|
|||
|
return (
|
|||
|
<section
|
|||
|
id="pricing"
|
|||
|
aria-labelledby="pricing-title"
|
|||
|
className="border-t border-gray-200 bg-gray-100 py-20 sm:py-32"
|
|||
|
>
|
|||
|
<Container>
|
|||
|
<div className="mx-auto max-w-2xl text-center">
|
|||
|
<h2
|
|||
|
id="pricing-title"
|
|||
|
className="text-3xl font-medium tracking-tight text-gray-900"
|
|||
|
>
|
|||
|
Flat pricing, no management fees.
|
|||
|
</h2>
|
|||
|
<p className="mt-2 text-lg text-gray-600">
|
|||
|
Whether you’re one person trying to get ahead or a big firm trying
|
|||
|
to take over the world, we’ve got a plan for you.
|
|||
|
</p>
|
|||
|
</div>
|
|||
|
|
|||
|
<div className="mt-8 flex justify-center">
|
|||
|
<div className="relative">
|
|||
|
<RadioGroup
|
|||
|
value={activePeriod}
|
|||
|
onChange={setActivePeriod}
|
|||
|
className="grid grid-cols-2"
|
|||
|
>
|
|||
|
{['Monthly', 'Annually'].map((period) => (
|
|||
|
<RadioGroup.Option
|
|||
|
key={period}
|
|||
|
value={period}
|
|||
|
className={clsx(
|
|||
|
'cursor-pointer border border-gray-300 py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm text-gray-700 outline-2 outline-offset-2 transition-colors hover:border-gray-400',
|
|||
|
period === 'Monthly'
|
|||
|
? 'rounded-l-lg'
|
|||
|
: '-ml-px rounded-r-lg'
|
|||
|
)}
|
|||
|
>
|
|||
|
{period}
|
|||
|
</RadioGroup.Option>
|
|||
|
))}
|
|||
|
</RadioGroup>
|
|||
|
<div
|
|||
|
aria-hidden="true"
|
|||
|
className={clsx(
|
|||
|
'pointer-events-none absolute inset-0 z-10 grid grid-cols-2 overflow-hidden rounded-lg bg-cyan-500 transition-all duration-300',
|
|||
|
activePeriod === 'Monthly'
|
|||
|
? '[clip-path:inset(0_50%_0_0)]'
|
|||
|
: '[clip-path:inset(0_0_0_calc(50%-1px))]'
|
|||
|
)}
|
|||
|
>
|
|||
|
{['Monthly', 'Annually'].map((period) => (
|
|||
|
<div
|
|||
|
key={period}
|
|||
|
className={clsx(
|
|||
|
'py-2 text-center text-sm font-semibold text-white [&:not(:focus-visible)]:focus:outline-none',
|
|||
|
period === 'Annually' && '-ml-px'
|
|||
|
)}
|
|||
|
>
|
|||
|
{period}
|
|||
|
</div>
|
|||
|
))}
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<div className="mx-auto mt-16 grid max-w-2xl grid-cols-1 items-start gap-x-8 gap-y-10 sm:mt-20 lg:max-w-none lg:grid-cols-3">
|
|||
|
{plans.map((plan) => (
|
|||
|
<Plan key={plan.name} {...plan} activePeriod={activePeriod} />
|
|||
|
))}
|
|||
|
</div>
|
|||
|
</Container>
|
|||
|
</section>
|
|||
|
)
|
|||
|
}
|