Skip to content
112 changes: 63 additions & 49 deletions app/pricing/components/ai-power-section.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,95 @@
import React from 'react';
import Image from 'next/image';
import card1 from '/assets/images/pricing/card-1.svg';
import card2 from '/assets/images/pricing/card-2.svg';
import card3 from '/assets/images/pricing/card-3.svg';
import card4 from '/assets/images/pricing/card-4.svg';
import ScrollIcons from '@/components/shared/scroll-icons';

const aiFeatures = [
const aiFeatures: {
id: number;
title: string;
description?: string;
background: string;
image: string;
imageWidth: number;
imageHeight: number;
}[] = [
{
id: 1,
title: 'Brainstorm new ideas and first drafts',
image: card1,
alt: 'Brainstorm new ideas'
background: '#FEF6F5',
image: '/images/pricing/ai/brainstorm.png',
imageWidth: 832,
imageHeight: 424,
},
{
id: 2,
title: 'AI meeting notes',
description: 'automatically captured and enhanced',
image: card2,
alt: 'AI meeting notes automatically captured and enhanced'
background: '#F4EFFF',
image: '/images/pricing/ai/meeting-notes.png',
imageWidth: 832,
imageHeight: 424,
},
{
id: 3,
title: 'Auto-fill columns',
image: card3,
alt: 'Auto-fill columns'
background: '#FEF9DD',
image: '/images/pricing/ai/autofill-columns.png',
imageWidth: 888,
imageHeight: 424,
},
{
id: 4,
title: 'AI search',
description: 'get answers with traceable sources',
image: card4,
alt: 'AI search with answers from traceable sources'
}
background: '#FDF5E7',
image: '/images/pricing/ai/ai-search.png',
imageWidth: 832,
imageHeight: 424,
},
];



export function AiPowerSection() {
return (
<section className='w-full bg-white py-[110px]'>
<div className='mx-auto w-full max-w-screen-xl px-4 sm:px-6 lg:px-8'>
<div className='mx-auto w-full max-w-[1100px] text-center'>
<h2 className='font-inter text-3xl font-medium leading-[105%] tracking-[-0.03em] text-[#101012] sm:text-4xl md:text-5xl lg:text-[52px] xl:text-[56px]'>
<span className='text-[#8427E0]'>Unlock</span> unlimited AI power
<section className='relative isolate w-full overflow-hidden bg-white px-4 py-[120px] sm:px-6'>
<div className='pointer-events-none absolute left-1/2 top-1/2 -z-10 h-[520px] w-[520px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[#F6F4FF] opacity-80 blur-[110px]' />
<div className='mx-auto w-full max-w-[960px]'>
<div className='text-center'>
<h2 className='font-inter text-[36px] font-bold leading-[1.2] tracking-[-0.03em] text-[#140F28] sm:text-[44px] lg:text-[56px] lg:leading-[68px]'>
Unlock unlimited AI power
</h2>
<h3 className='mt-5 text-center font-inter text-2xl font-medium leading-[120%] tracking-[-0.72px] text-[#101012]'>
<p className='mt-3 font-inter text-base font-medium leading-7 text-[#5A5A5A] sm:text-xl'>
AppFlowy AI includes
</h3>
</p>
</div>

{/* AI Feature Cards */}
<div className='mt-[60px]'>
<div className='mx-auto grid w-full max-w-[1100px] grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4'>
{aiFeatures.map((feature) => (
<div key={feature.id} className='flex flex-col items-center'>
<div className='flex h-[320px] w-full flex-shrink-0 flex-col items-center justify-center gap-[25px] rounded-lg bg-gray-50 p-[40px]'>
<Image
src={feature.image}
alt={feature.alt}
width={257}
height={320}
className='max-h-full max-w-full object-contain'
/>
</div>
<h4 className='mt-6 w-full px-6 text-center font-inter text-2xl font-medium leading-[120%] tracking-[-0.24px] text-[#101012]'>
<span className='block'>{feature.title}</span>
{feature.description && <span className='block'>{feature.description}</span>}
</h4>
</div>
))}
</div>
</div>
<div className='mt-10 grid grid-cols-1 gap-4 sm:grid-cols-2'>
{aiFeatures.map((feature) => (
<article
key={feature.id}
className='relative h-[240px] overflow-hidden rounded-2xl px-7 pt-7'
style={{ backgroundColor: feature.background }}
>
<h3 className='relative z-10 w-full max-w-none font-inter text-lg font-medium leading-7 text-[#140F28] sm:text-xl'>
{feature.title}
{feature.description &&
(feature.id === 4 ? (
<> {feature.description}</>
) : (
<span className='block'>{feature.description}</span>
))}
</h3>
<Image
aria-hidden='true'
alt=''
src={feature.image}
width={feature.imageWidth}
height={feature.imageHeight}
className={`pointer-events-none absolute left-7 top-7 h-auto max-w-none ${
feature.id === 3 ? 'w-[calc(100%-28px)]' : 'w-[calc(100%-56px)]'
}`}
/>
</article>
))}
</div>
</div>
<div className={'w-full bg-white pt-[100px]'}>
<ScrollIcons />
</div>
</section>
);
}
127 changes: 47 additions & 80 deletions app/pricing/components/billing-switch.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { motion } from 'framer-motion';

export type BillingCycle = 'yearly' | 'monthly';

Expand All @@ -12,7 +12,6 @@ interface BillingCycleSwitchProps {
}

export function BillingCycleSwitch({ billingCycle, onBillingChange, show }: BillingCycleSwitchProps) {

const toggleBilling = () => {
onBillingChange(billingCycle === 'yearly' ? 'monthly' : 'yearly');
};
Expand All @@ -23,94 +22,62 @@ export function BillingCycleSwitch({ billingCycle, onBillingChange, show }: Bill

return (
<motion.div
className='mx-auto w-full max-w-screen-xl px-4 py-8 sm:px-6 sm:py-9 md:py-10 lg:px-8 lg:py-10 xl:py-10'
className='mx-auto w-full max-w-screen-xl px-4 pt-10 sm:px-6 lg:px-8'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
>
<div className='mx-auto w-full max-w-[1100px]'>
<div className='relative flex items-center justify-center'>
{/* Left: Yearly Label with Save 20% Tag */}
<div
className='absolute right-1/2 flex items-center gap-2 sm:gap-2.5 md:gap-3 lg:gap-3 xl:gap-3'
style={{ marginRight: '34px' }}
>
<AnimatePresence>
{billingCycle === 'yearly' && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.2 }}
className='flex items-center justify-center rounded-md bg-[rgba(200,154,250,0.20)] px-1.5 py-1 sm:px-2 sm:py-1 md:px-2 md:py-1 lg:px-2 lg:py-1 xl:px-2 xl:py-1'
>
<span className='whitespace-nowrap font-inter text-xs font-medium leading-[150%] text-[#8427E0] sm:text-xs md:text-xs lg:text-xs xl:text-xs'>
Save 20%
</span>
</motion.div>
)}
</AnimatePresence>

<button
onClick={() => onBillingChange('yearly')}
className='transition-all duration-200 hover:scale-105 active:scale-95 select-none touch-manipulation'
style={{ WebkitTapHighlightColor: 'transparent' }}
>
<span
className={`whitespace-nowrap font-inter text-sm font-medium leading-[160%] transition-all duration-200 sm:text-base md:text-base lg:text-base xl:text-base ${
billingCycle === 'yearly' ? 'text-[#9327FF]' : 'text-[#101012] opacity-40'
}`}
>
Yearly
</span>
</button>
<div className='flex items-center justify-center gap-5'>
<div className='flex items-center gap-3'>
<div className='flex shrink-0 items-center justify-center rounded-[4px] bg-[rgba(141,66,203,0.1)] px-[6px] py-1'>
<span className='whitespace-nowrap font-inter text-[10px] font-semibold leading-3 text-[#8D42CB]'>
Save 20%
</span>
</div>

