Created
January 28, 2026 20:58
-
-
Save jonathanpeterwu/111a8e4d8b700c86ed807c0cb1fa5cb5 to your computer and use it in GitHub Desktop.
proposal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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