Light theme + reposition as AI grammar tool #4017
2 changed files+209−233
Modifiedapp/globals.css+35−29View fileUnifiedSplit
@@ -3,19 +3,22 @@
33@tailwind utilities;
44
55:root {
6 --bg: #09090b;
7 --bg-subtle: #111113;
8 --border: rgba(255, 255, 255, 0.06);
9 --text-primary: rgba(255, 255, 255, 0.92);
10 --text-secondary: rgba(255, 255, 255, 0.55);
11 --text-muted: rgba(255, 255, 255, 0.3);
12 --text-faint: rgba(255, 255, 255, 0.12);
13 --accent: #6366f1; /* indigo — modern, not neon */
14 --accent-light: #818cf8;
15 --accent-glow: rgba(99, 102, 241, 0.15);
16 --success: #22c55e;
17 --danger: #ef4444;
18 --warning: #eab308;
6 /* Light, clean palette */
7 --bg: #ffffff;
8 --bg-subtle: #f8f9fa;
9 --bg-muted: #f1f3f5;
10 --border: rgba(0, 0, 0, 0.08);
11 --border-strong: rgba(0, 0, 0, 0.12);
12 --text-primary: #111111;
13 --text-secondary: #555555;
14 --text-muted: #888888;
15 --text-faint: #bbbbbb;
16 --accent: #4f46e5; /* indigo */
17 --accent-light: #6366f1;
18 --accent-bg: rgba(79, 70, 229, 0.06);
19 --success: #16a34a;
20 --danger: #dc2626;
21 --warning: #ca8a04;
1922}
2023
2124* {
@@ -26,12 +29,13 @@
2629
2730body {
2831 background: var(--bg);
32 color: var(--text-primary);
2933 font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif;
3034 -webkit-font-smoothing: antialiased;
3135}
3236
33/* ── Extension ring states ────────────────────────────────── */
34.ring-idle { box-shadow: 0 0 0 2px rgba(255,255,255,0.15); }
37/* ── Extension ring states (dark overlay context) ─────── */
38.ring-idle { box-shadow: 0 0 0 2px rgba(0,0,0,0.15); }
3539.ring-record { box-shadow: 0 0 0 2px var(--danger), 0 0 16px var(--danger); animation: pulse_ring 1.2s ease-in-out infinite; }
3640.ring-process { box-shadow: 0 0 0 2px var(--warning), 0 0 12px var(--warning); }
3741.ring-done { box-shadow: 0 0 0 2px var(--success), 0 0 12px var(--success); }
@@ -41,7 +45,7 @@ body {
4145 50% { box-shadow: 0 0 0 4px var(--danger), 0 0 28px var(--danger); }
4246}
4347
44/* ── Waveform bars ────────────────────────────────────────── */
48/* ── Waveform bars ────────────────────────────────────── */
4549@keyframes bar1 { 0%,100%{height:6px} 50%{height:22px} }
4650@keyframes bar2 { 0%,100%{height:10px} 50%{height:30px} }
4751@keyframes bar3 { 0%,100%{height:4px} 50%{height:26px} }
@@ -53,9 +57,9 @@ body {
5357.bar4 { animation: bar4 1.0s ease-in-out infinite 0.05s; }
5458.bar5 { animation: bar5 0.75s ease-in-out infinite 0.15s; }
5559
56/* ── Animations ───────────────────────────────────────────── */
60/* ── Animations ───────────────────────────────────────── */
5761@keyframes fade-up {
58 from { opacity: 0; transform: translateY(20px); }
62 from { opacity: 0; transform: translateY(16px); }
5963 to { opacity: 1; transform: translateY(0); }
6064}
6165
@@ -64,14 +68,14 @@ body {
6468 51%, 100% { border-right-color: transparent; }
6569}
6670
67.animate-fade-up { animation: fade-up 0.6s ease-out both; }
71.animate-fade-up { animation: fade-up 0.5s ease-out both; }
6872.animate-typing-cursor { border-right: 2px solid var(--accent); animation: typing-cursor 1s step-end infinite; }
6973
7074/* Scroll reveal */
7175.reveal {
7276 opacity: 0;
73 transform: translateY(24px);
74 transition: opacity 0.7s ease, transform 0.7s ease;
77 transform: translateY(20px);
78 transition: opacity 0.6s ease, transform 0.6s ease;
7579}
7680.reveal.visible {
7781 opacity: 1;
@@ -80,24 +84,26 @@ body {
8084
8185/* Card */
8286.card {
83 background: var(--bg-subtle);
87 background: var(--bg);
8488 border: 1px solid var(--border);
85 border-radius: 16px;
89 border-radius: 14px;
90 transition: border-color 0.2s, box-shadow 0.2s;
8691}
8792.card:hover {
88 border-color: rgba(255, 255, 255, 0.1);
93 border-color: var(--border-strong);
94 box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
8995}
9096
91/* Subtle gradient mesh (hero only) */
97/* Hero gradient — subtle warm */
9298.hero-gradient {
9399 background:
94 radial-gradient(ellipse 80% 50% at 50% -20%, rgba(99, 102, 241, 0.12), transparent),
95 radial-gradient(ellipse 60% 40% at 80% 60%, rgba(34, 197, 94, 0.06), transparent),
100 radial-gradient(ellipse 70% 50% at 50% 0%, rgba(79, 70, 229, 0.06), transparent),
101 radial-gradient(ellipse 50% 30% at 80% 20%, rgba(16, 163, 74, 0.04), transparent),
96102 var(--bg);
97103}
98104
99105/* Clean scrollbar */
100106::-webkit-scrollbar { width: 6px; }
101107::-webkit-scrollbar-track { background: transparent; }
102::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; }
103::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.15); }
108::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.1); border-radius: 3px; }
109::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.2); }
Modifiedapp/page.jsx+174−204View fileUnifiedSplit
@@ -3,69 +3,55 @@
33import { useState, useRef, useEffect } from 'react'
44import Waveform from '../components/Waveform'
55
6// ── Typing demo ──────────────────────────────────────────
6// ── Typing demo — shows AI rewrite in action ─────────────
77function useTypingDemo() {
88 const phrases = [
99 {
10 before: 'uh so like I think we should maybe redo the the dashboard because users are confused',
11 after: 'I recommend redesigning the dashboard to improve user clarity and navigation.',
12 label: 'AI Rewrite',
10 before: 'uh so like I think we should probly redo the the dashboard cos users are confused about where to click',
11 after: 'I think we should redesign the dashboard — users are confused about where to click.',
12 label: 'AI Grammar',
1313 },
1414 {
15 before: 'Hey Claude comma can you refactor this function to use async await question mark',
16 after: 'Hey Claude, can you refactor this function to use async/await?',
17 label: 'Voice Punctuation',
15 before: 'Hey can u send me the report asap i need it for the meeting tmrw thx',
16 after: 'Hey, can you send me the report ASAP? I need it for the meeting tomorrow. Thanks!',
17 label: 'Email Polish',
1818 },
1919 {
20 before: 'send an email to the team new line we ship tomorrow exclamation point',
21 after: 'Send an email to the team\nWe ship tomorrow!',
22 label: 'Natural Commands',
20 before: 'the function should take an array of numbers and return only the ones that are greater then 10',
21 after: 'The function should take an array of numbers and return only the ones that are greater than 10.',
22 label: 'Technical Writing',
2323 },
2424 ]
2525
2626 const [idx, setIdx] = useState(0)
2727 const [charIdx, setCharIdx] = useState(0)
28 const [phase, setPhase] = useState('typing') // typing | pause | next
29
28 const [phase, setPhase] = useState('typing')
3029 const phrase = phrases[idx]
3130
3231 useEffect(() => {
3332 if (phase === 'typing' && charIdx < phrase.after.length) {
34 const t = setTimeout(() => setCharIdx(c => c + 1), 30 + Math.random() * 20)
33 const t = setTimeout(() => setCharIdx(c => c + 1), 25 + Math.random() * 15)
3534 return () => clearTimeout(t)
3635 }
3736 if (phase === 'typing' && charIdx >= phrase.after.length) {
3837 setPhase('pause')
3938 const t = setTimeout(() => {
4039 setPhase('next')
41 setTimeout(() => {
42 setIdx(i => (i + 1) % phrases.length)
43 setCharIdx(0)
44 setPhase('typing')
45 }, 400)
46 }, 2500)
40 setTimeout(() => { setIdx(i => (i + 1) % phrases.length); setCharIdx(0); setPhase('typing') }, 400)
41 }, 2800)
4742 return () => clearTimeout(t)
4843 }
4944 }, [charIdx, phase, phrase.after.length, idx, phrases.length])
5045
51 return {
52 before: phrase.before,
53 after: phrase.after.slice(0, charIdx),
54 label: phrase.label,
55 isTyping: phase === 'typing' && charIdx < phrase.after.length,
56 }
46 return { before: phrase.before, after: phrase.after.slice(0, charIdx), label: phrase.label, isTyping: phase === 'typing' && charIdx < phrase.after.length }
5747}
5848
59// ── Scroll reveal ────────────────────────────────────────
6049function useReveal() {
6150 const ref = useRef(null)
6251 useEffect(() => {
6352 const el = ref.current
6453 if (!el) return
65 const observer = new IntersectionObserver(
66 ([entry]) => { if (entry.isIntersecting) el.classList.add('visible') },
67 { threshold: 0.1 }
68 )
54 const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) el.classList.add('visible') }, { threshold: 0.1 })
6955 observer.observe(el)
7056 return () => observer.disconnect()
7157 }, [])
@@ -77,228 +63,215 @@ export default function LandingPage() {
7763 const [demoStatus, setDemoStatus] = useState('idle')
7864 const [demoTranscript, setDemoTranscript] = useState('')
7965 const recognitionRef = useRef(null)
66 const r1 = useReveal(), r2 = useReveal(), r3 = useReveal(), r4 = useReveal(), r5 = useReveal()
8067
81 const r1 = useReveal()
82 const r2 = useReveal()
83 const r3 = useReveal()
84 const r4 = useReveal()
85
86 useEffect(() => {
87 return () => { if (recognitionRef.current) { recognitionRef.current.abort(); recognitionRef.current = null } }
88 }, [])
68 useEffect(() => { return () => { if (recognitionRef.current) { recognitionRef.current.abort(); recognitionRef.current = null } } }, [])
8969
9070 function handleDemoClick() {
91 if (demoStatus === 'recording') {
92 if (recognitionRef.current) { recognitionRef.current.stop(); recognitionRef.current = null }
93 return
94 }
71 if (demoStatus === 'recording') { if (recognitionRef.current) { recognitionRef.current.stop(); recognitionRef.current = null } return }
9572 const SR = typeof window !== 'undefined' && (window.SpeechRecognition || window.webkitSpeechRecognition)
9673 if (!SR) { setDemoTranscript('Requires Chrome or Edge.'); setDemoStatus('done'); setTimeout(() => setDemoStatus('idle'), 3000); return }
97 const rec = new SR()
98 rec.continuous = true; rec.interimResults = true; rec.lang = 'en'
74 const rec = new SR(); rec.continuous = true; rec.interimResults = true; rec.lang = 'en'
9975 rec.onstart = () => { setDemoStatus('recording'); setDemoTranscript('') }
10076 rec.onresult = (e) => { let f = ''; for (let i = 0; i < e.results.length; i++) f += e.results[i][0].transcript; setDemoTranscript(f) }
10177 rec.onend = () => { setDemoStatus('done'); setTimeout(() => setDemoStatus('idle'), 2500) }
102 rec.onerror = (e) => {
103 if (e.error === 'not-allowed') setDemoTranscript('Microphone access denied.')
104 else if (e.error === 'no-speech') setDemoTranscript('No speech detected.')
105 setDemoStatus('done'); setTimeout(() => setDemoStatus('idle'), 3000)
106 }
78 rec.onerror = (e) => { if (e.error === 'not-allowed') setDemoTranscript('Mic access denied.'); else if (e.error === 'no-speech') setDemoTranscript('No speech detected.'); setDemoStatus('done'); setTimeout(() => setDemoStatus('idle'), 3000) }
10779 recognitionRef.current = rec; rec.start()
10880 }
10981
11082 return (
111 <main className="min-h-screen bg-[#09090b] text-white overflow-x-hidden">
83 <main className="min-h-screen bg-white overflow-x-hidden">
11284
113 {/* ── NAV ───────────────────────────────────────────── */}
114 <nav className="fixed top-0 w-full z-50 bg-[#09090b]/80 backdrop-blur-xl border-b border-white/[0.04]">
85 {/* ── NAV ─────────────────────────────────────── */}
86 <nav className="fixed top-0 w-full z-50 bg-white/80 backdrop-blur-xl border-b border-black/[0.04]">
11587 <div className="max-w-6xl mx-auto flex items-center justify-between px-6 py-3">
116 <a href="/" className="text-base font-semibold tracking-tight">
117 <span className="text-white/90">48</span><span className="text-indigo-400">co</span>
88 <a href="/" className="text-base font-bold tracking-tight">
89 48<span className="text-indigo-600">co</span>
11890 </a>
119 <div className="flex items-center gap-6">
120 <a href="/compare" className="text-[13px] text-white/40 hover:text-white/70 transition-colors">Compare</a>
121 <a href="/pricing" className="text-[13px] text-white/40 hover:text-white/70 transition-colors">Pricing</a>
122 <a href="/live" className="text-[13px] text-white/40 hover:text-white/70 transition-colors">Try Live</a>
123 <a href="/download" className="text-[13px] px-4 py-1.5 rounded-lg bg-indigo-500/10 border border-indigo-500/20 text-indigo-400 hover:bg-indigo-500/20 transition-all">
91 <div className="hidden sm:flex items-center gap-6">
92 <a href="/compare" className="text-[13px] text-gray-400 hover:text-gray-700 transition-colors">Compare</a>
93 <a href="/pricing" className="text-[13px] text-gray-400 hover:text-gray-700 transition-colors">Pricing</a>
94 <a href="/live" className="text-[13px] text-gray-400 hover:text-gray-700 transition-colors">Try Live</a>
95 <a href="/download" className="text-[13px] px-4 py-1.5 rounded-lg bg-indigo-600 text-white hover:bg-indigo-500 transition-all">
12496 Download
12597 </a>
12698 </div>
12799 </div>
128100 </nav>
129101
130 {/* ── HERO ──────────────────────────────────────────── */}
131 <section className="hero-gradient pt-32 pb-24 px-4">
102 {/* ── HERO ────────────────────────────────────── */}
103 <section className="hero-gradient pt-32 pb-20 px-4">
132104 <div className="max-w-3xl mx-auto text-center">
133 <div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-indigo-500/10 border border-indigo-500/15 mb-8 animate-fade-up">
134 <span className="w-1.5 h-1.5 rounded-full bg-green-400"></span>
135 <span className="text-[12px] text-indigo-300/70">Now with AI Rewrite Mode</span>
105
106 <div className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-indigo-50 border border-indigo-100 mb-8 animate-fade-up">
107 <span className="w-1.5 h-1.5 rounded-full bg-green-500"></span>
108 <span className="text-[12px] text-indigo-600 font-medium">AI Grammar + Voice-to-Text</span>
136109 </div>
137110
138 <h1 className="text-5xl md:text-7xl font-bold tracking-tight mb-6 animate-fade-up" style={{ animationDelay: '0.1s' }}>
139 Speak naturally.
111 <h1 className="text-5xl md:text-6xl lg:text-7xl font-bold tracking-tight text-gray-900 mb-6 animate-fade-up" style={{ animationDelay: '0.1s' }}>
112 Everything you write,
140113 <br />
141 <span className="bg-gradient-to-r from-indigo-400 to-emerald-400 bg-clip-text text-transparent">Get perfect text.</span>
114 <span className="text-indigo-600">perfected by AI.</span>
142115 </h1>
143116
144 <p className="text-lg text-white/45 max-w-xl mx-auto leading-relaxed mb-10 animate-fade-up" style={{ animationDelay: '0.2s' }}>
145 Voice-to-text that types into any app. AI polishes your words automatically. Works on Mac and Windows.
117 <p className="text-lg md:text-xl text-gray-400 max-w-xl mx-auto leading-relaxed mb-10 animate-fade-up" style={{ animationDelay: '0.2s' }}>
118 Speak or type — 48co fixes your grammar, polishes your tone, and types it perfectly. On every device, in every app.
146119 </p>
147120
148121 <div className="flex flex-col sm:flex-row gap-3 justify-center mb-16 animate-fade-up" style={{ animationDelay: '0.3s' }}>
149 <a href="/download" className="px-8 py-3 rounded-xl bg-indigo-500 hover:bg-indigo-400 text-white text-sm font-medium transition-all">
122 <a href="/download" className="px-8 py-3 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white text-[15px] font-medium transition-all shadow-sm">
150123 Download Free
151124 </a>
152 <a href="/live" className="px-8 py-3 rounded-xl border border-white/10 text-white/50 text-sm font-medium hover:border-white/20 hover:text-white/70 transition-all">
125 <a href="/live" className="px-8 py-3 rounded-xl border border-gray-200 text-gray-500 text-[15px] font-medium hover:border-gray-300 hover:text-gray-700 transition-all">
153126 Try in Browser
154127 </a>
155128 </div>
156129
157 {/* ── Demo Card ──────────────────────────────── */}
130 {/* ── Demo Card ──────────────────────── */}
158131 <div className="max-w-lg mx-auto animate-fade-up" style={{ animationDelay: '0.4s' }}>
159 <div className="card overflow-hidden">
160 <div className="flex items-center justify-between px-5 py-3 border-b border-white/[0.04]">
132 <div className="card overflow-hidden shadow-lg shadow-black/[0.03]">
133 <div className="flex items-center justify-between px-5 py-3 border-b border-black/[0.04] bg-gray-50/50">
161134 <div className="flex items-center gap-2">
162135 <div className="flex gap-1.5">
163 <div className="w-2 h-2 rounded-full bg-white/10" />
164 <div className="w-2 h-2 rounded-full bg-white/10" />
165 <div className="w-2 h-2 rounded-full bg-white/10" />
136 <div className="w-2.5 h-2.5 rounded-full bg-red-400/60" />
137 <div className="w-2.5 h-2.5 rounded-full bg-amber-400/60" />
138 <div className="w-2.5 h-2.5 rounded-full bg-green-400/60" />
166139 </div>
167 <span className="text-[11px] text-white/20 ml-2 font-mono">{typing.label}</span>
140 <span className="text-[11px] text-gray-400 ml-2 font-mono">{typing.label}</span>
168141 </div>
169 <span className={`w-2 h-2 rounded-full ${typing.isTyping ? 'bg-red-400 shadow-[0_0_6px_rgba(239,68,68,0.5)]' : 'bg-green-400'}`} />
142 <span className={`w-2 h-2 rounded-full ${typing.isTyping ? 'bg-red-400' : 'bg-green-400'}`} />
170143 </div>
171144
172 <div className="px-5 py-3 border-b border-white/[0.04]">
173 <p className="text-[10px] text-white/20 uppercase tracking-wider mb-1">You said</p>
174 <p className="text-[13px] text-white/25 leading-relaxed">{typing.before}</p>
145 <div className="px-5 py-3 border-b border-black/[0.04]">
146 <p className="text-[10px] text-gray-300 uppercase tracking-wider mb-1.5">You wrote</p>
147 <p className="text-[13px] text-gray-400 leading-relaxed line-through decoration-red-300/40">{typing.before}</p>
175148 </div>
176149
177 <div className="px-5 py-4 bg-indigo-500/[0.03]">
178 <p className="text-[10px] text-indigo-400/40 uppercase tracking-wider mb-1">48co types</p>
179 <p className={`text-[14px] text-white/75 leading-relaxed whitespace-pre-wrap min-h-[44px] ${typing.isTyping ? 'animate-typing-cursor pr-0.5' : ''}`}>
180 {typing.after || <span className="text-white/10">|</span>}
150 <div className="px-5 py-4 bg-indigo-50/30">
151 <p className="text-[10px] text-indigo-400 uppercase tracking-wider mb-1.5">48co corrects to</p>
152 <p className={`text-[14px] text-gray-800 leading-relaxed whitespace-pre-wrap min-h-[44px] ${typing.isTyping ? 'animate-typing-cursor pr-0.5' : ''}`}>
153 {typing.after || <span className="text-gray-200">|</span>}
181154 </p>
182155 </div>
183156 </div>
184157 </div>
185158
186 <p className="text-[11px] text-white/15 mt-6 animate-fade-up" style={{ animationDelay: '0.5s' }}>
187 Mac + Windows · Free tier available · No account required
159 <p className="text-[12px] text-gray-300 mt-8 animate-fade-up" style={{ animationDelay: '0.5s' }}>
160 Mac · Windows · iPhone · Chrome · Free to start
188161 </p>
189162 </div>
190163 </section>
191164
192 {/* ── HOW IT WORKS ─────────────────────────────────── */}
193 <section ref={r1} className="reveal py-24 px-4">
165 {/* ── WORKS EVERYWHERE ───────────────────────── */}
166 <section ref={r1} className="reveal py-24 px-4 bg-gray-50">
194167 <div className="max-w-5xl mx-auto">
195168 <div className="text-center mb-16">
196 <h2 className="text-3xl font-bold text-white/90 mb-3">How it works</h2>
197 <p className="text-sm text-white/35">Three steps. No configuration.</p>
169 <h2 className="text-3xl font-bold text-gray-900 mb-3">Works everywhere you write</h2>
170 <p className="text-[15px] text-gray-400">One tool for every device and every app.</p>
198171 </div>
199172
200 <div className="grid md:grid-cols-3 gap-6">
173 <div className="grid md:grid-cols-4 gap-4">
201174 {[
202 { step: '1', title: 'Press the hotkey', desc: 'Ctrl+Shift+Space (Cmd on Mac). Works from any app — browser, Slack, email, code editor, anything.' },
203 { step: '2', title: 'Speak naturally', desc: 'Talk like you normally would. Say punctuation out loud or let AI figure it out. Supports 50+ languages.' },
204 { step: '3', title: 'Text appears instantly', desc: 'AI rewrites your words into clean, professional text and types it directly into the focused field.' },
205 ].map((s) => (
206 <div key={s.step} className="card p-6">
207 <div className="w-8 h-8 rounded-lg bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center text-indigo-400 text-sm font-semibold mb-4">
208 {s.step}
209 </div>
210 <h3 className="text-[15px] font-semibold text-white/85 mb-2">{s.title}</h3>
211 <p className="text-[13px] text-white/35 leading-relaxed">{s.desc}</p>
175 { icon: '💻', title: 'Desktop App', desc: 'Mac + Windows. Types into any app with a global hotkey.', status: 'Available' },
176 { icon: '🌐', title: 'Chrome Extension', desc: 'Grammar check on any website. Gmail, Slack, Docs, everything.', status: 'Available' },
177 { icon: '📱', title: 'iPhone & iPad', desc: 'Custom keyboard that corrects as you type.', status: 'Coming soon' },
178 { icon: '🤖', title: 'Android', desc: 'Custom keyboard for Samsung, Pixel, and all Android.', status: 'Coming soon' },
179 ].map((p) => (
180 <div key={p.title} className="card p-5 text-center">
181 <span className="text-3xl mb-3 block">{p.icon}</span>
182 <h3 className="text-[14px] font-semibold text-gray-800 mb-1">{p.title}</h3>
183 <p className="text-[12px] text-gray-400 leading-relaxed mb-3">{p.desc}</p>
184 <span className={`text-[10px] px-2 py-0.5 rounded-full ${
185 p.status === 'Available' ? 'bg-green-50 text-green-600 border border-green-200' : 'bg-gray-100 text-gray-400 border border-gray-200'
186 }`}>{p.status}</span>
212187 </div>
213188 ))}
214189 </div>
215190 </div>
216191 </section>
217192
218 {/* ── KEY FEATURES ──────────────────────────────────── */}
219 <section ref={r2} className="reveal py-24 px-4 bg-[#0c0c0f]">
193 {/* ── KEY FEATURES ──────────────────────────── */}
194 <section ref={r2} className="reveal py-24 px-4">
220195 <div className="max-w-5xl mx-auto">
221196 <div className="text-center mb-16">
222 <h2 className="text-3xl font-bold text-white/90 mb-3">What makes 48co different</h2>
223 <p className="text-sm text-white/35">Features no other dictation tool has.</p>
197 <h2 className="text-3xl font-bold text-gray-900 mb-3">More than just grammar</h2>
198 <p className="text-[15px] text-gray-400">AI that understands what you mean, not just what you wrote.</p>
224199 </div>
225200
226 <div className="grid md:grid-cols-2 gap-5">
201 <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5">
227202 {[
228 {
229 title: 'AI Rewrite Mode',
230 desc: 'You ramble, it writes professionally. Claude AI removes filler words, fixes grammar, and adjusts tone automatically. No other dictation tool does this.',
231 tag: 'Exclusive',
232 tagColor: 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20',
233 },
234 {
235 title: 'Context-Aware',
236 desc: 'Automatically detects which app you\'re in. Professional tone for email, casual for Slack, technical for code editors. Zero configuration.',
237 tag: 'Exclusive',
238 tagColor: 'text-indigo-400 bg-indigo-500/10 border-indigo-500/20',
239 },
240 {
241 title: 'Works Everywhere',
242 desc: 'Types into any focused text field on your computer. Browsers, Slack, Discord, VS Code, Word, email — anything. Not limited to the browser.',
243 tag: 'Desktop',
244 tagColor: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
245 },
246 {
247 title: 'Privacy First',
248 desc: 'Offline mode coming soon with local Whisper. Your voice never has to leave your computer. No data stored on our servers.',
249 tag: 'Local-first',
250 tagColor: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
251 },
252 {
253 title: 'Auto Code Detection',
254 desc: 'Detects coding keywords and wraps in markdown fences with language detection. Perfect for ChatGPT, Claude, and GitHub.',
255 tag: 'Developers',
256 tagColor: 'text-amber-400 bg-amber-500/10 border-amber-500/20',
257 },
258 {
259 title: '50+ Languages',
260 desc: 'Powered by OpenAI Whisper for near-perfect accuracy in over 50 languages. English, Spanish, French, German, Japanese, and many more.',
261 tag: 'Global',
262 tagColor: 'text-white/40 bg-white/5 border-white/10',
263 },
203 { title: 'AI Grammar Fix', desc: 'Catches every error — spelling, grammar, punctuation, word choice. Powered by Claude AI, not basic rules.', color: 'text-indigo-600 bg-indigo-50' },
204 { title: 'Tone Adjustment', desc: 'Automatically adjusts tone based on context. Professional for email, casual for Slack, technical for code.', color: 'text-emerald-600 bg-emerald-50' },
205 { title: 'Voice-to-Text', desc: 'Press a hotkey, speak naturally, get perfect text. 99%+ accuracy with OpenAI Whisper in 50+ languages.', color: 'text-blue-600 bg-blue-50' },
206 { title: 'Real-time Polish', desc: 'Works as you type, not after. See corrections inline before you hit send.', color: 'text-amber-600 bg-amber-50' },
207 { title: 'Works Offline', desc: 'Local AI model for privacy-sensitive work. Your text never has to leave your device.', color: 'text-purple-600 bg-purple-50' },
208 { title: 'Developer Mode', desc: 'Auto-detects code and wraps in markdown. Voice commands for refactoring, debugging, and testing.', color: 'text-rose-600 bg-rose-50' },
264209 ].map((f) => (
265 <div key={f.title} className="card p-6 transition-all">
266 <div className="flex items-center justify-between mb-3">
267 <h3 className="text-[15px] font-semibold text-white/85">{f.title}</h3>
268 <span className={`text-[10px] px-2 py-0.5 rounded-full border ${f.tagColor}`}>{f.tag}</span>
210 <div key={f.title} className="card p-6">
211 <h3 className="text-[15px] font-semibold text-gray-800 mb-2">{f.title}</h3>
212 <p className="text-[13px] text-gray-400 leading-relaxed">{f.desc}</p>
213 </div>
214 ))}
215 </div>
216 </div>
217 </section>
218
219 {/* ── BEFORE/AFTER ──────────────────────────── */}
220 <section ref={r3} className="reveal py-24 px-4 bg-gray-50">
221 <div className="max-w-3xl mx-auto">
222 <div className="text-center mb-16">
223 <h2 className="text-3xl font-bold text-gray-900 mb-3">See the difference</h2>
224 <p className="text-[15px] text-gray-400">Real examples of 48co improving everyday writing.</p>
225 </div>
226
227 <div className="space-y-4">
228 {[
229 { before: 'hey can u send me the report i need it for tmrw thx', after: 'Hey, can you send me the report? I need it for tomorrow. Thanks!' },
230 { before: 'i thinkt he main issue is that there server is not responding properly and we need too fix it asap', after: 'I think the main issue is that their server is not responding properly, and we need to fix it ASAP.' },
231 { before: 'the product is great but i think we should of added more features before launching it was to early', after: 'The product is great, but I think we should have added more features before launching — it was too early.' },
232 ].map((ex, i) => (
233 <div key={i} className="card overflow-hidden">
234 <div className="grid md:grid-cols-2">
235 <div className="p-5 border-b md:border-b-0 md:border-r border-black/[0.04]">
236 <p className="text-[10px] text-red-400 uppercase tracking-wider mb-2 font-medium">Before</p>
237 <p className="text-[13px] text-gray-400 leading-relaxed">{ex.before}</p>
238 </div>
239 <div className="p-5 bg-green-50/30">
240 <p className="text-[10px] text-green-600 uppercase tracking-wider mb-2 font-medium">After 48co</p>
241 <p className="text-[13px] text-gray-800 leading-relaxed">{ex.after}</p>
242 </div>
269243 </div>
270 <p className="text-[13px] text-white/35 leading-relaxed">{f.desc}</p>
271244 </div>
272245 ))}
273246 </div>
274247 </div>
275248 </section>
276249
277 {/* ── LIVE DEMO ─────────────────────────────────────── */}
278 <section ref={r3} className="reveal py-24 px-4">
250 {/* ── LIVE DEMO ─────────────────────────────── */}
251 <section ref={r4} className="reveal py-24 px-4">
279252 <div className="max-w-lg mx-auto text-center">
280 <h2 className="text-3xl font-bold text-white/90 mb-3">Try it now</h2>
281 <p className="text-sm text-white/35 mb-10">Click the mic, speak, see your words. Runs in your browser — no download needed.</p>
282
283 <div className="card overflow-hidden">
284 <div className="flex items-center justify-between px-5 py-3 border-b border-white/[0.04]">
285 <span className="text-[12px] font-medium text-white/40">Live Demo</span>
286 <span className={`text-[10px] px-2 py-0.5 rounded-full border transition-all ${
287 demoStatus === 'recording' ? 'border-red-500/30 text-red-400 bg-red-500/10' :
288 demoStatus === 'done' ? 'border-green-500/30 text-green-400 bg-green-500/10' :
289 'border-white/10 text-white/25'
253 <h2 className="text-3xl font-bold text-gray-900 mb-3">Try voice-to-text now</h2>
254 <p className="text-[15px] text-gray-400 mb-10">Click the mic and speak. Runs in your browser.</p>
255
256 <div className="card overflow-hidden shadow-lg shadow-black/[0.04]">
257 <div className="flex items-center justify-between px-5 py-3 border-b border-black/[0.04] bg-gray-50/50">
258 <span className="text-[12px] font-medium text-gray-500">Live Demo</span>
259 <span className={`text-[10px] px-2 py-0.5 rounded-full border ${
260 demoStatus === 'recording' ? 'border-red-200 text-red-500 bg-red-50' :
261 demoStatus === 'done' ? 'border-green-200 text-green-600 bg-green-50' :
262 'border-gray-200 text-gray-400'
290263 }`}>
291 {demoStatus === 'recording' ? 'Listening' : demoStatus === 'done' ? 'Done' : 'Ready'}
264 {demoStatus === 'recording' ? 'Listening...' : demoStatus === 'done' ? 'Done' : 'Ready'}
292265 </span>
293266 </div>
294267
295 <div className="py-4 border-b border-white/[0.04]">
268 <div className="py-4 border-b border-black/[0.04]">
296269 <Waveform isRecording={demoStatus === 'recording'} />
297270 </div>
298271
299272 {demoTranscript && (
300 <div className="px-5 py-3 border-b border-white/[0.04]">
301 <p className="text-[13px] text-white/50 leading-relaxed">{demoTranscript}</p>
273 <div className="px-5 py-3 border-b border-black/[0.04] bg-indigo-50/30">
274 <p className="text-[13px] text-gray-700 leading-relaxed">{demoTranscript}</p>
302275 </div>
303276 )}
304277
@@ -306,21 +279,21 @@ export default function LandingPage() {
306279 <button
307280 onClick={handleDemoClick}
308281 className={`w-14 h-14 rounded-full flex items-center justify-center transition-all cursor-pointer border-2 ${
309 demoStatus === 'recording' ? 'border-red-500 bg-red-500/10 shadow-[0_0_20px_rgba(239,68,68,0.3)]' :
310 demoStatus === 'done' ? 'border-green-500 bg-green-500/10' :
311 'border-white/15 bg-white/[0.03] hover:border-white/25'
282 demoStatus === 'recording' ? 'border-red-400 bg-red-50 shadow-[0_0_20px_rgba(220,38,38,0.15)]' :
283 demoStatus === 'done' ? 'border-green-400 bg-green-50' :
284 'border-gray-200 bg-gray-50 hover:border-gray-300'
312285 }`}
313286 >
314287 {demoStatus === 'recording' ? (
315 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="1.5"><path d="M2 12h2M6 8v8M10 5v14M14 9v6M18 7v10M22 12h-2"/></svg>
288 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#dc2626" strokeWidth="1.5"><path d="M2 12h2M6 8v8M10 5v14M14 9v6M18 7v10M22 12h-2"/></svg>
316289 ) : demoStatus === 'done' ? (
317 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2"><path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round"/></svg>
290 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#16a34a" strokeWidth="2"><path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round"/></svg>
318291 ) : (
319 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.4)" strokeWidth="1.5"><rect x="9" y="2" width="6" height="11" rx="3"/><path d="M5 10a7 7 0 0014 0"/><line x1="12" y1="21" x2="12" y2="17"/><line x1="9" y1="21" x2="15" y2="21"/></svg>
292 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#888" strokeWidth="1.5"><rect x="9" y="2" width="6" height="11" rx="3"/><path d="M5 10a7 7 0 0014 0"/><line x1="12" y1="21" x2="12" y2="17"/><line x1="9" y1="21" x2="15" y2="21"/></svg>
320293 )}
321294 </button>
322295 <span className={`text-[12px] ${
323 demoStatus === 'recording' ? 'text-red-400' : demoStatus === 'done' ? 'text-green-400' : 'text-white/25'
296 demoStatus === 'recording' ? 'text-red-500' : demoStatus === 'done' ? 'text-green-600' : 'text-gray-400'
324297 }`}>
325298 {demoStatus === 'recording' ? 'Click to stop' : demoStatus === 'done' ? 'Done' : 'Click to try'}
326299 </span>
@@ -329,62 +302,59 @@ export default function LandingPage() {
329302 </div>
330303 </section>
331304
332 {/* ── PRICING TEASER ─────────────────────────────────── */}
333 <section ref={r4} className="reveal py-24 px-4 bg-[#0c0c0f]">
305 {/* ── PRICING TEASER ────────────────────────── */}
306 <section ref={r5} className="reveal py-24 px-4 bg-gray-50">
334307 <div className="max-w-3xl mx-auto text-center">
335 <h2 className="text-3xl font-bold text-white/90 mb-3">Simple pricing</h2>
336 <p className="text-sm text-white/35 mb-10">Free tier to get started. Pro when you need AI rewrite.</p>
308 <h2 className="text-3xl font-bold text-gray-900 mb-3">Simple pricing</h2>
309 <p className="text-[15px] text-gray-400 mb-10">Free to start. Upgrade when you need AI grammar.</p>
337310
338311 <div className="grid md:grid-cols-3 gap-4 mb-10">
339312 {[
340 { name: 'Free', price: '$0', desc: 'Basic dictation, 60 min/mo', highlight: false },
341 { name: 'Pro', price: '$12/mo', desc: 'Unlimited + AI Rewrite + Offline', highlight: true },
342 { name: 'Lifetime', price: '$89', desc: 'Pro features forever. Limited.', highlight: false },
313 { name: 'Free', price: '$0', desc: 'Basic dictation and grammar hints', highlight: false },
314 { name: 'Pro', price: '$12/mo', desc: 'Unlimited AI grammar, voice, offline', highlight: true },
315 { name: 'Lifetime', price: '$89', desc: 'Pro forever. Limited offer.', highlight: false },
343316 ].map((p) => (
344 <div key={p.name} className={`card p-6 ${p.highlight ? 'border-indigo-500/30 bg-indigo-500/[0.03]' : ''}`}>
345 <p className="text-sm font-semibold text-white/70 mb-1">{p.name}</p>
346 <p className="text-2xl font-bold text-white/90 mb-2">{p.price}</p>
347 <p className="text-[12px] text-white/30">{p.desc}</p>
317 <div key={p.name} className={`card p-6 ${p.highlight ? 'border-indigo-200 bg-indigo-50/30 shadow-md shadow-indigo-500/5' : ''}`}>
318 <p className="text-[14px] font-semibold text-gray-600 mb-1">{p.name}</p>
319 <p className="text-3xl font-bold text-gray-900 mb-2">{p.price}</p>
320 <p className="text-[12px] text-gray-400">{p.desc}</p>
348321 </div>
349322 ))}
350323 </div>
351324
352 <a href="/pricing" className="text-[13px] text-indigo-400 hover:text-indigo-300 transition-colors">
353 See full pricing details →
325 <a href="/pricing" className="text-[13px] text-indigo-600 hover:text-indigo-500 font-medium transition-colors">
326 See full pricing →
354327 </a>
355328 </div>
356329 </section>
357330
358 {/* ── CTA ───────────────────────────────────────────── */}
331 {/* ── CTA ───────────────────────────────────── */}
359332 <section className="py-24 px-4">
360333 <div className="max-w-2xl mx-auto text-center">
361 <h2 className="text-4xl font-bold text-white/90 mb-4">
362 Stop typing. Start talking.
334 <h2 className="text-4xl font-bold text-gray-900 mb-4">
335 Never send a badly<br />written message again.
363336 </h2>
364 <p className="text-white/35 text-base mb-10">
365 Join thousands of writers, developers, and professionals who type 3x faster with their voice.
337 <p className="text-gray-400 text-base mb-10 max-w-md mx-auto">
338 Join thousands who write with confidence. AI grammar that works on every device, in every app.
366339 </p>
367 <a href="/download" className="inline-block px-10 py-3.5 rounded-xl bg-indigo-500 hover:bg-indigo-400 text-white text-sm font-medium transition-all">
340 <a href="/download" className="inline-block px-10 py-3.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white text-[15px] font-medium transition-all shadow-sm">
368341 Download 48co Free
369342 </a>
370 <p className="text-[11px] text-white/15 mt-4">Mac + Windows. Free tier. No credit card.</p>
343 <p className="text-[12px] text-gray-300 mt-4">Mac + Windows + Chrome. Free tier. No credit card.</p>
371344 </div>
372345 </section>
373346
374 {/* ── FOOTER ────────────────────────────────────────── */}
375 <footer className="border-t border-white/[0.04] py-10">
347 {/* ── FOOTER ────────────────────────────────── */}
348 <footer className="border-t border-black/[0.06] py-10">
376349 <div className="max-w-5xl mx-auto px-6 flex flex-col md:flex-row items-center justify-between gap-4">
377 <div className="flex items-center gap-2">
378 <span className="text-sm font-semibold text-white/40">48</span>
379 <span className="text-sm font-semibold text-indigo-400/60">co</span>
380 </div>
381 <div className="flex gap-6 text-[12px] text-white/20">
382 <a href="/download" className="hover:text-white/50 transition-colors">Download</a>
383 <a href="/compare" className="hover:text-white/50 transition-colors">Compare</a>
384 <a href="/pricing" className="hover:text-white/50 transition-colors">Pricing</a>
385 <a href="/live" className="hover:text-white/50 transition-colors">Try Live</a>
350 <span className="text-[14px] font-bold text-gray-400">48<span className="text-indigo-500">co</span></span>
351 <div className="flex gap-6 text-[12px] text-gray-400">
352 <a href="/download" className="hover:text-gray-700 transition-colors">Download</a>
353 <a href="/compare" className="hover:text-gray-700 transition-colors">Compare</a>
354 <a href="/pricing" className="hover:text-gray-700 transition-colors">Pricing</a>
355 <a href="/live" className="hover:text-gray-700 transition-colors">Try Live</a>
386356 </div>
387 <p className="text-[11px] text-white/10">Built in New Zealand</p>
357 <p className="text-[11px] text-gray-300">Built in New Zealand</p>
388358 </div>
389359 </footer>
390360 </main>
391361
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts