Import source: cshare-app/src/pages/PricingPage
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,171 @@
|
|||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { CheckIcon, ZapIcon, ShieldIcon, GlobeIcon } from '../components/Icons';
|
||||||
|
|
||||||
|
const PLANS = [
|
||||||
|
{
|
||||||
|
name: 'Free',
|
||||||
|
price: null,
|
||||||
|
desc: 'For casual sharing. No card required.',
|
||||||
|
cta: 'Get started free',
|
||||||
|
ctaPath: '/upload',
|
||||||
|
primary: false,
|
||||||
|
features: [
|
||||||
|
'5 uploads per month',
|
||||||
|
'100 MB max per file',
|
||||||
|
'7-day link expiry',
|
||||||
|
'Basic view tracking',
|
||||||
|
'Copy share link',
|
||||||
|
],
|
||||||
|
missing: ['Custom expiry', 'Password protection', 'Analytics dashboard', 'Priority support'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pro',
|
||||||
|
price: 9,
|
||||||
|
desc: 'For individuals who share files seriously.',
|
||||||
|
cta: 'Start Pro trial',
|
||||||
|
ctaPath: '/upload',
|
||||||
|
primary: true,
|
||||||
|
features: [
|
||||||
|
'Unlimited uploads',
|
||||||
|
'500 MB max per file',
|
||||||
|
'Custom expiry (1h → 90 days)',
|
||||||
|
'Full analytics dashboard',
|
||||||
|
'Password-protected links',
|
||||||
|
'Download tracking',
|
||||||
|
'Priority support',
|
||||||
|
],
|
||||||
|
missing: ['Team dashboard', 'SSO / SAML'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Team',
|
||||||
|
price: 24,
|
||||||
|
desc: 'For small teams that need shared oversight.',
|
||||||
|
cta: 'Talk to us',
|
||||||
|
ctaPath: '/contact',
|
||||||
|
primary: false,
|
||||||
|
features: [
|
||||||
|
'Everything in Pro',
|
||||||
|
'Up to 10 seats',
|
||||||
|
'Shared team dashboard',
|
||||||
|
'Centralized billing',
|
||||||
|
'Role-based permissions',
|
||||||
|
'API access',
|
||||||
|
'SLA + dedicated support',
|
||||||
|
],
|
||||||
|
missing: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function PricingPage() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page-enter" style={{ minHeight: '100vh' }}>
|
||||||
|
{/* Header */}
|
||||||
|
<section style={{ padding: 'clamp(64px, 10vh, 100px) 24px 48px', textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 70% 60% at 50% 0%, rgba(74,144,255,0.06) 0%, transparent 70%)', pointerEvents: 'none' }} />
|
||||||
|
<div style={{ position: 'relative', zIndex: 1, maxWidth: '640px', margin: '0 auto' }}>
|
||||||
|
<div style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', marginBottom: '20px' }}>
|
||||||
|
<span style={{ color: 'var(--accent)', fontFamily: 'var(--font-heading)', fontWeight: 700 }}>//</span>
|
||||||
|
<span style={{ color: 'var(--text-muted)', fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.75rem', letterSpacing: '0.12em', textTransform: 'uppercase' }}>Pricing</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="heading-lg" style={{ marginBottom: '16px' }}>Simple, honest pricing.</h1>
|
||||||
|
<p style={{ color: 'var(--text-secondary)', fontSize: '1.0625rem', lineHeight: 1.7 }}>
|
||||||
|
No per-file fees, no storage surprises. Pay for what you need, cancel any time.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Plan cards */}
|
||||||
|
<section style={{ padding: '0 24px 80px', maxWidth: '1080px', margin: '0 auto' }}>
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '20px', alignItems: 'start' }}>
|
||||||
|
{PLANS.map((plan) => (
|
||||||
|
<div
|
||||||
|
key={plan.name}
|
||||||
|
className="card"
|
||||||
|
style={{
|
||||||
|
padding: '32px',
|
||||||
|
position: 'relative',
|
||||||
|
border: plan.primary ? '1px solid var(--accent-border)' : '1px solid var(--border-subtle)',
|
||||||
|
boxShadow: plan.primary ? '0 0 40px rgba(74,144,255,0.08)' : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{plan.primary && (
|
||||||
|
<>
|
||||||
|
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '2px', background: 'linear-gradient(90deg, transparent, var(--accent), transparent)' }} />
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: '16px', right: '16px',
|
||||||
|
padding: '3px 10px', background: 'var(--accent)', borderRadius: '999px',
|
||||||
|
fontSize: '0.6875rem', fontFamily: 'var(--font-body)', fontWeight: 600,
|
||||||
|
color: '#fff', letterSpacing: '0.04em',
|
||||||
|
}}>
|
||||||
|
Most popular
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div style={{ marginBottom: '24px' }}>
|
||||||
|
<div style={{ fontFamily: 'var(--font-heading)', fontWeight: 700, fontSize: '1rem', color: 'var(--text-secondary)', letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: '12px' }}>
|
||||||
|
{plan.name}
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'baseline', gap: '4px', marginBottom: '8px' }}>
|
||||||
|
{plan.price ? (
|
||||||
|
<>
|
||||||
|
<span style={{ fontFamily: 'var(--font-heading)', fontWeight: 800, fontSize: '2.75rem', letterSpacing: '-0.04em', lineHeight: 1 }}>
|
||||||
|
${plan.price}
|
||||||
|
</span>
|
||||||
|
<span style={{ color: 'var(--text-muted)', fontSize: '0.875rem' }}>/mo</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span style={{ fontFamily: 'var(--font-heading)', fontWeight: 800, fontSize: '2.75rem', letterSpacing: '-0.04em', lineHeight: 1 }}>
|
||||||
|
Free
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p style={{ color: 'var(--text-muted)', fontSize: '0.875rem', lineHeight: 1.6 }}>{plan.desc}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className={`btn ${plan.primary ? 'btn-primary' : 'btn-secondary'}`}
|
||||||
|
style={{ width: '100%', marginBottom: '28px' }}
|
||||||
|
onClick={() => navigate(plan.ctaPath)}
|
||||||
|
>
|
||||||
|
{plan.cta}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||||
|
{plan.features.map((f) => (
|
||||||
|
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.875rem', color: 'var(--text-secondary)' }}>
|
||||||
|
<CheckIcon size={14} style={{ color: 'var(--success)', flexShrink: 0 }} />
|
||||||
|
{f}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{plan.missing.map((f) => (
|
||||||
|
<div key={f} style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.875rem', color: 'var(--text-muted)', opacity: 0.5 }}>
|
||||||
|
<div style={{ width: '14px', height: '14px', borderRadius: '50%', border: '1.5px solid var(--border)', flexShrink: 0 }} />
|
||||||
|
{f}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* FAQ strip */}
|
||||||
|
<div style={{ marginTop: '64px', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))', gap: '20px' }}>
|
||||||
|
{[
|
||||||
|
{ icon: <ZapIcon size={18} />, q: 'Can I upgrade or downgrade?', a: 'Yes, any time. Changes take effect immediately and are prorated to the day.' },
|
||||||
|
{ icon: <ShieldIcon size={18} />, q: 'What happens when a free link expires?', a: 'The file and link are permanently deleted from our servers. No recovery.' },
|
||||||
|
{ icon: <GlobeIcon size={18} />, q: 'Do recipients need an account?', a: 'Never. Anyone with the link can download — no sign-up wall on their end.' },
|
||||||
|
].map(({ icon, q, a }) => (
|
||||||
|
<div key={q} className="card" style={{ padding: '24px' }}>
|
||||||
|
<div style={{ color: 'var(--accent)', marginBottom: '10px' }}>{icon}</div>
|
||||||
|
<div style={{ fontFamily: 'var(--font-heading)', fontWeight: 600, fontSize: '0.9375rem', marginBottom: '8px' }}>{q}</div>
|
||||||
|
<p style={{ color: 'var(--text-secondary)', fontSize: '0.875rem', lineHeight: 1.65 }}>{a}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user