{/* Center: Switch Component - Absolutely centered */}
<button
onClick={toggleBilling}
className='relative z-10 flex-shrink-0 cursor-pointer transition-all duration-200 hover:scale-105 active:scale-95 select-none touch-manipulation'
style={{ WebkitTapHighlightColor: 'transparent' }}
type='button'
aria-pressed={billingCycle === 'yearly'}
onClick={() => onBillingChange('yearly')}
className='touch-manipulation select-none whitespace-nowrap font-inter text-sm leading-5 transition-colors duration-200'
style={{
color: billingCycle === 'yearly' ? '#854CFF' : '#AAA',
fontWeight: billingCycle === 'yearly' ? 700 : 400,
WebkitTapHighlightColor: 'transparent',
}}
>
<motion.div
className={`flex items-center rounded-full p-0.5 transition-colors duration-300 sm:p-0.5 md:p-0.5 lg:p-0.5 xl:p-0.5 ${
billingCycle === 'yearly' ? 'bg-[#8427E0]' : 'bg-[#000]'
}`}
style={{ width: '44px', height: '26px' }}
>
<motion.div
className='rounded-full bg-white'
style={{ width: '20px', height: '20px' }}
animate={{
x: billingCycle === 'yearly' ? '2px' : '18px',
}}
transition={{
type: 'spring',
stiffness: 500,
damping: 30,
}}
/>
</motion.div>
Yearly
</button>

{/* Right: Monthly Label */}
<div className='absolute left-1/2' style={{ marginLeft: '34px' }}>
<button
onClick={() => onBillingChange('monthly')}
className='transition-all duration-200 hover:scale-105 active:scale-95 select-none touch-manipulation'
style={{ WebkitTapHighlightColor: 'transparent' }}
>
<span
className={`whitespace-nowrap font-inter text-sm font-medium leading-[160%] transition-all duration-200 sm:text-base md:text-base lg:text-base xl:text-base ${
billingCycle === 'monthly' ? 'text-[#101012]' : 'text-[#101012] opacity-40'
}`}
>
Monthly
</span>
</button>
</div>
</div>

<button
type='button'
aria-label={`Switch to ${billingCycle === 'yearly' ? 'monthly' : 'yearly'} billing`}
aria-pressed={billingCycle === 'monthly'}
onClick={toggleBilling}
className='flex h-5 w-11 shrink-0 touch-manipulation items-center rounded-full bg-[#854CFF] p-0.5'
style={{ WebkitTapHighlightColor: 'transparent' }}
>
<motion.span
className='block h-4 w-7 shrink-0 rounded-full bg-white shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)]'
animate={{ x: billingCycle === 'yearly' ? 0 : 12 }}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
/>
</button>

<button
type='button'
aria-pressed={billingCycle === 'monthly'}
onClick={() => onBillingChange('monthly')}
className='w-[108px] touch-manipulation select-none text-left font-inter text-sm leading-5 transition-colors duration-200'
style={{
color: billingCycle === 'monthly' ? '#854CFF' : '#AAA',
fontWeight: billingCycle === 'monthly' ? 700 : 400,
WebkitTapHighlightColor: 'transparent',
}}
>
Monthly
</button>
</div>
</motion.div>
);
Expand Down
7 changes: 0 additions & 7 deletions app/pricing/components/comparison-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ export function ComparisonTable({
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.3 }}
>
{/* Compare plans title */}
<div className='mx-auto w-full max-w-screen-xl px-4 py-10 text-center sm:px-6 lg:px-8'>
<div className='mx-auto w-full max-w-[1100px]'>
<h2 className='font-inter text-2xl font-medium text-[#101012]'>Compare plans</h2>
</div>
</div>

{/* Responsive Table Display */}
<div className='hidden md:block'>
<DesktopComparisonTable plans={plans} featureGroups={featureGroups} />
Expand Down
Loading
Loading