Skip to content

Instantly share code, notes, and snippets.

@jonathanpeterwu
Created January 28, 2026 20:58
Show Gist options
  • Select an option

  • Save jonathanpeterwu/111a8e4d8b700c86ed807c0cb1fa5cb5 to your computer and use it in GitHub Desktop.

Select an option

Save jonathanpeterwu/111a8e4d8b700c86ed807c0cb1fa5cb5 to your computer and use it in GitHub Desktop.
proposal
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rize - Pricing & Onboarding</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
* { box-sizing: border-box; }
body { margin: 0; padding: 0; background: #080808; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel" data-type="module">
const { useState, useEffect } = React;
// ==========================================
// MAIN APP
// ==========================================
function App() {
const [view, setView] = useState('home');
const [selectedTier, setSelectedTier] = useState(null);
const [showDemoModal, setShowDemoModal] = useState(false);
const startOnboarding = (tier) => {
if (tier === 'enterprise') {
setShowDemoModal(true);
return;
}
setSelectedTier(tier);
setView('onboarding');
};
const backToPricing = () => {
setView('pricing');
setSelectedTier(null);
};
return (
<div className="min-h-screen text-white antialiased" style={{ backgroundColor: '#080808' }}>
{/* View Toggle */}
<div className="sticky top-0 z-50 border-b p-3" style={{ backgroundColor: '#0a0a0a', borderColor: 'rgba(255,255,255,0.1)' }}>
<div className="mx-auto max-w-5xl flex justify-center gap-2">
<button
onClick={() => setView('home')}
className="px-4 py-1.5 rounded-full text-sm transition-all"
style={{
backgroundColor: view === 'home' ? '#fff' : 'transparent',
color: view === 'home' ? '#18181b' : '#a1a1aa',
border: view === 'home' ? 'none' : '1px solid rgba(255,255,255,0.1)'
}}
>
Homepage
</button>
<button
onClick={() => setView('pricing')}
className="px-4 py-1.5 rounded-full text-sm transition-all"
style={{
backgroundColor: view === 'pricing' ? '#fff' : 'transparent',
color: view === 'pricing' ? '#18181b' : '#a1a1aa',
border: view === 'pricing' ? 'none' : '1px solid rgba(255,255,255,0.1)'
}}
>
Pricing
</button>
<button
onClick={() => setView('onboarding')}
className="px-4 py-1.5 rounded-full text-sm transition-all"
style={{
backgroundColor: view === 'onboarding' ? '#fff' : 'transparent',
color: view === 'onboarding' ? '#18181b' : '#a1a1aa',
border: view === 'onboarding' ? 'none' : '1px solid rgba(255,255,255,0.1)'
}}
>
Onboarding
</button>
<button
onClick={() => setShowDemoModal(true)}
className="px-4 py-1.5 rounded-full text-sm transition-all"
style={{
backgroundColor: 'transparent',
color: '#a1a1aa',
border: '1px solid rgba(255,255,255,0.1)'
}}
>
Demo Modal
</button>
</div>
</div>
{view === 'home' && (
<HomePage onGetStarted={() => startOnboarding('pro')} onBookDemo={() => setShowDemoModal(true)} onViewPricing={() => setView('pricing')} />
)}
{view === 'pricing' && (
<PricingPage onSelectTier={startOnboarding} onBookDemo={() => setShowDemoModal(true)} />
)}
{view === 'onboarding' && (
<OnboardingFlow tier={selectedTier} onBack={backToPricing} />
)}
{showDemoModal && (
<DemoModal onClose={() => setShowDemoModal(false)} />
)}
</div>
);
}
// ==========================================
// HOMEPAGE
// ==========================================
function HomePage({ onGetStarted, onBookDemo, onViewPricing }) {
const [currentTestimonial, setCurrentTestimonial] = useState(0);
const testimonials = [
{ name: 'Ben Jackson', role: 'CEO, Momentum Studio', quote: "Rize has completely transformed how our team tracks time. We've recovered over 10 hours per week.", color: '#8B5CF6' },
{ name: 'Sarah Chen', role: 'Design Director, TechFlow', quote: "The automatic categorization is incredible. I finally understand where my time actually goes.", color: '#3B82F6' },
{ name: 'Marcus Williams', role: 'Partner, Agency X', quote: "Client reporting used to take hours. Now Rize generates everything automatically.", color: '#F97316' },
];
useEffect(() => {
const timer = setInterval(() => {
setCurrentTestimonial(prev => (prev + 1) % testimonials.length);
}, 5000);
return () => clearInterval(timer);
}, []);
const integrations = [
{ name: 'ClickUp', color: '#7B68EE' },
{ name: 'Linear', color: '#5E6AD2' },
{ name: 'Asana', color: '#F06A6A' },
{ name: 'Google Calendar', color: '#4285F4' },
{ name: 'Zapier', color: '#FF4A00' },
];
const benefits = [
{ stat: '5+ hrs', label: 'saved per week', color: '#10B981' },
{ stat: '98%', label: 'tracking accuracy', color: '#14B8A6' },
{ stat: '30 sec', label: 'daily review', color: '#8B5CF6' },
{ stat: '2x', label: 'billable capture', color: '#3B82F6' },
];
return (
<div style={{ backgroundColor: '#080808' }}>
{/* Navigation */}
<nav className="px-6 py-4 flex items-center justify-between" style={{ maxWidth: '1200px', margin: '0 auto' }}>
<span className="text-xl tracking-widest font-light text-white">RIZE</span>
<div className="flex items-center gap-6">
<button onClick={onViewPricing} className="text-sm" style={{ color: '#a1a1aa' }}>Pricing</button>
<button className="text-sm" style={{ color: '#a1a1aa' }}>Teams</button>
<button className="text-sm" style={{ color: '#a1a1aa' }}>About</button>
<button onClick={onGetStarted} className="px-4 py-2 rounded-full text-sm font-medium" style={{ backgroundColor: '#fff', color: '#000' }}>
Start Free Trial
</button>
</div>
</nav>
{/* Hero */}
<section className="px-6 py-20 text-center" style={{ maxWidth: '900px', margin: '0 auto' }}>
<h1 className="text-4xl md:text-5xl font-bold text-white mb-6 leading-tight">
Save 5+ Hours Every Week<br />on Time Tracking
</h1>
<p className="text-lg mb-8" style={{ color: '#a1a1aa' }}>
Rize automatically tracks your time, categorizes your work, and generates reportsβ€”so you can focus on what matters.
</p>
<div className="flex items-center justify-center gap-4 mb-12">
<button onClick={onGetStarted} className="px-6 py-3 rounded-full text-sm font-medium" style={{ backgroundColor: '#fff', color: '#000' }}>
Start Free Trial
</button>
<button onClick={onBookDemo} className="px-6 py-3 rounded-full text-sm font-medium" style={{ backgroundColor: 'transparent', color: '#d4d4d8', border: '1px solid rgba(255,255,255,0.2)' }}>
Book Demo
</button>
</div>
<div className="flex items-center justify-center gap-8 text-sm" style={{ color: '#71717a' }}>
<span>β˜… 4.5/5 on G2</span>
<span>πŸ† #1 Product Hunt</span>
<span>100K+ users</span>
</div>
</section>
{/* Integrations */}
<section className="px-6 py-12" style={{ backgroundColor: '#0a0a0a' }}>
<div style={{ maxWidth: '800px', margin: '0 auto' }}>
<p className="text-center text-sm mb-6" style={{ color: '#52525b' }}>Works with your favorite tools</p>
<div className="flex items-center justify-center gap-8">
{integrations.map(int => (
<div key={int.name} className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg flex items-center justify-center text-white text-xs font-bold" style={{ backgroundColor: int.color }}>
{int.name[0]}
</div>
<span className="text-sm" style={{ color: '#71717a' }}>{int.name}</span>
</div>
))}
</div>
</div>
</section>
{/* Benefits */}
<section className="px-6 py-16" style={{ maxWidth: '1000px', margin: '0 auto' }}>
<h2 className="text-2xl font-semibold text-white text-center mb-12">Quantified results from real users</h2>
<div className="grid grid-cols-4 gap-4">
{benefits.map((benefit, i) => (
<div key={i} className="text-center p-6 rounded-2xl" style={{ backgroundColor: '#111113', border: '1px solid rgba(255,255,255,0.06)' }}>
<div className="text-3xl font-bold mb-2" style={{ color: benefit.color }}>{benefit.stat}</div>
<div className="text-sm" style={{ color: '#71717a' }}>{benefit.label}</div>
</div>
))}
</div>
</section>
{/* Testimonials */}
<section className="px-6 py-16" style={{ backgroundColor: '#0a0a0a' }}>
<div style={{ maxWidth: '700px', margin: '0 auto' }}>
<div
className="p-8 rounded-2xl text-center"
style={{ backgroundColor: '#111113', border: `1px solid ${testimonials[currentTestimonial].color}30` }}
>
<p className="text-lg mb-6 text-white">"{testimonials[currentTestimonial].quote}"</p>
<div className="flex items-center justify-center gap-3">
<div className="w-10 h-10 rounded-full flex items-center justify-center text-white font-bold" style={{ backgroundColor: testimonials[currentTestimonial].color }}>
{testimonials[currentTestimonial].name[0]}
</div>
<div className="text-left">
<div className="text-sm font-medium text-white">{testimonials[currentTestimonial].name}</div>
<div className="text-xs" style={{ color: '#71717a' }}>{testimonials[currentTestimonial].role}</div>
</div>
</div>
</div>
<div className="flex items-center justify-center gap-2 mt-6">
{testimonials.map((_, i) => (
<button
key={i}
onClick={() => setCurrentTestimonial(i)}
className="h-2 rounded-full transition-all"
style={{
backgroundColor: i === currentTestimonial ? testimonials[currentTestimonial].color : '#3f3f46',
width: i === currentTestimonial ? '24px' : '8px'
}}
/>
))}
</div>
</div>
</section>
{/* Team & Enterprise */}
<section className="px-6 py-16" style={{ maxWidth: '1000px', margin: '0 auto' }}>
<div className="grid md:grid-cols-2 gap-6">
<div className="p-8 rounded-2xl" style={{ background: 'linear-gradient(135deg, rgba(139,92,246,0.15) 0%, rgba(139,92,246,0.05) 100%)', border: '1px solid rgba(139,92,246,0.2)' }}>
<span className="text-xs font-medium px-2 py-1 rounded" style={{ backgroundColor: 'rgba(139,92,246,0.2)', color: '#A78BFA' }}>TEAMS</span>
<h3 className="text-xl font-semibold text-white mt-4 mb-2">Roll out Rize to your team</h3>
<p className="text-sm mb-6" style={{ color: '#a1a1aa' }}>Get visibility into team workload, project profitability, and client reports.</p>
<div className="flex items-baseline gap-1 mb-6">
<span className="text-2xl font-bold text-white">$29.99</span>
<span className="text-sm" style={{ color: '#71717a' }}>/user/mo</span>
</div>
<button onClick={onGetStarted} className="px-4 py-2 rounded-full text-sm font-medium" style={{ backgroundColor: '#fff', color: '#000' }}>
Start Team Trial
</button>
</div>
<div className="p-8 rounded-2xl" style={{ backgroundColor: '#111113', border: '1px solid rgba(255,255,255,0.06)' }}>
<span className="text-xs font-medium px-2 py-1 rounded" style={{ backgroundColor: 'rgba(255,255,255,0.1)', color: '#71717a' }}>ENTERPRISE (10+ SEATS)</span>
<h3 className="text-xl font-semibold text-white mt-4 mb-2">Need 10+ seats?</h3>
<p className="text-sm mb-6" style={{ color: '#71717a' }}>SSO, dedicated account manager, custom onboarding, and priority support.</p>
<button onClick={onBookDemo} className="px-4 py-2 rounded-full text-sm font-medium" style={{ backgroundColor: '#006BFF', color: '#fff' }}>
πŸ“… Book a Demo
</button>
</div>
</div>
</section>
<FinalCTA onGetStarted={onGetStarted} onBookDemo={onBookDemo} />
</div>
);
}
// ==========================================
// FINAL CTA
// ==========================================
function FinalCTA({ onGetStarted, onBookDemo }) {
return (
<div className="py-20 relative overflow-hidden" style={{ backgroundColor: '#080808' }}>
<div className="relative mx-auto" style={{ width: '700px', height: '400px' }}>
<div className="absolute inset-0 rounded-full" style={{ background: 'radial-gradient(ellipse at center, rgba(20,20,25,1) 0%, rgba(20,20,25,0.8) 40%, transparent 70%)', transform: 'scale(1.2)' }} />
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full" style={{ width: '500px', height: '500px', background: 'radial-gradient(ellipse at center, #0c0c0c 0%, #0a0a0a 50%, #080808 100%)', boxShadow: 'inset 0 0 100px 20px rgba(0,0,0,0.8)' }} />
<div className="absolute left-1/2 -translate-x-1/2" style={{ bottom: '20px', width: '400px', height: '100px', background: 'linear-gradient(180deg, rgba(139,92,246,0.4) 0%, rgba(168,85,247,0.2) 30%, transparent 100%)', filter: 'blur(30px)', borderRadius: '50%' }} />
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center">
<span className="text-3xl tracking-widest font-light" style={{ color: 'rgba(212,212,216,0.9)' }}>RIZE</span>
</div>
</div>
<div className="relative text-center mt-4 px-6">
<p className="text-xl mb-10" style={{ color: '#a1a1aa' }}>
Join hundreds of thousands of people who<br />made Rize a core part of how they work.
</p>
<div className="flex items-center justify-center gap-4">
<button onClick={onGetStarted} className="px-10 py-4 rounded-xl text-base font-medium" style={{ backgroundColor: '#fff', color: '#000' }}>
Get Started for Free
</button>
<button onClick={onBookDemo} className="px-10 py-4 rounded-xl text-base font-medium" style={{ backgroundColor: 'transparent', color: '#d4d4d8', border: '1px solid rgba(255,255,255,0.2)' }}>
Book Demo
</button>
</div>
</div>
</div>
);
}
// ==========================================
// PRICING PAGE
// ==========================================
function PricingPage({ onSelectTier, onBookDemo }) {
const [billingCycle, setBillingCycle] = useState('yearly');
const tiers = [
{ id: 'lite', name: 'Lite', monthlyPrice: 9.99, yearlyPrice: 7.99, desc: 'Basic time tracking', features: ['Automatic time tracking', 'Daily/weekly reports', 'Focus sessions', 'Basic analytics'], cta: 'Start Free Trial', highlighted: false },
{ id: 'pro', name: 'Pro', monthlyPrice: 19.99, yearlyPrice: 16.99, desc: 'For professionals', features: ['Everything in Lite', 'Projects & clients', 'Billable hours tracking', 'Integrations (2 apps)', 'Priority support'], cta: 'Start Free Trial', highlighted: true },
{ id: 'team', name: 'Team', monthlyPrice: 29.99, yearlyPrice: 24.99, desc: 'For growing teams', features: ['Everything in Pro', 'Team dashboard', 'Unlimited integrations', 'Client reports', 'Admin controls'], cta: 'Start Free Trial', highlighted: false, perUser: true },
{ id: 'enterprise', name: 'Enterprise', monthlyPrice: null, yearlyPrice: null, desc: 'For large organizations', features: ['Everything in Team', 'SSO & SAML', 'Dedicated manager', 'Custom onboarding', 'SLA & uptime guarantee'], cta: 'Contact Us', highlighted: false },
];
return (
<div style={{ backgroundColor: '#0a0a0a' }}>
<div className="px-6 py-16" style={{ maxWidth: '1100px', margin: '0 auto' }}>
<div className="text-center mb-12">
<h1 className="text-3xl font-bold text-white mb-4">Simple, transparent pricing</h1>
<p className="text-base mb-8" style={{ color: '#71717a' }}>Start free, upgrade when you're ready</p>
<div className="inline-flex rounded-full p-1" style={{ backgroundColor: '#1a1a1f' }}>
<button onClick={() => setBillingCycle('yearly')} className="px-4 py-2 rounded-full text-sm transition-all" style={{ backgroundColor: billingCycle === 'yearly' ? '#fff' : 'transparent', color: billingCycle === 'yearly' ? '#18181b' : '#71717a' }}>
Yearly <span style={{ color: '#10B981' }}>Save 20%</span>
</button>
<button onClick={() => setBillingCycle('monthly')} className="px-4 py-2 rounded-full text-sm transition-all" style={{ backgroundColor: billingCycle === 'monthly' ? '#fff' : 'transparent', color: billingCycle === 'monthly' ? '#18181b' : '#71717a' }}>
Monthly
</button>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '12px' }}>
{tiers.map(tier => (
<div key={tier.id} className="rounded-2xl p-5 flex flex-col" style={{ backgroundColor: '#111113', border: tier.highlighted ? '1px solid rgba(255,255,255,0.15)' : '1px solid rgba(255,255,255,0.06)' }}>
<div className="mb-4">
<h3 className="text-lg font-semibold text-white">{tier.name}</h3>
<p className="text-xs mt-1" style={{ color: '#52525b' }}>{tier.desc}</p>
</div>
<div className="mb-4">
{tier.monthlyPrice ? (
<>
<span className="text-2xl font-bold text-white">${billingCycle === 'yearly' ? tier.yearlyPrice : tier.monthlyPrice}</span>
<span className="text-xs" style={{ color: '#71717a' }}>{tier.perUser ? '/user/mo' : '/mo'}</span>
</>
) : (
<span className="text-2xl font-bold text-white">Custom</span>
)}
</div>
<button onClick={() => onSelectTier(tier.id)} className="w-full py-2.5 rounded-full text-sm font-medium mb-4" style={{ backgroundColor: tier.highlighted ? '#fff' : 'transparent', color: tier.highlighted ? '#18181b' : '#d4d4d8', border: tier.highlighted ? 'none' : '1px solid rgba(255,255,255,0.2)' }}>
{tier.cta}
</button>
<div className="space-y-2 mt-auto">
{tier.features.map((feature, i) => (
<div key={i} className="flex items-start gap-2">
<svg className="w-4 h-4 mt-0.5 flex-shrink-0" viewBox="0 0 20 20" fill="none">
<path d="M16.667 5L7.5 14.167L3.333 10" stroke="#8B5CF6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
<span className="text-xs" style={{ color: '#e4e4e7' }}>{feature}</span>
</div>
))}
</div>
</div>
))}
</div>
<FinalCTA onGetStarted={() => onSelectTier('pro')} onBookDemo={onBookDemo} />
</div>
</div>
);
}
// ==========================================
// ONBOARDING FLOW
// ==========================================
function OnboardingFlow({ tier, onBack }) {
const [step, setStep] = useState(0);
const [formData, setFormData] = useState({
email: '', name: '', role: '', teamSize: '', teamTools: [], teamEmails: [''], domainAutoJoin: false, goals: [], integrations: [], phone: ''
});
const isEnterprise = tier === 'enterprise';
const isTeam = tier === 'team';
const isManagerFlow = ['Manager', 'Executive'].includes(formData.role);
const isLargeTeam = ['50+'].includes(formData.teamSize);
const showTeamSizeForRole = ['Founder', 'Manager', 'Executive', 'Consultant'].includes(formData.role);
const tierColors = { lite: '#71717a', pro: '#8B5CF6', team: '#10B981', enterprise: '#006BFF' };
const tierColor = tierColors[tier] || '#8B5CF6';
const tierNames = { lite: 'Lite', pro: 'Pro', team: 'Team', enterprise: 'Enterprise' };
const getSteps = () => {
const baseSteps = [
{ id: 'email', title: 'Get started', subtitle: 'Create your Rize account' },
{ id: 'name', title: "What's your name?", subtitle: "We'll personalize your experience" },
{ id: 'role', title: 'What best describes you?', subtitle: "This helps us tailor Rize" },
];
// Enterprise tier or 50+ routes directly to sales
if (isEnterprise || (isManagerFlow && isLargeTeam)) {
return [...baseSteps, { id: 'teamSize', title: 'How many people?', subtitle: 'This helps us prepare' }, { id: 'calendly', title: 'Schedule your call', subtitle: 'Meet with our team' }];
}
// Manager/Team flow
if (isManagerFlow || isTeam) {
const flowSteps = [...baseSteps, { id: 'teamSize', title: 'How many people?', subtitle: 'This helps us set up' }, { id: 'teamTools', title: 'What tools does your team use?', subtitle: 'Select all that apply' }, { id: 'invite', title: 'Invite your team', subtitle: 'Get started together' }];
// Add phone step for 6+ users (6-10 or 11-50)
if (formData.teamSize === '6-10' || formData.teamSize === '11-50') {
flowSteps.push({ id: 'phone', title: 'Enter your phone number if you\'d like help with account setup:', subtitle: 'Optional' });
}
flowSteps.push({ id: 'integrations', title: 'Connect your tools', subtitle: 'Rize works with your apps' });
// Add demo option for 6+ users at download step
if (formData.teamSize === '6-10' || formData.teamSize === '11-50') {
flowSteps.push({ id: 'download', title: "You're all set!", subtitle: 'Download Rize or schedule a demo' });
} else {
flowSteps.push({ id: 'download', title: "You're all set!", subtitle: 'Download Rize' });
}
return flowSteps;
}
if (showTeamSizeForRole) {
return [...baseSteps, { id: 'teamSize', title: 'Team size?', subtitle: 'Helps us personalize' }, { id: 'goals', title: 'Your goals?', subtitle: 'Select all that apply' }, { id: 'integrations', title: 'Connect tools', subtitle: 'Your favorite apps' }, { id: 'download', title: "You're all set!", subtitle: 'Download Rize' }];
}
return [...baseSteps, { id: 'goals', title: 'Your goals?', subtitle: 'Select all that apply' }, { id: 'integrations', title: 'Connect tools', subtitle: 'Your favorite apps' }, { id: 'download', title: "You're all set!", subtitle: 'Download Rize' }];
};
const steps = getSteps();
const currentStep = steps[step];
const progress = ((step + 1) / steps.length) * 100;
const nextStep = () => { if (step < steps.length - 1) setStep(step + 1); };
const prevStep = () => { if (step > 0) setStep(step - 1); else onBack(); };
const updateFormData = (key, value) => setFormData(prev => ({ ...prev, [key]: value }));
const toggleArrayItem = (key, item) => {
setFormData(prev => ({
...prev,
[key]: prev[key].includes(item) ? prev[key].filter(i => i !== item) : [...prev[key], item]
}));
};
return (
<div className="min-h-screen flex flex-col" style={{ backgroundColor: '#080808' }}>
{/* Progress */}
<div className="h-1" style={{ backgroundColor: '#1a1a1f' }}>
<div className="h-full transition-all duration-300" style={{ width: `${progress}%`, backgroundColor: tierColor }} />
</div>
{/* Header */}
<div className="px-4 py-4 flex items-center justify-between">
<span className="text-lg tracking-widest font-light text-white">RIZE</span>
<span className="text-xs px-2 py-1 rounded" style={{ backgroundColor: `${tierColor}20`, color: tierColor }}>{tierNames[tier] || 'Pro'}</span>
</div>
{/* Content */}
<div className="flex-1 flex items-center justify-center px-4 py-8">
<div className="w-full max-w-md">
<div className="text-center mb-8">
{currentStep.subtitle && <p className="text-xs mb-2" style={{ color: '#71717a' }}>{currentStep.subtitle}</p>}
<h2 className="text-2xl font-semibold text-white">{currentStep.title}</h2>
</div>
{/* Step Content */}
{currentStep.id === 'email' && (
<div className="space-y-4">
<input type="email" placeholder="you@company.com" value={formData.email} onChange={(e) => updateFormData('email', e.target.value)} className="w-full px-4 py-3 rounded-xl text-white text-sm outline-none" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.1)' }} />
<button onClick={nextStep} disabled={!formData.email} className="w-full py-3 rounded-full text-sm font-medium disabled:opacity-50" style={{ backgroundColor: tierColor, color: '#fff' }}>Continue with Email</button>
<div className="flex items-center gap-3">
<div className="flex-1 h-px" style={{ backgroundColor: 'rgba(255,255,255,0.1)' }}></div>
<span className="text-xs" style={{ color: '#71717a' }}>or</span>
<div className="flex-1 h-px" style={{ backgroundColor: 'rgba(255,255,255,0.1)' }}></div>
</div>
<button className="w-full py-3 rounded-full text-sm font-medium" style={{ backgroundColor: '#fff', color: '#18181b' }}>Continue with Google</button>
</div>
)}
{currentStep.id === 'name' && (
<input type="text" placeholder="Your name" value={formData.name} onChange={(e) => updateFormData('name', e.target.value)} className="w-full px-4 py-3 rounded-xl text-white text-sm outline-none" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.1)' }} />
)}
{currentStep.id === 'role' && (
<div className="grid grid-cols-2 gap-2">
{['Freelancer', 'Employee', 'Manager', 'Executive', 'Founder', 'Consultant', 'Designer', 'Developer'].map(role => (
<button key={role} onClick={() => updateFormData('role', role)} className="px-4 py-3 rounded-xl text-sm" style={{ backgroundColor: formData.role === role ? `${tierColor}20` : '#141419', border: formData.role === role ? `2px solid ${tierColor}` : '1px solid rgba(255,255,255,0.1)', color: formData.role === role ? tierColor : '#d4d4d8' }}>
{role}
</button>
))}
</div>
)}
{currentStep.id === 'teamSize' && (
<div className="grid grid-cols-2 gap-2">
{(isManagerFlow || isTeam) ? ['2-5', '6-10', '11-50', '50+'].map(size => (
<button key={size} onClick={() => updateFormData('teamSize', size)} className="px-4 py-3 rounded-xl text-sm" style={{ backgroundColor: formData.teamSize === size ? `${tierColor}20` : '#141419', border: formData.teamSize === size ? `2px solid ${tierColor}` : '1px solid rgba(255,255,255,0.1)', color: formData.teamSize === size ? tierColor : '#d4d4d8' }}>
{size} people
</button>
)) : ['Just me', '2-5', '6-10', '11-25', '26-50', '50+'].map(size => (
<button key={size} onClick={() => updateFormData('teamSize', size)} className="px-4 py-3 rounded-xl text-sm" style={{ backgroundColor: formData.teamSize === size ? `${tierColor}20` : '#141419', border: formData.teamSize === size ? `2px solid ${tierColor}` : '1px solid rgba(255,255,255,0.1)', color: formData.teamSize === size ? tierColor : '#d4d4d8' }}>
{size === 'Just me' ? size : `${size} people`}
</button>
))}
</div>
)}
{currentStep.id === 'teamTools' && (
<div className="space-y-3">
{[{ name: 'ClickUp', icon: 'πŸ“‹' }, { name: 'Linear', icon: '⚑' }, { name: 'Asana', icon: '🎯' }, { name: 'Notion', icon: 'πŸ“' }, { name: 'Jira', icon: 'πŸ”·' }, { name: 'Other', icon: 'βž•' }].map(tool => (
<button key={tool.name} onClick={() => toggleArrayItem('teamTools', tool.name)} className="w-full px-4 py-4 rounded-xl text-sm text-left flex items-center gap-4" style={{ backgroundColor: formData.teamTools.includes(tool.name) ? 'rgba(139,92,246,0.15)' : '#141419', border: formData.teamTools.includes(tool.name) ? '2px solid #8B5CF6' : '1px solid rgba(255,255,255,0.1)' }}>
<span className="text-xl">{tool.icon}</span>
<span className="flex-1 text-white">{tool.name}</span>
{formData.teamTools.includes(tool.name) && <span style={{ color: '#8B5CF6' }}>βœ“</span>}
</button>
))}
</div>
)}
{currentStep.id === 'invite' && (
<div className="space-y-6">
<p className="text-sm text-center" style={{ color: '#71717a' }}>Invite your team to <span style={{ color: '#10B981' }}>get started 200% faster</span></p>
<input type="text" placeholder="Enter email addresses (or paste multiple)" value={formData.teamEmails[0]} onChange={(e) => updateFormData('teamEmails', [e.target.value])} className="w-full px-4 py-4 rounded-xl text-white text-sm outline-none" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.15)' }} />
<div className="flex items-center justify-between p-4 rounded-xl" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.1)' }}>
<div className="flex items-center gap-3">
<span style={{ color: '#8B5CF6' }}>πŸ”—</span>
<div>
<p className="text-sm text-white">Allow @{formData.email?.split('@')[1] || 'company.com'}</p>
<p className="text-xs" style={{ color: '#71717a' }}>Auto-join workspace</p>
</div>
</div>
<button onClick={() => updateFormData('domainAutoJoin', !formData.domainAutoJoin)} className="w-12 h-6 rounded-full relative" style={{ backgroundColor: formData.domainAutoJoin ? '#8B5CF6' : '#3f3f46' }}>
<div className="w-5 h-5 rounded-full bg-white absolute top-0.5 transition-all" style={{ left: formData.domainAutoJoin ? '26px' : '2px' }} />
</button>
</div>
<button onClick={nextStep} className="text-sm block mx-auto" style={{ color: '#71717a' }}>Skip for now β†’</button>
</div>
)}
{currentStep.id === 'phone' && (
<div className="space-y-6">
<div className="flex gap-3">
<button className="px-4 py-4 rounded-xl flex items-center gap-2" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.15)' }}>
<span>πŸ‡ΊπŸ‡Έ</span><span style={{ color: '#71717a' }}>β–Ό</span>
</button>
<input type="tel" placeholder="(201) 555-0123" value={formData.phone} onChange={(e) => updateFormData('phone', e.target.value)} className="flex-1 px-4 py-4 rounded-xl text-white text-sm outline-none" style={{ backgroundColor: '#141419', border: '1px solid rgba(255,255,255,0.15)' }} />
</div>
<p className="text-xs" style={{ color: '#52525b' }}>Our team might reach out to help you get started. We won't share your phone number.</p>
<button onClick={nextStep} className="text-sm block mx-auto" style={{ color: '#71717a' }}>Skip β†’</button>
</div>
)}
{currentStep.id === 'goals' && (
<div className="space-y-2">
{['Track billable hours', 'Improve focus', 'Understand time usage', 'Generate reports', 'Manage team workload'].map(goal => (
<button key={goal} onClick={() => toggleArrayItem('goals', goal)} className="w-full px-4 py-3 rounded-xl text-sm text-left flex items-center gap-3" style={{ backgroundColor: formData.goals.includes(goal) ? `${tierColor}20` : '#141419', border: formData.goals.includes(goal) ? `2px solid ${tierColor}` : '1px solid rgba(255,255,255,0.1)', color: formData.goals.includes(goal) ? tierColor : '#d4d4d8' }}>
<span className="w-5 h-5 rounded flex items-center justify-center text-xs" style={{ backgroundColor: formData.goals.includes(goal) ? tierColor : 'rgba(255,255,255,0.1)', color: '#fff' }}>
{formData.goals.includes(goal) ? 'βœ“' : ''}
</span>
{goal}
</button>
))}
</div>
)}
{currentStep.id === 'integrations' && (
<div className="space-y-2">
{[{ name: 'ClickUp', color: '#7B68EE' }, { name: 'Linear', color: '#5E6AD2' }, { name: 'Asana', color: '#F06A6A' }, { name: 'Google Calendar', color: '#4285F4' }, { name: 'None for now', color: '#71717a' }].map(int => (
<button key={int.name} onClick={() => toggleArrayItem('integrations', int.name)} className="w-full px-4 py-3 rounded-xl text-sm text-left flex items-center gap-3" style={{ backgroundColor: formData.integrations.includes(int.name) ? `${int.color}20` : '#141419', border: formData.integrations.includes(int.name) ? `2px solid ${int.color}` : '1px solid rgba(255,255,255,0.1)', color: '#d4d4d8' }}>
<div className="w-6 h-6 rounded flex items-center justify-center text-white text-xs font-bold" style={{ backgroundColor: int.color }}>{int.name[0]}</div>
{int.name}
{formData.integrations.includes(int.name) && <span className="ml-auto" style={{ color: int.color }}>βœ“</span>}
</button>
))}
</div>
)}
{currentStep.id === 'calendly' && (
<div className="text-center space-y-4">
<div className="w-16 h-16 mx-auto rounded-full flex items-center justify-center text-2xl" style={{ backgroundColor: '#006BFF' }}>πŸ“…</div>
<p className="text-sm" style={{ color: '#71717a' }}>With {formData.teamSize} people, we'd love to give you a personalized demo.</p>
<button className="w-full py-3 rounded-xl text-sm font-medium" style={{ backgroundColor: '#006BFF', color: '#fff' }}>Book a 25-min Call</button>
</div>
)}
{currentStep.id === 'download' && (
<div className="text-center space-y-4">
<div className="w-20 h-20 mx-auto rounded-2xl flex items-center justify-center text-3xl" style={{ backgroundColor: '#141419' }}>πŸŽ‰</div>
<p className="text-sm" style={{ color: '#a1a1aa' }}>Your {tierNames[tier] || 'Pro'} account is ready!</p>
<button className="w-full py-3 rounded-xl text-sm font-medium flex items-center justify-center gap-2" style={{ backgroundColor: tierColor, color: '#fff' }}>
⬇ Download for Mac
</button>
<a href="https://app.rize.io/signin" target="_blank" rel="noopener noreferrer" className="w-full py-3 rounded-xl text-sm font-medium block text-center" style={{ backgroundColor: '#141419', color: '#d4d4d8', border: '1px solid rgba(255,255,255,0.1)' }}>
Skip to Web App β†’
</a>
{/* Demo option for 6+ users */}
{(formData.teamSize === '6-10' || formData.teamSize === '11-50') && (
<div className="pt-4 mt-4" style={{ borderTop: '1px solid rgba(255,255,255,0.1)' }}>
<p className="text-xs mb-3" style={{ color: '#71717a' }}>Want help getting your team set up?</p>
<button className="w-full py-3 rounded-xl text-sm font-medium flex items-center justify-center gap-2" style={{ backgroundColor: '#006BFF', color: '#fff' }}>
πŸ“… Schedule Onboarding Call
</button>
</div>
)}
</div>
)}
{/* Navigation */}
{!['email', 'download', 'calendly', 'invite', 'phone'].includes(currentStep.id) && (
<button onClick={nextStep} disabled={(currentStep.id === 'name' && !formData.name) || (currentStep.id === 'role' && !formData.role) || (currentStep.id === 'teamSize' && !formData.teamSize)} className="w-full py-3 rounded-full text-sm font-medium mt-6 disabled:opacity-50" style={{ backgroundColor: tierColor, color: '#fff' }}>
Continue
</button>
)}
{currentStep.id === 'invite' && (
<button onClick={nextStep} className="w-full py-3 rounded-full text-sm font-medium mt-4" style={{ backgroundColor: tierColor, color: '#fff' }}>
{formData.teamEmails[0] ? 'Send Invites & Continue' : 'Continue'}
</button>
)}
</div>
</div>
{/* Footer */}
<div className="px-6 py-4" style={{ borderTop: '1px solid rgba(255,255,255,0.1)' }}>
<div className="flex items-center justify-between">
<button onClick={prevStep} className="px-4 py-2 rounded-lg text-sm" style={{ backgroundColor: '#1a1a1f', color: '#d4d4d8', border: '1px solid rgba(255,255,255,0.1)' }}>β€Ή Back</button>
{['phone', 'teamTools', 'goals'].includes(currentStep.id) && (
<button onClick={nextStep} className="px-4 py-2 rounded-lg text-sm" style={{ backgroundColor: '#1a1a1f', color: '#71717a', border: '1px solid rgba(255,255,255,0.1)' }}>Skip β€Ί</button>
)}
</div>
</div>
</div>
);
}
// ==========================================
// DEMO MODAL
// ==========================================
function DemoModal({ onClose }) {
const [selectedDate, setSelectedDate] = useState(29);
const [selectedTime, setSelectedTime] = useState(null);
const benefits = [
{ title: 'Accurate, automatic time tracking', desc: 'Stop chasing timesheets.' },
{ title: 'Real-time visibility', desc: 'See where time is going.' },
{ title: 'Profitability at a glance', desc: 'Instant utilization insights.' },
{ title: 'Automated reports', desc: 'Save hours every week.' },
{ title: 'Privacy-first tracking', desc: 'No screenshots or surveillance.' },
];
const times = ['9:00am', '9:30am', '10:00am', '10:30am', '11:00am', '1:00pm', '1:30pm', '2:00pm'];
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4" style={{ backgroundColor: 'rgba(0,0,0,0.8)' }} onClick={onClose}>
<div className="relative w-full max-w-5xl max-h-[90vh] overflow-auto rounded-2xl" style={{ backgroundColor: '#111113' }} onClick={e => e.stopPropagation()}>
<button onClick={onClose} className="absolute top-4 right-4 z-10 w-8 h-8 rounded-full flex items-center justify-center" style={{ backgroundColor: 'rgba(255,255,255,0.1)', color: '#fff' }}>βœ•</button>
<div className="p-6 text-center" style={{ background: 'linear-gradient(180deg, rgba(139,92,246,0.15) 0%, transparent 100%)' }}>
<p className="text-sm mb-2" style={{ color: '#a1a1aa' }}>For Agency Owners & Operations Leaders</p>
<h2 className="text-3xl font-bold text-white">Unlock Accurate Time & Profitability Data</h2>
</div>
<div className="grid md:grid-cols-2 gap-8 p-8">
<div>
<h3 className="text-xl font-semibold text-white mb-4">What to expect</h3>
<p className="text-sm mb-6" style={{ color: '#71717a' }}>In this 25-minute session, we'll walk through your specific needs.</p>
<div className="space-y-4">
{benefits.map((b, i) => (
<div key={i} className="flex gap-3">
<svg className="w-5 h-5 flex-shrink-0" viewBox="0 0 20 20" fill="none"><path d="M16.667 5L7.5 14.167L3.333 10" stroke="#8B5CF6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>
<div>
<h4 className="text-sm font-medium text-white">{b.title}</h4>
<p className="text-xs" style={{ color: '#71717a' }}>{b.desc}</p>
</div>
</div>
))}
</div>
</div>
<div className="rounded-xl p-6" style={{ backgroundColor: '#fff', color: '#111' }}>
<h3 className="text-xl font-bold mb-2">Book a call with Macgill!</h3>
<p className="text-sm mb-4" style={{ color: '#666' }}>πŸ‘‹ Hi, I'm the CEO. Book a time on my calendar.</p>
<div className="rounded-lg border p-4 mb-4" style={{ borderColor: '#e5e5e5' }}>
<h4 className="font-semibold">Rize Team Demo</h4>
<p className="text-xs" style={{ color: '#666' }}>25 min β€’ Web conferencing</p>
</div>
<h4 className="font-semibold text-center mb-3">Select a Day - January 2026</h4>
<div className="grid grid-cols-7 gap-1 text-center text-xs mb-4">
{['S', 'M', 'T', 'W', 'T', 'F', 'S'].map((d, i) => <div key={i} style={{ color: '#999' }}>{d}</div>)}
{[...Array(4)].map((_, i) => <div key={`e${i}`} />)}
{[...Array(31)].map((_, i) => (
<button key={i} onClick={() => setSelectedDate(i + 1)} className="w-8 h-8 rounded-full mx-auto flex items-center justify-center" style={{ backgroundColor: selectedDate === i + 1 ? '#006BFF' : 'transparent', color: selectedDate === i + 1 ? '#fff' : i < 27 ? '#ccc' : '#111' }} disabled={i < 27}>
{i + 1}
</button>
))}
</div>
{selectedDate && (
<div>
<h4 className="font-semibold text-center mb-3">Select a Time</h4>
<div className="grid grid-cols-3 gap-2">
{times.map(time => (
<button key={time} onClick={() => setSelectedTime(time)} className="py-2 px-3 rounded-lg text-sm" style={{ backgroundColor: selectedTime === time ? '#006BFF' : '#f5f5f5', color: selectedTime === time ? '#fff' : '#111' }}>
{time}
</button>
))}
</div>
{selectedTime && (
<button className="w-full mt-4 py-3 rounded-lg text-sm font-semibold" style={{ backgroundColor: '#006BFF', color: '#fff' }}>
Confirm - Jan {selectedDate}, {selectedTime}
</button>
)}
</div>
)}
</div>
</div>
</div>
</div>
);
}
// Render
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment