AUXteam commited on
Commit
17b60d4
·
verified ·
1 Parent(s): 69b89f7

Upload folder using huggingface_hub

Browse files
App.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import Navbar from './components/Navbar';
4
+ import Hero from './components/Hero';
5
+ import TrustedBy from './components/TrustedBy';
6
+ import ProductOverview from './components/ProductOverview';
7
+ import InteractiveDemo from './components/InteractiveDemo';
8
+ import UseCases from './components/UseCases';
9
+ import HowItWorks from './components/HowItWorks';
10
+ import Accuracy from './components/Accuracy';
11
+ import Documentation from './components/Documentation';
12
+ import FAQ from './components/FAQ';
13
+ import Footer from './components/Footer';
14
+ import SimulationPage from './components/SimulationPage';
15
+ import ConversationPage from './components/ConversationPage';
16
+ import ChatPage from './components/ChatPage';
17
+
18
+ function App() {
19
+ const [currentView, setCurrentView] = useState<'landing' | 'simulation' | 'conversation' | 'chat'>('landing');
20
+
21
+ const startSimulation = () => {
22
+ setCurrentView('simulation');
23
+ window.scrollTo(0,0);
24
+ };
25
+
26
+ const goBackToLanding = () => {
27
+ setCurrentView('landing');
28
+ };
29
+
30
+ const openConversation = () => {
31
+ setCurrentView('conversation');
32
+ };
33
+
34
+ const openChat = () => {
35
+ setCurrentView('chat');
36
+ };
37
+
38
+ const goBackToSimulation = () => {
39
+ setCurrentView('simulation');
40
+ };
41
+
42
+ if (currentView === 'simulation') {
43
+ return (
44
+ <SimulationPage
45
+ onBack={goBackToLanding}
46
+ onOpenConversation={openConversation}
47
+ onOpenChat={openChat}
48
+ />
49
+ );
50
+ }
51
+
52
+ if (currentView === 'conversation') {
53
+ return <ConversationPage onBack={goBackToSimulation} />;
54
+ }
55
+
56
+ if (currentView === 'chat') {
57
+ return <ChatPage onBack={goBackToSimulation} />;
58
+ }
59
+
60
+ return (
61
+ <div className="bg-black min-h-screen text-white selection:bg-teal-500/30">
62
+ <Navbar onStart={startSimulation} />
63
+ <main>
64
+ <Hero onStart={startSimulation} />
65
+ <TrustedBy />
66
+ <ProductOverview />
67
+ <InteractiveDemo />
68
+ <UseCases />
69
+ <HowItWorks />
70
+ <Accuracy />
71
+ <Documentation />
72
+ <FAQ />
73
+ </main>
74
+ <Footer />
75
+ </div>
76
+ );
77
+ }
78
+
79
+ export default App;
Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim AS build
2
+ WORKDIR /app
3
+ COPY package*.json ./
4
+ RUN npm install
5
+ COPY . .
6
+ RUN npm run build
7
+
8
+ FROM node:20-slim
9
+ WORKDIR /app
10
+ COPY --from=build /app/dist ./dist
11
+ COPY --from=build /app/package*.json ./
12
+ RUN npm install --only=production
13
+ COPY server.js ./
14
+ EXPOSE 7860
15
+ CMD ["node", "server.js"]
README.md CHANGED
@@ -1,10 +1,29 @@
1
  ---
2
  title: UserSyncInterface
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: indigo
6
- sdk: static
7
- pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: UserSyncInterface
3
+ emoji: 🔄
4
+ colorFrom: blue
5
+ colorTo: blue
6
+ sdk: docker
7
+ app_port: 7860
8
  ---
9
 
10
+ <div align="center">
11
+ <img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
12
+ </div>
13
+
14
+ # Run and deploy your AI Studio app
15
+
16
+ This contains everything you need to run your app locally.
17
+
18
+ View your app in AI Studio: https://ai.studio/apps/drive/1uBpK_suSmvNgTEgSyU7qMenb62ZRrQwX
19
+
20
+ ## Run Locally
21
+
22
+ **Prerequisites:** Node.js
23
+
24
+
25
+ 1. Install dependencies:
26
+ `npm install`
27
+ 2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
28
+ 3. Run the app:
29
+ `npm run dev`
components/Accuracy.tsx ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts';
3
+
4
+ const data = [
5
+ { name: 'SyncUsers', score: 82, color: '#ffffff' }, // White
6
+ { name: 'Claude 3.7 Sonnet', score: 64, color: '#4b5563' }, // Gray-600
7
+ { name: 'Gemini 1.5 pro', score: 63, color: '#4b5563' },
8
+ { name: 'GPT-4o', score: 60, color: '#4b5563' },
9
+ { name: 'GPT-3.5 Turbo', score: 54, color: '#4b5563' },
10
+ ];
11
+
12
+ const Accuracy: React.FC = () => {
13
+ return (
14
+ <section id="accuracy" className="py-24 bg-black">
15
+ <div className="max-w-7xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
16
+ <div>
17
+ <div className="inline-block px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300 mb-8">Accuracy</div>
18
+ <h2 className="text-3xl md:text-5xl font-semibold leading-tight mb-8">
19
+ SyncUsers is <span className="text-white">30% more accurate</span> at predicting engagement than standard LLMs.
20
+ </h2>
21
+
22
+ <div className="bg-gray-900/30 border border-gray-800 rounded-2xl p-6 md:p-8">
23
+ <h4 className="text-gray-400 mb-6">Success rate at picking winners from pairs of LinkedIn posts</h4>
24
+ <div className="h-[300px] w-full">
25
+ <ResponsiveContainer width="100%" height="100%">
26
+ <BarChart data={data} layout="vertical" margin={{ left: 0, right: 40 }}>
27
+ <XAxis type="number" hide />
28
+ <YAxis
29
+ dataKey="name"
30
+ type="category"
31
+ width={150}
32
+ tick={{ fill: '#9ca3af', fontSize: 12 }}
33
+ axisLine={false}
34
+ tickLine={false}
35
+ />
36
+ <Tooltip
37
+ cursor={{ fill: 'transparent' }}
38
+ contentStyle={{ backgroundColor: '#111', border: '1px solid #333' }}
39
+ />
40
+ <Bar dataKey="score" radius={[0, 4, 4, 0]} barSize={32}>
41
+ {data.map((entry, index) => (
42
+ <Cell key={`cell-${index}`} fill={entry.name === 'SyncUsers' ? 'url(#gradient)' : '#374151'} />
43
+ ))}
44
+ </Bar>
45
+ </BarChart>
46
+ </ResponsiveContainer>
47
+ {/* Def for gradient */}
48
+ <svg style={{ height: 0 }}>
49
+ <defs>
50
+ <linearGradient id="gradient" x1="0" y1="0" x2="1" y2="0">
51
+ <stop offset="0%" stopColor="#e5e7eb" />
52
+ <stop offset="100%" stopColor="#ffffff" />
53
+ </linearGradient>
54
+ </defs>
55
+ </svg>
56
+ </div>
57
+ {/* Custom Labels overlay for scores since Recharts labels are tricky with layout vertical sometimes */}
58
+ <div className="hidden">
59
+ {/* Placeholder for accessibility */}
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ {/* Right 3D Visual */}
65
+ <div className="relative h-[600px] flex items-end justify-center perspective-1000">
66
+ {/* Abstract 3D Bar representation */}
67
+ <div className="relative w-40 h-80 bg-teal-800 transform rotate-y-12 shadow-2xl translate-x-10 translate-y-10 z-10 opacity-90">
68
+ <div className="absolute top-0 left-0 w-full h-10 bg-teal-400 transform -skew-x-[40deg] origin-top -translate-y-10"></div>
69
+ <div className="absolute top-0 left-0 h-full w-10 bg-teal-900 transform skew-y-[50deg] origin-left -translate-x-10"></div>
70
+ </div>
71
+
72
+ <div className="relative w-24 h-40 bg-blue-900 transform rotate-y-12 shadow-xl -translate-x-10 translate-y-20 opacity-70">
73
+ <div className="absolute top-0 left-0 w-full h-8 bg-blue-500 transform -skew-x-[40deg] origin-top -translate-y-8"></div>
74
+ <div className="absolute top-0 left-0 h-full w-8 bg-blue-950 transform skew-y-[50deg] origin-left -translate-x-8"></div>
75
+ </div>
76
+
77
+ <div className="absolute top-20 right-20 text-9xl font-bold text-gray-800 opacity-20 select-none">
78
+ Λ
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </section>
83
+ );
84
+ };
85
+
86
+ export default Accuracy;
components/ChatPage.tsx ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useRef } from 'react';
3
+ import { X, ClipboardList, Linkedin, Instagram, Mail, Layout, Edit3, MonitorPlay, Lightbulb, Image, Plus, Sparkles, Zap, AlertCircle, Video, Megaphone, Link as LinkIcon, Loader2, RefreshCw } from 'lucide-react';
4
+ import { GradioService } from '../services/gradioService';
5
+
6
+ // --- Types ---
7
+ interface ChatPageProps {
8
+ onBack: () => void;
9
+ }
10
+
11
+ // --- Sub-components (Modular Structure) ---
12
+
13
+ const ChatButton: React.FC<{ label: string; primary?: boolean; icon?: React.ReactNode; onClick?: () => void; className?: string }> = ({ label, primary, icon, onClick, className = "" }) => (
14
+ <button
15
+ onClick={onClick}
16
+ className={`flex items-center gap-2 px-4 py-2 rounded-lg text-xs md:text-sm font-medium transition-all duration-200 whitespace-nowrap
17
+ ${primary
18
+ ? 'bg-white text-black hover:bg-gray-200 shadow-[0_0_15px_rgba(255,255,255,0.2)]'
19
+ : 'bg-gray-900 border border-gray-800 text-gray-300 hover:bg-gray-800 hover:text-white hover:border-gray-600'
20
+ } ${className}`}
21
+ >
22
+ {icon}
23
+ {primary && <Zap size={14} className="fill-black" />}
24
+ {label}
25
+ </button>
26
+ );
27
+
28
+ const CategoryCard: React.FC<{ title: string; options: { label: string; icon: React.ReactNode }[] }> = ({ title, options }) => (
29
+ <div className="flex flex-col gap-3">
30
+ <h3 className="text-[10px] font-bold text-gray-500 uppercase tracking-widest mb-2 ml-1">{title}</h3>
31
+ <div className="space-y-1">
32
+ {options.map((option) => (
33
+ <div key={option.label} className="group flex items-center gap-3 p-3 rounded-xl hover:bg-gray-900/80 cursor-pointer border border-transparent hover:border-gray-800 transition-all duration-200">
34
+ <div className="text-gray-500 group-hover:text-white transition-colors w-5 flex justify-center">
35
+ {option.icon}
36
+ </div>
37
+ <span className="text-sm font-medium text-gray-400 group-hover:text-gray-200">{option.label}</span>
38
+ </div>
39
+ ))}
40
+ </div>
41
+ </div>
42
+ );
43
+
44
+ const ChatInput: React.FC<{ onSimulate: (msg: string) => void; onHelpMeCraft: (msg: string) => void; isSimulating: boolean }> = ({ onSimulate, onHelpMeCraft, isSimulating }) => {
45
+ const [message, setMessage] = useState('');
46
+ const fileInputRef = useRef<HTMLInputElement>(null);
47
+
48
+ const handleUploadClick = () => {
49
+ fileInputRef.current?.click();
50
+ };
51
+
52
+ const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53
+ const file = e.target.files?.[0];
54
+ if (file) {
55
+ console.log("Selected file:", file.name);
56
+ // In a real app, you'd handle the upload here
57
+ }
58
+ };
59
+
60
+ return (
61
+ <div className="border-t border-gray-800 pt-6 mt-4 bg-[#0a0a0a] px-6 pb-8 md:pb-10 absolute bottom-0 left-0 right-0 z-20 shadow-[0_-20px_50px_rgba(0,0,0,0.8)]">
62
+ <div className="max-w-5xl mx-auto space-y-4">
63
+ <textarea
64
+ className="w-full h-24 bg-black border border-gray-800 text-gray-200 placeholder-gray-600 p-4 rounded-2xl resize-none focus:outline-none focus:border-gray-600 focus:ring-1 focus:ring-gray-600 transition-all text-sm leading-relaxed"
65
+ placeholder="Paste your website link here"
66
+ value={message}
67
+ onChange={(e) => setMessage(e.target.value)}
68
+ />
69
+ <div className="flex flex-wrap justify-between items-start gap-4">
70
+ <div className="flex gap-2 md:gap-3 flex-wrap">
71
+ <div className="flex flex-col gap-2">
72
+ <ChatButton label="Website Link for UX Testing" icon={<LinkIcon size={14} />} />
73
+ </div>
74
+ <input
75
+ type="file"
76
+ ref={fileInputRef}
77
+ onChange={handleFileChange}
78
+ className="hidden"
79
+ accept="image/*"
80
+ />
81
+ <ChatButton
82
+ label="Upload Images"
83
+ icon={<Image size={14} />}
84
+ className="h-fit"
85
+ onClick={handleUploadClick}
86
+ />
87
+ </div>
88
+ <div className="flex gap-3 items-center mt-auto">
89
+ <button
90
+ onClick={() => onHelpMeCraft(message)}
91
+ className="hidden md:flex text-xs text-gray-500 hover:text-white transition-colors items-center gap-1 mr-2"
92
+ >
93
+ Help Me Craft <Sparkles size={12} />
94
+ </button>
95
+ <ChatButton
96
+ label={isSimulating ? "Simulating..." : "Simulate"}
97
+ primary
98
+ onClick={() => onSimulate(message)}
99
+ icon={isSimulating ? <Loader2 size={14} className="animate-spin" /> : undefined}
100
+ />
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ );
106
+ };
107
+
108
+ // --- Main Page Component ---
109
+
110
+ const ChatPage: React.FC<ChatPageProps> = ({ onBack }) => {
111
+ const [showNotification, setShowNotification] = useState(false);
112
+ const [isSimulating, setIsSimulating] = useState(false);
113
+ const [simulationResult, setSimulationResult] = useState<string | null>(null);
114
+
115
+ const handleSimulate = (msg: string) => {
116
+ setIsSimulating(true);
117
+ setShowNotification(true);
118
+
119
+ // Simulate API call
120
+ setTimeout(() => {
121
+ setIsSimulating(false);
122
+ setSimulationResult("Please wait, the results will show here. Click Refresh to gather results from the API.");
123
+ }, 2000);
124
+ };
125
+
126
+ const handleRefresh = async () => {
127
+ setIsSimulating(true);
128
+ try {
129
+ // In a real scenario, we would poll the Gradio API for results
130
+ // result = await GradioService.simulate(...)
131
+ setTimeout(() => {
132
+ setIsSimulating(false);
133
+ setSimulationResult("Final Simulation Results: The content resonated well with 85% of the target audience. Key feedback: 'Clearer value proposition needed in the header'.");
134
+ }, 1500);
135
+ } catch (error) {
136
+ setIsSimulating(false);
137
+ }
138
+ };
139
+
140
+ const handleHelpMeCraft = async (msg: string) => {
141
+ const crafted = await GradioService.helpMeCraft(msg);
142
+ alert("Help Me Craft suggestion: " + crafted);
143
+ };
144
+
145
+ const categories = {
146
+ 'Survey': [
147
+ { label: 'Survey', icon: <ClipboardList size={18} /> }
148
+ ],
149
+ 'Marketing Content': [
150
+ { label: 'Article', icon: <Edit3 size={18} /> },
151
+ { label: 'Website Link', icon: <Layout size={18} /> },
152
+ { label: 'Advertisement', icon: <Megaphone size={18} /> }
153
+ ],
154
+ 'Social Media Posts': [
155
+ { label: 'LinkedIn Post', icon: <Linkedin size={18} /> },
156
+ { label: 'Instagram Post', icon: <Instagram size={18} /> },
157
+ { label: 'X Post', icon: <X size={18} /> },
158
+ { label: 'TikTok Script', icon: <Video size={18} /> }
159
+ ],
160
+ 'Communication': [
161
+ { label: 'Email Subject Line', icon: <Mail size={18} /> },
162
+ { label: 'Email', icon: <Mail size={18} /> }
163
+ ],
164
+ 'Product': [
165
+ { label: 'Product Proposition', icon: <Lightbulb size={18} /> }
166
+ ]
167
+ };
168
+
169
+ return (
170
+ <div className="fixed inset-0 z-50 bg-[#050505] text-white flex flex-col animate-in fade-in duration-300">
171
+
172
+ {/* Header / Nav */}
173
+ <div className="flex items-center justify-between px-6 py-4 md:px-8 md:py-6 border-b border-gray-800/50 bg-[#050505] z-10 relative">
174
+ <div className="flex items-center gap-3">
175
+ <div className="w-8 h-8 flex items-center justify-center bg-gray-800 rounded-lg text-white font-bold">Λ</div>
176
+ <h2 className="text-xl font-medium tracking-tight">New Simulation</h2>
177
+ </div>
178
+ <button onClick={onBack} className="p-2 text-gray-500 hover:text-white hover:bg-gray-900 rounded-full transition-colors">
179
+ <X size={24} />
180
+ </button>
181
+ </div>
182
+
183
+ {/* Notification Banner */}
184
+ {showNotification && (
185
+ <div className="absolute top-24 left-1/2 -translate-x-1/2 z-50 w-[90%] max-w-lg animate-in slide-in-from-top-4 fade-in duration-500">
186
+ <div className="bg-[#0f1f15] backdrop-blur-xl border border-green-500/20 text-green-100 px-6 py-5 rounded-2xl shadow-2xl flex items-start gap-4 ring-1 ring-green-500/10">
187
+ <div className="p-1.5 bg-green-500/20 rounded-full mt-0.5 shrink-0">
188
+ <AlertCircle size={18} className="text-green-400" />
189
+ </div>
190
+ <div className="flex-1">
191
+ <h4 className="font-semibold text-green-300 mb-1">Simulation Initiated</h4>
192
+ <p className="text-sm text-green-200/70 leading-relaxed">
193
+ The simulation has started. This process can take up to <strong className="text-white">30 minutes</strong>. The results will show here when ready.
194
+ </p>
195
+ {simulationResult && (
196
+ <div className="mt-4 p-3 bg-black/40 rounded-xl border border-green-500/20 text-xs text-green-200 flex flex-col gap-3">
197
+ <p>{simulationResult}</p>
198
+ <button
199
+ onClick={handleRefresh}
200
+ disabled={isSimulating}
201
+ className="flex items-center gap-2 px-3 py-1.5 bg-green-600/20 hover:bg-green-600/40 rounded-lg self-end transition-colors disabled:opacity-50"
202
+ >
203
+ <RefreshCw size={14} className={isSimulating ? "animate-spin" : ""} />
204
+ Gather Results
205
+ </button>
206
+ </div>
207
+ )}
208
+ </div>
209
+ <button onClick={() => setShowNotification(false)} className="text-green-400 hover:text-white ml-auto p-1">
210
+ <X size={16} />
211
+ </button>
212
+ </div>
213
+ </div>
214
+ )}
215
+
216
+ {/* Scrollable Content Area */}
217
+ <div className="flex-1 overflow-y-auto custom-scrollbar p-6 md:p-8 pb-80"> {/* Large bottom padding for fixed input */}
218
+ <div className="max-w-5xl mx-auto">
219
+ <h1 className="text-2xl md:text-3xl font-semibold text-center mb-12 mt-4 md:mt-8">What would you like to simulate?</h1>
220
+
221
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
222
+ {/* Column 1 */}
223
+ <div className="space-y-12">
224
+ <CategoryCard title="Survey" options={categories['Survey']} />
225
+ <CategoryCard title="Marketing Content" options={categories['Marketing Content']} />
226
+ </div>
227
+
228
+ {/* Column 2 */}
229
+ <div className="space-y-12">
230
+ <CategoryCard title="Social Media Posts" options={categories['Social Media Posts']} />
231
+ </div>
232
+
233
+ {/* Column 3 */}
234
+ <div className="space-y-12">
235
+ <CategoryCard title="Communication" options={categories['Communication']} />
236
+ <CategoryCard title="Product" options={categories['Product']} />
237
+ </div>
238
+ </div>
239
+
240
+ <div className="flex justify-center mt-16 mb-8">
241
+ <button className="flex items-center gap-2 text-gray-500 hover:text-gray-300 transition-colors text-sm px-4 py-2 hover:bg-gray-900 rounded-lg">
242
+ <Plus size={16} />
243
+ Request a new context
244
+ </button>
245
+ </div>
246
+ </div>
247
+ </div>
248
+
249
+ {/* Input Footer */}
250
+ <ChatInput onSimulate={handleSimulate} onHelpMeCraft={handleHelpMeCraft} isSimulating={isSimulating} />
251
+ </div>
252
+ );
253
+ };
254
+
255
+ export default ChatPage;
components/ConversationPage.tsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import { X, ClipboardList, Linkedin, Instagram, Mail, Layout, Edit3, Type, MonitorPlay, Lightbulb, Image, Plus } from 'lucide-react';
4
+ import Button from './ui/Button';
5
+
6
+ interface ConversationPageProps {
7
+ onBack: () => void;
8
+ }
9
+
10
+ const ConversationPage: React.FC<ConversationPageProps> = ({ onBack }) => {
11
+ const [activeTab, setActiveTab] = useState('Website Content');
12
+
13
+ return (
14
+ <div className="fixed inset-0 z-50 bg-black flex items-center justify-center p-6 animate-in fade-in duration-300">
15
+ {/* Background Mesh (Subtle) */}
16
+ <div className="absolute inset-0 pointer-events-none opacity-20">
17
+ <div className="absolute top-10 left-10 w-64 h-64 bg-purple-900/30 rounded-full blur-[100px]" />
18
+ <div className="absolute bottom-10 right-10 w-96 h-96 bg-teal-900/20 rounded-full blur-[120px]" />
19
+ </div>
20
+
21
+ <div className="bg-[#050505] border border-gray-800 w-full max-w-5xl rounded-3xl shadow-2xl flex flex-col overflow-hidden max-h-[90vh] relative z-10">
22
+
23
+ {/* Close Button */}
24
+ <button onClick={onBack} className="absolute top-6 right-6 text-gray-500 hover:text-white transition-colors">
25
+ <X size={24} />
26
+ </button>
27
+
28
+ <div className="p-10 pb-0">
29
+ <h2 className="text-2xl font-semibold text-center mb-8">What would you like to simulate?</h2>
30
+
31
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-8 max-w-4xl mx-auto">
32
+
33
+ {/* Column 1 */}
34
+ <div className="space-y-6">
35
+ <h3 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-4">Survey</h3>
36
+ <OptionItem icon={<ClipboardList />} label="Survey" />
37
+
38
+ <h3 className="text-xs font-bold text-gray-500 uppercase tracking-widest mt-8 mb-4">Marketing Content</h3>
39
+ <OptionItem icon={<Edit3 />} label="Article" />
40
+ <OptionItem icon={<Layout />} label="Website Content" active />
41
+ <OptionItem icon={<MonitorPlay />} label="Advertisement" />
42
+ </div>
43
+
44
+ {/* Column 2 */}
45
+ <div className="space-y-6">
46
+ <h3 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-4">Social Media Posts</h3>
47
+ <OptionItem icon={<Linkedin />} label="LinkedIn Post" />
48
+ <OptionItem icon={<Instagram />} label="Instagram Post" />
49
+ <OptionItem icon={<X className="text-white" />} label="X Post" />
50
+ <OptionItem icon={<MonitorPlay />} label="TikTok Script" />
51
+ </div>
52
+
53
+ {/* Column 3 */}
54
+ <div className="space-y-6">
55
+ <h3 className="text-xs font-bold text-gray-500 uppercase tracking-widest mb-4">Communication</h3>
56
+ <OptionItem icon={<Mail />} label="Email Subject Line" />
57
+ <OptionItem icon={<Mail />} label="Email" />
58
+
59
+ <h3 className="text-xs font-bold text-gray-500 uppercase tracking-widest mt-8 mb-4">Product</h3>
60
+ <OptionItem icon={<Lightbulb />} label="Product Proposition" />
61
+ </div>
62
+
63
+ </div>
64
+ </div>
65
+
66
+ {/* Bottom Input Area */}
67
+ <div className="mt-8 p-6 bg-gray-900/30 border-t border-gray-800 flex-1 flex flex-col justify-end">
68
+ <div className="max-w-4xl mx-auto w-full">
69
+ <div className="bg-black border border-gray-700 rounded-2xl p-4 shadow-lg">
70
+ <textarea
71
+ placeholder="Upload an image of your website, describe it, or write your website content here..."
72
+ className="w-full bg-transparent text-gray-300 placeholder-gray-600 resize-none focus:outline-none min-h-[80px]"
73
+ />
74
+ <div className="flex justify-between items-center mt-4">
75
+ <div className="flex gap-3">
76
+ <Button variant="outline" size="sm" className="gap-2 text-xs border-gray-800 bg-gray-900 text-gray-300">
77
+ <Layout size={14} /> Website Content
78
+ </Button>
79
+ <Button variant="outline" size="sm" className="gap-2 text-xs border-gray-800 bg-gray-900 text-gray-300">
80
+ <Image size={14} /> Upload Images
81
+ </Button>
82
+ </div>
83
+ <div className="flex gap-3">
84
+ <Button variant="ghost" size="sm" className="text-xs text-teal-400 hover:text-teal-300">
85
+ Help Me Craft <span className="ml-1">✨</span>
86
+ </Button>
87
+ <Button variant="primary" size="sm" className="gap-2 bg-white text-black hover:bg-gray-200">
88
+ Simulate <span className="ml-1">⚡</span>
89
+ </Button>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ <div className="flex justify-center mt-4">
95
+ <button className="text-gray-500 text-sm hover:text-white flex items-center gap-2">
96
+ <Plus size={14} /> Request a new context
97
+ </button>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ );
104
+ };
105
+
106
+ const OptionItem: React.FC<{ icon: React.ReactNode, label: string, active?: boolean }> = ({ icon, label, active }) => (
107
+ <div className={`flex items-center gap-4 group cursor-pointer p-2 rounded-lg transition-colors ${active ? 'bg-gray-800' : 'hover:bg-gray-900'}`}>
108
+ <div className="w-8 h-8 flex items-center justify-center text-gray-400 group-hover:text-white transition-colors">
109
+ {icon}
110
+ </div>
111
+ <span className={`text-sm font-medium ${active ? 'text-white' : 'text-gray-400 group-hover:text-white'}`}>{label}</span>
112
+ </div>
113
+ );
114
+
115
+ export default ConversationPage;
components/Documentation.tsx ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Book, Code, Terminal, Zap } from 'lucide-react';
3
+ import Button from './ui/Button';
4
+
5
+ const Documentation: React.FC = () => {
6
+ return (
7
+ <section id="docs" className="py-24 bg-black border-t border-gray-900">
8
+ <div className="max-w-7xl mx-auto px-6">
9
+ <div className="flex flex-col md:flex-row justify-between items-end mb-12 gap-6">
10
+ <div>
11
+ <div className="inline-block px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300 mb-6">
12
+ Developers First
13
+ </div>
14
+ <h2 className="text-3xl md:text-5xl font-semibold mb-4">
15
+ Build with our API
16
+ </h2>
17
+ <p className="text-gray-400 text-lg max-w-xl">
18
+ Integrate user simulation directly into your CI/CD pipeline or product workflow.
19
+ Get started for free.
20
+ </p>
21
+ </div>
22
+ <Button variant="outline">View Full Documentation</Button>
23
+ </div>
24
+
25
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
26
+ {/* Code Snippet Card */}
27
+ <div className="bg-gray-900/30 border border-gray-800 rounded-2xl p-6 md:p-8 font-mono text-sm overflow-hidden relative group">
28
+ <div className="absolute top-4 right-4 flex gap-2">
29
+ <div className="w-3 h-3 rounded-full bg-red-500/20 border border-red-500/50"></div>
30
+ <div className="w-3 h-3 rounded-full bg-yellow-500/20 border border-yellow-500/50"></div>
31
+ <div className="w-3 h-3 rounded-full bg-green-500/20 border border-green-500/50"></div>
32
+ </div>
33
+ <div className="text-gray-500 mb-2"># Install the SDK</div>
34
+ <div className="text-teal-400 mb-6">$ npm install @aux/sdk</div>
35
+
36
+ <div className="text-gray-500 mb-2"># Run a simulation</div>
37
+ <div className="text-purple-300">import</div> <div className="text-white inline">{`{ AuxClient }`}</div> <div className="text-purple-300 inline">from</div> <div className="text-green-300 inline">'@aux/sdk'</div>;
38
+ <br/>
39
+ <br/>
40
+ <div className="text-purple-300">const</div> <div className="text-white inline">client</div> = <div className="text-purple-300 inline">new</div> <div className="text-yellow-300 inline">AuxClient</div>({`{ apiKey: '...' }`});
41
+ <br/>
42
+ <br/>
43
+ <div className="text-purple-300">const</div> <div className="text-white inline">result</div> = <div className="text-purple-300 inline">await</div> client.simulation.<div className="text-blue-300 inline">create</div>({`{`}
44
+ <br/>
45
+ &nbsp;&nbsp;audience: <div className="text-green-300 inline">'tech-founders'</div>,
46
+ <br/>
47
+ &nbsp;&nbsp;content: <div className="text-green-300 inline">'https://myapp.com/launch'</div>
48
+ <br/>
49
+ {`}`});
50
+ </div>
51
+
52
+ {/* Docs Links Grid */}
53
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
54
+ <div className="bg-gray-900/20 border border-gray-800 p-6 rounded-xl hover:bg-gray-900/40 transition-colors cursor-pointer group">
55
+ <Book className="w-8 h-8 text-teal-500 mb-4 group-hover:scale-110 transition-transform" />
56
+ <h3 className="text-white font-medium mb-2">Quick Start Guide</h3>
57
+ <p className="text-gray-500 text-sm">Deploy your first simulation in under 5 minutes.</p>
58
+ </div>
59
+ <div className="bg-gray-900/20 border border-gray-800 p-6 rounded-xl hover:bg-gray-900/40 transition-colors cursor-pointer group">
60
+ <Terminal className="w-8 h-8 text-purple-500 mb-4 group-hover:scale-110 transition-transform" />
61
+ <h3 className="text-white font-medium mb-2">API Reference</h3>
62
+ <p className="text-gray-500 text-sm">Detailed endpoints, parameters, and response types.</p>
63
+ </div>
64
+ <div className="bg-gray-900/20 border border-gray-800 p-6 rounded-xl hover:bg-gray-900/40 transition-colors cursor-pointer group">
65
+ <Code className="w-8 h-8 text-blue-500 mb-4 group-hover:scale-110 transition-transform" />
66
+ <h3 className="text-white font-medium mb-2">SDKs & Libraries</h3>
67
+ <p className="text-gray-500 text-sm">Official libraries for Node.js, Python, and Go.</p>
68
+ </div>
69
+ <div className="bg-gray-900/20 border border-gray-800 p-6 rounded-xl hover:bg-gray-900/40 transition-colors cursor-pointer group">
70
+ <Zap className="w-8 h-8 text-yellow-500 mb-4 group-hover:scale-110 transition-transform" />
71
+ <h3 className="text-white font-medium mb-2">Webhooks</h3>
72
+ <p className="text-gray-500 text-sm">Real-time event notifications for your integrations.</p>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </section>
78
+ );
79
+ };
80
+
81
+ export default Documentation;
components/FAQ.tsx ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import { FAQS } from '../constants';
3
+ import { ChevronDown, ChevronUp } from 'lucide-react';
4
+
5
+ const FAQ: React.FC = () => {
6
+ const [openIndex, setOpenIndex] = useState<number | null>(0);
7
+
8
+ const toggleFAQ = (index: number) => {
9
+ setOpenIndex(openIndex === index ? null : index);
10
+ };
11
+
12
+ return (
13
+ <section className="py-24 bg-black">
14
+ <div className="max-w-7xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-2 gap-16">
15
+ <div>
16
+ <div className="inline-block px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300 mb-8">FAQ</div>
17
+ <h2 className="text-3xl md:text-5xl font-semibold mb-6">
18
+ We simulated what questions you need answering
19
+ </h2>
20
+ <p className="text-gray-400 text-lg mb-8">
21
+ Explore quick solutions to common questions. Need more? Feel free to contact our <u className="text-white cursor-pointer">support team</u>.
22
+ </p>
23
+ </div>
24
+
25
+ <div className="space-y-4">
26
+ {FAQS.map((faq, idx) => (
27
+ <div
28
+ key={idx}
29
+ className="border border-gray-800 rounded-xl overflow-hidden bg-gray-900/20"
30
+ >
31
+ <button
32
+ className="w-full flex justify-between items-center p-6 text-left hover:bg-gray-900/50 transition-colors"
33
+ onClick={() => toggleFAQ(idx)}
34
+ >
35
+ <span className="font-medium text-lg">{faq.question}</span>
36
+ {openIndex === idx ? <ChevronUp className="text-gray-400" /> : <ChevronDown className="text-gray-400" />}
37
+ </button>
38
+
39
+ <div
40
+ className={`transition-all duration-300 ease-in-out overflow-hidden ${openIndex === idx ? 'max-h-48 opacity-100' : 'max-h-0 opacity-0'}`}
41
+ >
42
+ <div className="p-6 pt-0 text-gray-400 leading-relaxed">
43
+ {faq.answer}
44
+ </div>
45
+ </div>
46
+ </div>
47
+ ))}
48
+ </div>
49
+ </div>
50
+ </section>
51
+ );
52
+ };
53
+
54
+ export default FAQ;
components/Footer.tsx ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+
3
+ const Footer: React.FC = () => {
4
+ return (
5
+ <footer className="py-12 bg-black border-t border-gray-900 text-sm text-gray-500">
6
+ <div className="max-w-7xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-6">
7
+ <div className="flex items-center gap-2">
8
+ <div className="w-6 h-6 flex items-center justify-center font-bold text-white bg-gray-800 rounded">Λ</div>
9
+ <span className="text-white font-semibold">SyncUsers</span>
10
+ </div>
11
+
12
+ <div className="flex gap-8">
13
+ <a href="#" className="hover:text-white transition-colors">Twitter</a>
14
+ <a href="#" className="hover:text-white transition-colors">LinkedIn</a>
15
+ <a href="#" className="hover:text-white transition-colors">Privacy</a>
16
+ <a href="#" className="hover:text-white transition-colors">Terms</a>
17
+ </div>
18
+
19
+ <div>
20
+ © 2024 SyncUsers Inc.
21
+ </div>
22
+ </div>
23
+ </footer>
24
+ );
25
+ };
26
+
27
+ export default Footer;
components/Hero.tsx ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import Button from './ui/Button';
4
+ import RealNetworkGraph from './RealNetworkGraph';
5
+
6
+ interface HeroProps {
7
+ onStart?: () => void;
8
+ }
9
+
10
+ const Hero: React.FC<HeroProps> = ({ onStart }) => {
11
+ const [currentSlide, setCurrentSlide] = useState(0);
12
+
13
+ useEffect(() => {
14
+ const interval = setInterval(() => {
15
+ setCurrentSlide((prev) => (prev === 0 ? 1 : 0));
16
+ }, 6000);
17
+ return () => clearInterval(interval);
18
+ }, []);
19
+
20
+ return (
21
+ <section className="relative min-h-[90vh] flex flex-col items-center justify-center pt-32 pb-20 overflow-hidden">
22
+ {/* Background Ambience */}
23
+ <div className="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[500px] bg-purple-900/10 rounded-full blur-[120px] pointer-events-none" />
24
+
25
+ <div className="relative z-10 max-w-7xl mx-auto px-6 w-full grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
26
+
27
+ {/* Left Content - Animated Transitions */}
28
+ <div className="space-y-8 relative h-[400px]">
29
+ {/* Slide 1 */}
30
+ <div className={`absolute top-0 left-0 w-full transition-all duration-1000 ease-in-out transform ${currentSlide === 0 ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-10 pointer-events-none'}`}>
31
+ <h1 className="text-5xl md:text-7xl font-bold leading-tight">
32
+ User Simulation <span className="text-gray-400">API</span> for Developers
33
+ </h1>
34
+ <p className="text-xl text-gray-400 mt-6 max-w-lg">
35
+ Programmatically test your UX decisions. Integrate accurate user simulation into your CI/CD pipeline. Free for developers.
36
+ </p>
37
+ <div className="flex flex-wrap gap-4 mt-8">
38
+ <Button variant="primary" size="lg" onClick={onStart}>HF Space Demo</Button>
39
+ <Button variant="outline" size="lg" onClick={() => window.location.href='#docs'}>Read the Docs</Button>
40
+ </div>
41
+ </div>
42
+
43
+ {/* Slide 2 */}
44
+ <div className={`absolute top-0 left-0 w-full transition-all duration-1000 ease-in-out transform ${currentSlide === 1 ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10 pointer-events-none'}`}>
45
+ <h1 className="text-5xl md:text-7xl font-bold leading-tight">
46
+ Stop guessing. <span className="bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text text-transparent">Start simulating.</span>
47
+ </h1>
48
+ <p className="text-xl text-gray-400 mt-6 max-w-lg">
49
+ Validate features with AI-generated user societies before writing a single line of frontend code. The open standard for agentic UX testing.
50
+ </p>
51
+ <div className="flex flex-wrap gap-4 mt-8">
52
+ <Button variant="primary" size="lg" onClick={onStart}>Start Building</Button>
53
+ <Button variant="outline" size="lg" onClick={() => window.location.href='#docs'}>Documentation</Button>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ {/* Right Visuals - Real Network Graph */}
59
+ <div className="relative h-[500px] w-full flex items-center justify-center">
60
+ <div
61
+ className="w-full h-full opacity-80"
62
+ style={{
63
+ maskImage: 'linear-gradient(to bottom, black 0%, black 70%, transparent 100%)',
64
+ WebkitMaskImage: 'linear-gradient(to bottom, black 0%, black 70%, transparent 100%)'
65
+ }}
66
+ >
67
+ <RealNetworkGraph />
68
+ </div>
69
+
70
+ {/* Overlay Elements for depth */}
71
+ <div className="absolute inset-0 pointer-events-none bg-gradient-to-t from-black via-transparent to-transparent"></div>
72
+ </div>
73
+ </div>
74
+ </section>
75
+ );
76
+ };
77
+
78
+ export default Hero;
components/HowItWorks.tsx ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { User, Share2, Cpu, CheckCircle, Scale } from 'lucide-react';
3
+
4
+ const HowItWorks: React.FC = () => {
5
+ const steps = [
6
+ { icon: <User />, label: "Persona Creation" },
7
+ { icon: <Share2 />, label: "Focus Group Construction" },
8
+ { icon: <Cpu />, label: "AI-driven Simulations" },
9
+ { icon: <CheckCircle />, label: "Result Generation" },
10
+ { icon: <Scale />, label: "Automatic A/B Testing" },
11
+ ];
12
+
13
+ return (
14
+ <section id="how-it-works" className="py-24 bg-black border-t border-gray-900">
15
+ <div className="max-w-7xl mx-auto px-6 text-center">
16
+ <div className="inline-block px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300 mb-8">How It Works</div>
17
+ <h2 className="text-3xl md:text-5xl font-semibold mb-20">From raw data to real understanding</h2>
18
+
19
+ {/* Steps Navigation/Visual */}
20
+ <div className="flex flex-wrap justify-center gap-4 md:gap-8 mb-20">
21
+ {steps.map((step, idx) => (
22
+ <div key={idx} className="flex flex-col items-center gap-3 group cursor-default">
23
+ <div className="w-16 h-12 md:w-auto md:h-auto px-6 py-3 rounded-full border border-gray-700 bg-gray-900/50 flex items-center gap-2 text-gray-400 group-hover:text-white group-hover:border-gray-500 transition-all">
24
+ {step.icon}
25
+ <span className="text-sm font-medium hidden md:inline">{step.label}</span>
26
+ </div>
27
+ </div>
28
+ ))}
29
+ </div>
30
+
31
+ {/* Abstract Visualization */}
32
+ <div className="relative h-[400px] w-full max-w-4xl mx-auto border border-gray-800 rounded-3xl bg-black overflow-hidden flex items-center justify-center">
33
+ <div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/stardust.png')] opacity-20"></div>
34
+ {/* Radar/Network Graphic */}
35
+ <div className="relative w-96 h-96">
36
+ <div className="absolute inset-0 border border-blue-900/30 rounded-full animate-ping duration-[3000ms]"></div>
37
+ <div className="absolute inset-10 border border-blue-800/30 rounded-full"></div>
38
+ <div className="absolute inset-20 border border-blue-700/30 rounded-full"></div>
39
+
40
+ {/* Connecting Lines */}
41
+ <svg className="absolute inset-0 w-full h-full pointer-events-none">
42
+ {/* Center to Top (in) */}
43
+ <line x1="50%" y1="50%" x2="50%" y2="5%" stroke="#3b82f6" strokeWidth="2" strokeOpacity="0.5" />
44
+ {/* Center to Bottom Right (X) */}
45
+ <line x1="50%" y1="50%" x2="85%" y2="85%" stroke="#3b82f6" strokeWidth="2" strokeOpacity="0.5" />
46
+ {/* Center to Bottom Left (Globe) */}
47
+ <line x1="50%" y1="50%" x2="15%" y2="85%" stroke="#3b82f6" strokeWidth="2" strokeOpacity="0.5" />
48
+ </svg>
49
+
50
+ {/* Center Node */}
51
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-16 h-16 bg-gray-900 rounded-full border-2 border-blue-500 flex items-center justify-center shadow-[0_0_30px_rgba(59,130,246,0.5)] z-10">
52
+ <User className="text-white w-6 h-6" />
53
+ </div>
54
+
55
+ {/* Satellite Nodes */}
56
+ <div className="absolute top-0 left-1/2 -translate-x-1/2 w-10 h-10 bg-gray-900 border border-gray-600 rounded-full flex items-center justify-center z-10">
57
+ <span className="text-xs font-bold">in</span>
58
+ </div>
59
+ <div className="absolute bottom-10 right-10 w-10 h-10 bg-gray-900 border border-gray-600 rounded-full flex items-center justify-center z-10">
60
+ <span className="text-xs font-bold">X</span>
61
+ </div>
62
+ <div className="absolute bottom-10 left-10 w-10 h-10 bg-gray-900 border border-gray-600 rounded-full flex items-center justify-center z-10">
63
+ <GlobeIcon />
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </section>
69
+ );
70
+ };
71
+
72
+ const GlobeIcon = () => (
73
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="w-4 h-4">
74
+ <circle cx="12" cy="12" r="10"></circle>
75
+ <line x1="2" y1="12" x2="22" y2="12"></line>
76
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
77
+ </svg>
78
+ );
79
+
80
+ export default HowItWorks;
components/InteractiveDemo.tsx ADDED
@@ -0,0 +1,374 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import Button from './ui/Button';
3
+ import { Check, Sparkles, Send, MessageSquare, X } from 'lucide-react';
4
+
5
+ // --- Shared Components ---
6
+
7
+ const NetworkBackground: React.FC<{ className?: string }> = ({ className = "" }) => {
8
+ // Static node positions for consistency in other steps
9
+ const nodes = [
10
+ { cx: "20%", cy: "20%", r: 4, fill: "#14b8a6" }, // teal
11
+ { cx: "50%", cy: "10%", r: 6, fill: "#a855f7" }, // purple
12
+ { cx: "80%", cy: "30%", r: 5, fill: "#f97316" }, // orange
13
+ { cx: "30%", cy: "50%", r: 7, fill: "#3b82f6" }, // blue
14
+ { cx: "70%", cy: "60%", r: 6, fill: "#ec4899" }, // pink
15
+ { cx: "10%", cy: "70%", r: 5, fill: "#ef4444" }, // red
16
+ { cx: "40%", cy: "80%", r: 4, fill: "#14b8a6" },
17
+ { cx: "90%", cy: "80%", r: 5, fill: "#8b5cf6" },
18
+ { cx: "60%", cy: "40%", r: 3, fill: "#eab308" },
19
+ { cx: "25%", cy: "90%", r: 6, fill: "#3b82f6" },
20
+ ];
21
+
22
+ return (
23
+ <div className={`absolute inset-0 overflow-hidden pointer-events-none opacity-30 ${className}`}>
24
+ <svg className="w-full h-full">
25
+ {/* Lines */}
26
+ <line x1="20%" y1="20%" x2="50%" y2="10%" stroke="#374151" strokeWidth="1" />
27
+ <line x1="50%" y1="10%" x2="80%" y2="30%" stroke="#374151" strokeWidth="1" />
28
+ <line x1="20%" y1="20%" x2="30%" y2="50%" stroke="#374151" strokeWidth="1" />
29
+ <line x1="30%" y1="50%" x2="70%" y2="60%" stroke="#374151" strokeWidth="1" />
30
+ <line x1="70%" y1="60%" x2="80%" y2="30%" stroke="#374151" strokeWidth="1" />
31
+ <line x1="30%" y1="50%" x2="10%" y2="70%" stroke="#374151" strokeWidth="1" />
32
+ <line x1="40%" y1="80%" x2="10%" y2="70%" stroke="#374151" strokeWidth="1" />
33
+ <line x1="70%" y1="60%" x2="90%" y2="80%" stroke="#374151" strokeWidth="1" />
34
+ <line x1="50%" y1="10%" x2="60%" y2="40%" stroke="#374151" strokeWidth="1" />
35
+ <line x1="25%" y1="90%" x2="40%" y2="80%" stroke="#374151" strokeWidth="1" />
36
+
37
+ {/* Nodes */}
38
+ {nodes.map((node, i) => (
39
+ <circle key={i} {...node} className="animate-pulse" style={{ animationDelay: `${i * 0.5}s` }} />
40
+ ))}
41
+ </svg>
42
+ </div>
43
+ );
44
+ };
45
+
46
+ const SectionLayout: React.FC<{
47
+ number: string;
48
+ title: string;
49
+ description: React.ReactNode;
50
+ visual: React.ReactNode;
51
+ }> = ({ number, title, description, visual }) => {
52
+ return (
53
+ <section className="py-24 border-t border-gray-900 bg-black">
54
+ <div className="max-w-7xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
55
+ {/* Text Content */}
56
+ <div className="order-2 lg:order-1">
57
+ <div className="w-12 h-12 rounded-full border border-gray-700 flex items-center justify-center mb-8 text-xl font-mono text-white">
58
+ {number}
59
+ </div>
60
+ <h2 className="text-4xl md:text-5xl font-semibold mb-6 text-white">{title}</h2>
61
+ <div className="text-xl text-gray-400 leading-relaxed max-w-lg">
62
+ {description}
63
+ </div>
64
+ </div>
65
+
66
+ {/* Visual Content */}
67
+ <div className="order-1 lg:order-2 relative h-[500px] bg-gray-900/20 border border-gray-800 rounded-3xl overflow-hidden flex items-center justify-center">
68
+ <div className="absolute inset-0 w-full h-full flex items-center justify-center p-0">
69
+ {visual}
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </section>
74
+ );
75
+ };
76
+
77
+ // --- Step 1: Generate Focus Group ---
78
+
79
+ const Step1 = () => {
80
+ const [status, setStatus] = useState<'input' | 'creating' | 'ready'>('input');
81
+ const [inputValue, setInputValue] = useState("AI-focused startup founders in Europe");
82
+
83
+ const handleCreate = () => {
84
+ setStatus('creating');
85
+ setTimeout(() => {
86
+ setStatus('ready');
87
+ }, 2000);
88
+ };
89
+
90
+ return (
91
+ <SectionLayout
92
+ number="1"
93
+ title="Generate Any Focus Group"
94
+ description={
95
+ <div className="space-y-6">
96
+ <p>
97
+ Use plain english to describe your target audience, or generate a personal focus group based on your real social media interactions.
98
+ </p>
99
+ <div className="space-y-2 pt-2">
100
+ <p className="text-base font-medium text-gray-300">
101
+ Personalize your experience with your own LinkedIn network database.
102
+ </p>
103
+ <p className="text-sm text-gray-500">
104
+ LinkedIn and X data are going into the persona simulator.
105
+ </p>
106
+ </div>
107
+ </div>
108
+ }
109
+ visual={
110
+ <>
111
+ {/* Background for all phases, fully visible in 'ready' */}
112
+ <div className={`transition-opacity duration-1000 ${status === 'ready' ? 'opacity-100' : 'opacity-30'}`}>
113
+ <NetworkBackground className="opacity-100" />
114
+ </div>
115
+
116
+ {/* Input State */}
117
+ <div className={`transition-all duration-500 absolute w-full max-w-sm z-20 ${status === 'input' ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'}`}>
118
+ <div className="bg-black border border-gray-800 rounded-xl p-6 shadow-2xl">
119
+ <div className="flex justify-between items-center mb-6">
120
+ <span className="text-gray-400 text-sm">Target Audience</span>
121
+ <button className="text-gray-500 hover:text-white"><X size={16}/></button>
122
+ </div>
123
+ <h4 className="text-xl mb-4 text-center">Who would you like to simulate?</h4>
124
+ <div className="relative mb-6">
125
+ <input
126
+ type="text"
127
+ value={inputValue}
128
+ onChange={(e) => setInputValue(e.target.value)}
129
+ className="w-full bg-gray-900 border border-gray-700 rounded-lg py-3 px-4 text-white focus:outline-none focus:border-teal-500"
130
+ />
131
+ <span className="absolute right-3 top-3.5 w-0.5 h-5 bg-teal-500 animate-blink"></span>
132
+ </div>
133
+ <Button className="w-full py-3" onClick={handleCreate}>
134
+ Generate Your Focus Group <Sparkles size={16} className="ml-2" />
135
+ </Button>
136
+ </div>
137
+ </div>
138
+
139
+ {/* Creating State */}
140
+ <div className={`transition-all duration-500 absolute z-20 ${status === 'creating' ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none'}`}>
141
+ <div className="bg-black/80 backdrop-blur-md border border-gray-700 rounded-full px-8 py-4 flex items-center gap-3 shadow-2xl">
142
+ <Sparkles className="text-teal-400 animate-pulse" />
143
+ <span className="text-lg font-medium">Generating Focus Group...</span>
144
+ </div>
145
+ </div>
146
+
147
+ {/* Ready State */}
148
+ <div className={`transition-all duration-1000 absolute inset-0 flex items-end justify-center pb-8 ${status === 'ready' ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}>
149
+ {/* Bottom Toast Overlay */}
150
+ <div className="bg-black/80 backdrop-blur border border-gray-800 rounded-full pl-3 pr-6 py-2 flex items-center gap-3 shadow-2xl pointer-events-auto">
151
+ <div className="bg-green-500/20 rounded-full p-1">
152
+ <Check className="w-4 h-4 text-green-500" />
153
+ </div>
154
+ <span className="text-sm font-medium text-white whitespace-nowrap">Your Personal Focus Group is Ready</span>
155
+ </div>
156
+ <div className="absolute bottom-2 text-center pointer-events-auto">
157
+ <button
158
+ onClick={() => setStatus('input')}
159
+ className="text-xs text-gray-500 hover:text-white transition-colors"
160
+ >
161
+ Start Over
162
+ </button>
163
+ </div>
164
+ </div>
165
+ </>
166
+ }
167
+ />
168
+ );
169
+ };
170
+
171
+ // --- Step 2: Run Experiments ---
172
+
173
+ const Step2 = () => {
174
+ const [isSimulating, setIsSimulating] = useState(false);
175
+
176
+ const handleSimulate = () => {
177
+ setIsSimulating(true);
178
+ setTimeout(() => {
179
+ setIsSimulating(false);
180
+ }, 2500);
181
+ };
182
+
183
+ return (
184
+ <SectionLayout
185
+ number="2"
186
+ title="Run Rapid Experiments"
187
+ description="Execute simulations in minutes to find the optimal form of your content or idea."
188
+ visual={
189
+ <>
190
+ <NetworkBackground />
191
+ <div className="w-full max-w-sm relative z-10">
192
+ {/* Post Card */}
193
+ <div className={`bg-black border border-gray-800 rounded-xl p-6 shadow-2xl transition-all duration-500 ${isSimulating ? 'opacity-40 blur-sm scale-95' : 'opacity-100 scale-100'}`}>
194
+ <div className="flex items-center gap-3 mb-4">
195
+ <div className="w-10 h-10 rounded-full bg-gray-700"></div>
196
+ <div>
197
+ <div className="w-24 h-3 bg-gray-700 rounded mb-1"></div>
198
+ <div className="w-16 h-2 bg-gray-800 rounded"></div>
199
+ </div>
200
+ </div>
201
+ <div className="space-y-2 mb-6">
202
+ <div className="w-full h-2 bg-gray-800 rounded"></div>
203
+ <div className="w-full h-2 bg-gray-800 rounded"></div>
204
+ <div className="w-3/4 h-2 bg-gray-800 rounded"></div>
205
+ </div>
206
+ <div className="bg-gray-900 rounded-lg p-4 mb-4 border border-gray-800">
207
+ <p className="text-gray-300 text-sm">
208
+ We just secured $5.3M to build AI-native tools...
209
+ </p>
210
+ </div>
211
+ <Button
212
+ className="w-full flex items-center justify-center gap-2"
213
+ onClick={handleSimulate}
214
+ >
215
+ Simulate Post <Send size={16} />
216
+ </Button>
217
+ </div>
218
+
219
+ {/* Simulating Overlay */}
220
+ <div className={`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transition-all duration-300 ${isSimulating ? 'opacity-100 scale-100' : 'opacity-0 scale-90 pointer-events-none'}`}>
221
+ <div className="bg-black/90 backdrop-blur border border-gray-700 rounded-2xl p-6 shadow-2xl flex flex-col items-center gap-4 min-w-[200px]">
222
+ <div className="relative">
223
+ <div className="w-12 h-12 rounded-full border-2 border-gray-800 border-t-teal-500 animate-spin"></div>
224
+ <Sparkles className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-teal-500 w-5 h-5" />
225
+ </div>
226
+ <span className="text-white font-medium">Simulating reactions...</span>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </>
231
+ }
232
+ />
233
+ );
234
+ };
235
+
236
+ // --- Step 3: Get Insights ---
237
+
238
+ const Step3 = () => {
239
+ return (
240
+ <SectionLayout
241
+ number="3"
242
+ title="Get Instant Insights"
243
+ description="Evaluate the performance of your experiment with scores, comments, and summaries."
244
+ visual={
245
+ <>
246
+ <NetworkBackground />
247
+ <div className="w-full max-w-sm space-y-4 relative z-10">
248
+ {/* Score Card */}
249
+ <div className="bg-black border border-gray-800 rounded-xl p-6 shadow-2xl hover:border-gray-600 transition-colors cursor-default group">
250
+ <div className="flex justify-between items-start mb-6">
251
+ <div>
252
+ <span className="text-xs text-gray-400 uppercase tracking-wider">Impact Score</span>
253
+ <div className="text-3xl font-bold text-white mt-1">88<span className="text-base font-normal text-gray-500">/100</span></div>
254
+ </div>
255
+ <div className="bg-green-500/10 text-green-400 text-xs px-2 py-1 rounded border border-green-500/20">Exceptional</div>
256
+ </div>
257
+
258
+ <div className="space-y-4">
259
+ <div>
260
+ <div className="flex justify-between text-xs text-gray-400 mb-1">
261
+ <span>Attention</span>
262
+ <span>80%</span>
263
+ </div>
264
+ <div className="h-1.5 bg-gray-800 rounded-full overflow-hidden">
265
+ <div className="h-full bg-green-500 w-[80%] rounded-full group-hover:bg-green-400 transition-colors"></div>
266
+ </div>
267
+ </div>
268
+ <div>
269
+ <div className="flex justify-between text-xs text-gray-400 mb-1">
270
+ <span>Relevance</span>
271
+ <span>92%</span>
272
+ </div>
273
+ <div className="h-1.5 bg-gray-800 rounded-full overflow-hidden">
274
+ <div className="h-full bg-teal-500 w-[92%] rounded-full group-hover:bg-teal-400 transition-colors"></div>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ {/* Feedback Card */}
281
+ <div className="bg-gray-900/50 border border-gray-800 rounded-xl p-5 backdrop-blur-sm animate-fade-in-up" style={{ animationDelay: '0.2s' }}>
282
+ <div className="flex items-center gap-2 mb-3">
283
+ <MessageSquare size={14} className="text-purple-400" />
284
+ <span className="text-xs font-medium text-purple-200">Key Insight</span>
285
+ </div>
286
+ <p className="text-sm text-gray-300 leading-relaxed italic">
287
+ "Founders in the EU region responded strongly to the 'no-code' angle, seeing it as a major time-saver."
288
+ </p>
289
+ </div>
290
+ </div>
291
+ </>
292
+ }
293
+ />
294
+ );
295
+ };
296
+
297
+ // --- Step 4: Forecast Outcome ---
298
+
299
+ const Step4 = () => {
300
+ const [activeVariant, setActiveVariant] = useState(0);
301
+
302
+ const variants = [
303
+ { label: "Original", score: 48, text: "We just secured $5.3M to build AI-native tools..." },
304
+ { label: "Variant 1", score: 88, text: "Stop writing code before you have product-market fit. We just raised $5.3M to help you simulate it first." },
305
+ { label: "Variant 2", score: 83, text: "Big news: $5.3M raised! We're building the future of founder tools in Europe." },
306
+ ];
307
+
308
+ return (
309
+ <SectionLayout
310
+ number="4"
311
+ title="Forecast Every Outcome"
312
+ description="SyncUsers uses your style to generate and test variations of your original post every time you run a simulation."
313
+ visual={
314
+ <div className="relative w-full max-w-lg flex items-center justify-center">
315
+ <NetworkBackground />
316
+
317
+ {/* Main Content Area */}
318
+ <div className="w-full max-w-xs bg-black border border-gray-800 rounded-xl p-6 shadow-2xl relative z-10 transition-all duration-300">
319
+ <div className="flex justify-between items-center mb-4">
320
+ <span className="text-xs text-gray-500 uppercase tracking-wide">{variants[activeVariant].label}</span>
321
+ <div className={`px-2 py-0.5 rounded text-xs font-bold ${activeVariant === 0 ? 'bg-gray-800 text-gray-400' : 'bg-green-900 text-green-400'}`}>
322
+ Score: {variants[activeVariant].score}
323
+ </div>
324
+ </div>
325
+ <div className="min-h-[100px]">
326
+ <p className="text-sm text-gray-200 leading-relaxed animate-fade-in">
327
+ {variants[activeVariant].text}
328
+ </p>
329
+ </div>
330
+ <div className="mt-4 pt-4 border-t border-gray-800 flex gap-4">
331
+ <div className="h-2 w-8 bg-gray-800 rounded-full"></div>
332
+ <div className="h-2 w-16 bg-gray-800 rounded-full"></div>
333
+ </div>
334
+ </div>
335
+
336
+ {/* Floating Menu */}
337
+ <div className="absolute -right-4 top-1/2 -translate-y-1/2 translate-x-1/2 w-48 bg-gray-900/90 backdrop-blur border border-gray-700 rounded-xl p-2 shadow-2xl z-20">
338
+ <div className="text-[10px] text-gray-500 uppercase px-2 mb-2 font-bold tracking-wider">Select Variant</div>
339
+ <div className="space-y-1">
340
+ {variants.map((v, i) => (
341
+ <div
342
+ key={i}
343
+ onMouseEnter={() => setActiveVariant(i)}
344
+ className={`p-2 rounded cursor-pointer transition-all flex justify-between items-center group ${activeVariant === i ? 'bg-white text-black' : 'hover:bg-white/10 text-gray-400'}`}
345
+ >
346
+ <span className="text-xs font-medium">{v.label}</span>
347
+ <span className={`text-xs font-bold ${activeVariant === i ? 'text-black' : 'text-gray-500 group-hover:text-white'}`}>{v.score}</span>
348
+ </div>
349
+ ))}
350
+ </div>
351
+ </div>
352
+
353
+ {/* Connecting Line Visual */}
354
+ <div className="absolute right-[50%] top-1/2 w-[30%] h-[1px] bg-gradient-to-r from-transparent to-gray-700 -z-10"></div>
355
+ </div>
356
+ }
357
+ />
358
+ );
359
+ };
360
+
361
+ // --- Main Container ---
362
+
363
+ const InteractiveDemo: React.FC = () => {
364
+ return (
365
+ <div className="flex flex-col">
366
+ <Step1 />
367
+ <Step2 />
368
+ <Step3 />
369
+ <Step4 />
370
+ </div>
371
+ );
372
+ };
373
+
374
+ export default InteractiveDemo;
components/Navbar.tsx ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Menu, X } from 'lucide-react';
3
+ import { NAV_LINKS } from '../constants';
4
+ import Button from './ui/Button';
5
+
6
+ interface NavbarProps {
7
+ onStart?: () => void;
8
+ }
9
+
10
+ const Navbar: React.FC<NavbarProps> = ({ onStart }) => {
11
+ const [isScrolled, setIsScrolled] = useState(false);
12
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
13
+
14
+ useEffect(() => {
15
+ const handleScroll = () => {
16
+ setIsScrolled(window.scrollY > 20);
17
+ };
18
+ window.addEventListener('scroll', handleScroll);
19
+ return () => window.removeEventListener('scroll', handleScroll);
20
+ }, []);
21
+
22
+ return (
23
+ <nav className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${isScrolled ? 'bg-black/80 backdrop-blur-md py-3 border-b border-gray-800' : 'bg-transparent py-5'}`}>
24
+ <div className="max-w-7xl mx-auto px-6 flex items-center justify-between">
25
+ <div className="flex items-center gap-2 cursor-pointer" onClick={() => window.scrollTo(0,0)}>
26
+ <div className="w-8 h-8 flex items-center justify-center font-bold text-xl text-white">
27
+ {/* Stylized Logo Mockup */}
28
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="w-full h-full">
29
+ <path d="M12 2L2 22h20L12 2z" className="text-gray-400" />
30
+ <path d="M12 6L6 20h12L12 6z" className="text-white fill-white" />
31
+ </svg>
32
+ </div>
33
+ <span className="font-semibold text-lg tracking-tight">SyncUsers</span>
34
+ </div>
35
+
36
+ {/* Desktop Nav */}
37
+ <div className="hidden md:flex items-center gap-8">
38
+ {NAV_LINKS.map((link) => (
39
+ <a
40
+ key={link.label}
41
+ href={link.href}
42
+ className="text-sm font-medium text-gray-300 hover:text-white transition-colors"
43
+ >
44
+ {link.label}
45
+ </a>
46
+ ))}
47
+ </div>
48
+
49
+ <div className="hidden md:block">
50
+ <Button variant="primary" size="sm" onClick={onStart}>Start Building</Button>
51
+ </div>
52
+
53
+ {/* Mobile Toggle */}
54
+ <button
55
+ className="md:hidden text-gray-300 hover:text-white"
56
+ onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
57
+ >
58
+ {isMobileMenuOpen ? <X /> : <Menu />}
59
+ </button>
60
+ </div>
61
+
62
+ {/* Mobile Menu */}
63
+ {isMobileMenuOpen && (
64
+ <div className="md:hidden bg-black border-t border-gray-800 p-6 absolute top-full left-0 right-0">
65
+ <div className="flex flex-col gap-4">
66
+ {NAV_LINKS.map((link) => (
67
+ <a
68
+ key={link.label}
69
+ href={link.href}
70
+ className="text-lg font-medium text-gray-300 hover:text-white"
71
+ onClick={() => setIsMobileMenuOpen(false)}
72
+ >
73
+ {link.label}
74
+ </a>
75
+ ))}
76
+ <div className="pt-4">
77
+ <Button variant="primary" className="w-full" onClick={() => { setIsMobileMenuOpen(false); if(onStart) onStart(); }}>Start Building</Button>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ )}
82
+ </nav>
83
+ );
84
+ };
85
+
86
+ export default Navbar;
components/Pricing.tsx ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import Button from './ui/Button';
3
+ import { Check } from 'lucide-react';
4
+
5
+ const Pricing: React.FC = () => {
6
+ const [isYearly, setIsYearly] = useState(false);
7
+
8
+ const plans = [
9
+ {
10
+ name: "Free",
11
+ price: "$0",
12
+ desc: "No cost to run your first experiments",
13
+ features: ["Access all features", "Configure your own focus groups", "3 Starting simulation credits"],
14
+ cta: "Select Free",
15
+ featured: false
16
+ },
17
+ {
18
+ name: "Pro",
19
+ price: "$55",
20
+ period: "/ mo",
21
+ desc: "Billed monthly",
22
+ features: ["Everything in Free", "Unlimited focus groups", "Unlimited simulation credits"],
23
+ cta: "Select Pro",
24
+ featured: true
25
+ },
26
+ {
27
+ name: "Enterprise",
28
+ price: "Get in Touch",
29
+ desc: "Custom builds for businesses",
30
+ features: ["Custom audience builds", "Custom contexts & segments", "Data & CRM integration", "API Access", "Dedicated Account Manager"],
31
+ cta: "Enquire",
32
+ featured: false
33
+ }
34
+ ];
35
+
36
+ return (
37
+ <section id="pricing" className="py-24 bg-black">
38
+ <div className="max-w-7xl mx-auto px-6">
39
+ <div className="text-center mb-16">
40
+ <h2 className="text-4xl md:text-5xl font-semibold mb-6">Get started today</h2>
41
+
42
+ {/* Toggle */}
43
+ <div className="inline-flex items-center bg-gray-900 rounded-full p-1 border border-gray-800 relative">
44
+ <button
45
+ className={`px-6 py-2 rounded-full text-sm font-medium transition-all ${!isYearly ? 'bg-white text-black' : 'text-gray-400 hover:text-white'}`}
46
+ onClick={() => setIsYearly(false)}
47
+ >
48
+ Monthly
49
+ </button>
50
+ <button
51
+ className={`px-6 py-2 rounded-full text-sm font-medium transition-all ${isYearly ? 'bg-white text-black' : 'text-gray-400 hover:text-white'}`}
52
+ onClick={() => setIsYearly(true)}
53
+ >
54
+ Yearly
55
+ </button>
56
+ <span className="absolute -top-3 -right-6 bg-teal-900 text-teal-200 text-[10px] font-bold px-2 py-0.5 rounded-full border border-teal-700">
57
+ -30%
58
+ </span>
59
+ </div>
60
+ </div>
61
+
62
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
63
+ {plans.map((plan, idx) => (
64
+ <div
65
+ key={idx}
66
+ className={`flex flex-col p-8 rounded-2xl border ${plan.featured ? 'border-gray-600 bg-gray-900/40 relative' : 'border-gray-800 bg-black'}`}
67
+ >
68
+ <div className="mb-8">
69
+ <h3 className="text-lg font-medium text-white mb-2">{plan.name}</h3>
70
+ <p className="text-gray-500 text-sm">{plan.name === 'Free' ? 'Get started with simulations' : plan.name === 'Pro' ? 'For founders, creators and builders' : 'Custom builds for businesses'}</p>
71
+ </div>
72
+
73
+ <div className="mb-8">
74
+ <div className="flex items-baseline">
75
+ <span className="text-4xl font-bold text-white">{plan.price}</span>
76
+ {plan.period && <span className="text-gray-400 ml-1">{plan.period}</span>}
77
+ </div>
78
+ <p className="text-gray-500 text-sm mt-2">{plan.desc}</p>
79
+ </div>
80
+
81
+ <div className="flex-grow space-y-4 mb-8">
82
+ {plan.features.map((feature, fIdx) => (
83
+ <div key={fIdx} className="flex items-start gap-3">
84
+ <Check className="w-5 h-5 text-white shrink-0" />
85
+ <span className="text-gray-300 text-sm">{feature}</span>
86
+ </div>
87
+ ))}
88
+ </div>
89
+
90
+ <Button
91
+ variant={plan.featured ? 'primary' : 'outline'}
92
+ className="w-full"
93
+ >
94
+ {plan.cta}
95
+ </Button>
96
+ </div>
97
+ ))}
98
+ </div>
99
+ </div>
100
+ </section>
101
+ );
102
+ };
103
+
104
+ export default Pricing;
components/ProductOverview.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Target, Maximize, Zap, Code2 } from 'lucide-react';
3
+
4
+ const ProductOverview: React.FC = () => {
5
+ const features = [
6
+ {
7
+ icon: <Target className="w-6 h-6" />,
8
+ title: "Targeted",
9
+ desc: "Accurately model even hard-to-reach audiences via API."
10
+ },
11
+ {
12
+ icon: <Maximize className="w-6 h-6" />,
13
+ title: "Scalable",
14
+ desc: "Run thousands of concurrent simulations."
15
+ },
16
+ {
17
+ icon: <Zap className="w-6 h-6" />,
18
+ title: "Low Latency",
19
+ desc: "Get actionable insights in your pipeline in seconds."
20
+ },
21
+ {
22
+ icon: <Code2 className="w-6 h-6" />,
23
+ title: "Free for Devs",
24
+ desc: "Generous free tier to build and test your integration."
25
+ }
26
+ ];
27
+
28
+ return (
29
+ <section id="features" className="py-24 bg-black">
30
+ <div className="max-w-7xl mx-auto px-6">
31
+ <div className="flex justify-center mb-8">
32
+ <span className="px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300">Product Overview</span>
33
+ </div>
34
+
35
+ <h2 className="text-3xl md:text-5xl font-semibold text-center max-w-4xl mx-auto leading-tight mb-20">
36
+ Create realistic simulations of your target audience to instantly test messages, content, or ideas
37
+ </h2>
38
+
39
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
40
+ {features.map((feature, idx) => (
41
+ <div key={idx} className="p-8 border border-gray-800 rounded-2xl hover:border-gray-600 transition-colors bg-gray-900/20">
42
+ <div className="w-12 h-12 rounded-full border border-gray-700 flex items-center justify-center mb-6 text-white">
43
+ {feature.icon}
44
+ </div>
45
+ <h3 className="text-xl font-medium mb-3">{feature.title}</h3>
46
+ <p className="text-gray-400 leading-relaxed">{feature.desc}</p>
47
+ </div>
48
+ ))}
49
+ </div>
50
+ </div>
51
+ </section>
52
+ );
53
+ };
54
+
55
+ export default ProductOverview;
components/RealNetworkGraph.tsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useEffect, useRef } from 'react';
3
+ import Plotly from 'plotly.js';
4
+
5
+ const RealNetworkGraph: React.FC = () => {
6
+ const graphDiv = useRef<HTMLDivElement>(null);
7
+
8
+ useEffect(() => {
9
+ if (!graphDiv.current) return;
10
+
11
+ // --- Data Generation Logic ---
12
+ const N = 80; // Node count
13
+ const radius = 0.22; // Connection threshold
14
+ const nodes = [];
15
+
16
+ // 1. Create nodes
17
+ for (let i = 0; i < N; i++) {
18
+ nodes.push({
19
+ x: Math.random(),
20
+ y: Math.random(),
21
+ connections: 0
22
+ });
23
+ }
24
+
25
+ // 2. Create edges
26
+ const edgeX: (number | null)[] = [];
27
+ const edgeY: (number | null)[] = [];
28
+
29
+ for (let i = 0; i < N; i++) {
30
+ for (let j = i + 1; j < N; j++) {
31
+ const dx = nodes[i].x - nodes[j].x;
32
+ const dy = nodes[i].y - nodes[j].y;
33
+ const dist = Math.sqrt(dx * dx + dy * dy);
34
+
35
+ if (dist < radius) {
36
+ nodes[i].connections++;
37
+ nodes[j].connections++;
38
+ edgeX.push(nodes[i].x, nodes[j].x, null);
39
+ edgeY.push(nodes[i].y, nodes[j].y, null);
40
+ }
41
+ }
42
+ }
43
+
44
+ const nodeX = nodes.map(n => n.x);
45
+ const nodeY = nodes.map(n => n.y);
46
+ const nodeColor = nodes.map(n => n.connections);
47
+
48
+ // --- Plotly Configuration ---
49
+
50
+ const edgeTrace = {
51
+ x: edgeX,
52
+ y: edgeY,
53
+ mode: 'lines',
54
+ line: { width: 0.5, color: '#4b5563' }, // gray-600
55
+ hoverinfo: 'none',
56
+ type: 'scatter'
57
+ };
58
+
59
+ const nodeTrace = {
60
+ x: nodeX,
61
+ y: nodeY,
62
+ mode: 'markers',
63
+ hoverinfo: 'none',
64
+ marker: {
65
+ showscale: false,
66
+ colorscale: 'Viridis',
67
+ color: nodeColor,
68
+ size: 8,
69
+ line: { width: 0 }
70
+ },
71
+ type: 'scatter'
72
+ };
73
+
74
+ const layout = {
75
+ showlegend: false,
76
+ hovermode: false,
77
+ margin: { b: 0, l: 0, r: 0, t: 0 },
78
+ xaxis: {
79
+ showgrid: false,
80
+ zeroline: false,
81
+ showticklabels: false,
82
+ range: [-0.05, 1.05],
83
+ fixedrange: true
84
+ },
85
+ yaxis: {
86
+ showgrid: false,
87
+ zeroline: false,
88
+ showticklabels: false,
89
+ range: [-0.05, 1.05],
90
+ fixedrange: true
91
+ },
92
+ paper_bgcolor: 'rgba(0,0,0,0)',
93
+ plot_bgcolor: 'rgba(0,0,0,0)',
94
+ autosize: true,
95
+ dragmode: false
96
+ };
97
+
98
+ const config = {
99
+ staticPlot: true, // Disables all interactions for a cleaner background look
100
+ displayModeBar: false,
101
+ responsive: true
102
+ };
103
+
104
+ // @ts-ignore - Plotly types can be tricky with ESM imports
105
+ Plotly.newPlot(graphDiv.current, [edgeTrace, nodeTrace], layout, config);
106
+
107
+ // Handle Resize
108
+ const resizeObserver = new ResizeObserver(() => {
109
+ if (graphDiv.current) {
110
+ // @ts-ignore
111
+ Plotly.Plots.resize(graphDiv.current);
112
+ }
113
+ });
114
+ resizeObserver.observe(graphDiv.current);
115
+
116
+ return () => {
117
+ resizeObserver.disconnect();
118
+ // @ts-ignore
119
+ if (graphDiv.current) Plotly.purge(graphDiv.current);
120
+ };
121
+ }, []);
122
+
123
+ return (
124
+ <div ref={graphDiv} className="w-full h-full" />
125
+ );
126
+ };
127
+
128
+ export default RealNetworkGraph;
components/SimulationGraph.tsx ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import Plotly from 'plotly.js';
3
+ import { X, Linkedin, Globe, MapPin, User, Briefcase, Users } from 'lucide-react';
4
+ import Button from './ui/Button';
5
+
6
+ interface SimulationGraphProps {
7
+ isBuilding: boolean;
8
+ societyType: string;
9
+ onStartChat?: () => void;
10
+ }
11
+
12
+ const SimulationGraph: React.FC<SimulationGraphProps> = ({ isBuilding, societyType, onStartChat }) => {
13
+ const graphDiv = useRef<HTMLDivElement>(null);
14
+ const [selectedProfile, setSelectedProfile] = useState<{ x: number, y: number, data: any } | null>(null);
15
+
16
+ // Close popup if building starts
17
+ useEffect(() => {
18
+ if (isBuilding) setSelectedProfile(null);
19
+ }, [isBuilding]);
20
+
21
+ useEffect(() => {
22
+ if (!graphDiv.current || isBuilding) return;
23
+
24
+ // --- Dynamic Data Generation based on Society Type ---
25
+ const isTech = societyType.includes('Tech') || societyType.includes('Founders');
26
+
27
+ const N = isTech ? 120 : 80;
28
+ const radius = isTech ? 0.18 : 0.22;
29
+ const nodes = [];
30
+
31
+ // Create nodes
32
+ for (let i = 0; i < N; i++) {
33
+ nodes.push({
34
+ x: Math.random(),
35
+ y: Math.random(),
36
+ connections: 0,
37
+ // Mock data for the popup
38
+ role: isTech ? ['Founder', 'CTO', 'Product Lead', 'VC'][Math.floor(Math.random() * 4)] : ['Journalist', 'Reader', 'Editor', 'Subscriber'][Math.floor(Math.random() * 4)],
39
+ location: ['New York, USA', 'London, UK', 'Berlin, DE', 'Paris, FR'][Math.floor(Math.random() * 4)]
40
+ });
41
+ }
42
+
43
+ // Create edges
44
+ const edgeX: (number | null)[] = [];
45
+ const edgeY: (number | null)[] = [];
46
+
47
+ for (let i = 0; i < N; i++) {
48
+ for (let j = i + 1; j < N; j++) {
49
+ const dx = nodes[i].x - nodes[j].x;
50
+ const dy = nodes[i].y - nodes[j].y;
51
+ const dist = Math.sqrt(dx * dx + dy * dy);
52
+
53
+ if (dist < radius) {
54
+ nodes[i].connections++;
55
+ nodes[j].connections++;
56
+ edgeX.push(nodes[i].x, nodes[j].x, null);
57
+ edgeY.push(nodes[i].y, nodes[j].y, null);
58
+ }
59
+ }
60
+ }
61
+
62
+ const nodeX = nodes.map(n => n.x);
63
+ const nodeY = nodes.map(n => n.y);
64
+ const nodeColor = nodes.map(n => n.connections);
65
+
66
+ // --- Plotly Config ---
67
+ const edgeTrace = {
68
+ x: edgeX,
69
+ y: edgeY,
70
+ mode: 'lines',
71
+ line: { width: 0.5, color: '#4b5563' },
72
+ hoverinfo: 'none',
73
+ type: 'scatter'
74
+ };
75
+
76
+ const nodeTrace = {
77
+ x: nodeX,
78
+ y: nodeY,
79
+ mode: 'markers',
80
+ hoverinfo: 'none',
81
+ marker: {
82
+ showscale: false,
83
+ colorscale: isTech ? 'Electric' : 'Viridis',
84
+ color: nodeColor,
85
+ size: 10,
86
+ line: { width: 0 }
87
+ },
88
+ type: 'scatter'
89
+ };
90
+
91
+ const layout = {
92
+ showlegend: false,
93
+ hovermode: 'closest',
94
+ margin: { b: 0, l: 0, r: 0, t: 0 },
95
+ xaxis: { showgrid: false, zeroline: false, showticklabels: false, range: [-0.05, 1.05], fixedrange: true },
96
+ yaxis: { showgrid: false, zeroline: false, showticklabels: false, range: [-0.05, 1.05], fixedrange: true },
97
+ paper_bgcolor: 'rgba(0,0,0,0)',
98
+ plot_bgcolor: 'rgba(0,0,0,0)',
99
+ autosize: true,
100
+ dragmode: false
101
+ };
102
+
103
+ const config = {
104
+ staticPlot: false,
105
+ displayModeBar: false,
106
+ responsive: true
107
+ };
108
+
109
+ // @ts-ignore
110
+ Plotly.newPlot(graphDiv.current, [edgeTrace, nodeTrace], layout, config).then((gd) => {
111
+ // @ts-ignore
112
+ gd.on('plotly_click', (data) => {
113
+ const point = data.points[0];
114
+ if (point) {
115
+ const nodeIndex = point.pointNumber;
116
+ const nodeData = nodes[nodeIndex];
117
+
118
+ setSelectedProfile({
119
+ x: point.x,
120
+ y: point.y,
121
+ data: nodeData
122
+ });
123
+ }
124
+ });
125
+ });
126
+
127
+ }, [isBuilding, societyType]);
128
+
129
+ return (
130
+ <div className="relative w-full h-full bg-black">
131
+ {/* Loading Overlay */}
132
+ {isBuilding && (
133
+ <div className="absolute inset-0 z-50 flex flex-col items-center justify-center bg-black/80 backdrop-blur-sm transition-opacity duration-300">
134
+ <div className="w-16 h-16 border-4 border-teal-900 border-t-teal-500 rounded-full animate-spin mb-4"></div>
135
+ <p className="text-teal-400 font-mono animate-pulse">Constructing Focus Group Graph...</p>
136
+ </div>
137
+ )}
138
+
139
+ {/* The Graph */}
140
+ <div ref={graphDiv} className="w-full h-full" />
141
+
142
+ {/* Profile Popup */}
143
+ {selectedProfile && !isBuilding && (
144
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 bg-gray-900/90 backdrop-blur-md border border-gray-700 rounded-2xl shadow-2xl overflow-hidden z-40 animate-in fade-in zoom-in-95 duration-200">
145
+ {/* Header */}
146
+ <div className="p-4 border-b border-gray-800 flex justify-between items-start">
147
+ <div className="flex items-center gap-3">
148
+ <div className="w-10 h-10 rounded-full bg-gradient-to-br from-teal-400 to-blue-600 flex items-center justify-center text-white font-bold text-lg">
149
+ {selectedProfile.data.role[0]}
150
+ </div>
151
+ <div>
152
+ <h3 className="text-white font-semibold text-sm">{selectedProfile.data.role}</h3>
153
+ <p className="text-gray-400 text-xs">Head of Product at BrightCore</p>
154
+ </div>
155
+ </div>
156
+ <button
157
+ onClick={() => setSelectedProfile(null)}
158
+ className="text-gray-500 hover:text-white"
159
+ >
160
+ <X size={16} />
161
+ </button>
162
+ </div>
163
+
164
+ {/* Body */}
165
+ <div className="p-4 space-y-4">
166
+ <div className="flex items-center gap-2 text-xs text-gray-400">
167
+ <span>Built from</span>
168
+ <Linkedin size={14} className="text-[#0077b5]" />
169
+ <Globe size={14} className="text-gray-300" />
170
+ </div>
171
+
172
+ <div className="flex flex-wrap gap-2">
173
+ <div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-gray-800 border border-gray-700 text-xs text-gray-300">
174
+ <MapPin size={12} /> {selectedProfile.data.location}
175
+ </div>
176
+ <div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-gray-800 border border-gray-700 text-xs text-gray-300">
177
+ <User size={12} /> Millennial
178
+ </div>
179
+ <div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-gray-800 border border-gray-700 text-xs text-gray-300">
180
+ <Briefcase size={12} /> Mid Level
181
+ </div>
182
+ <div className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-gray-800 border border-gray-700 text-xs text-gray-300">
183
+ <Users size={12} /> Creative & Design
184
+ </div>
185
+ </div>
186
+ </div>
187
+
188
+ {/* Footer */}
189
+ <div className="p-4 pt-0">
190
+ <Button
191
+ className="w-full text-sm py-2"
192
+ onClick={onStartChat}
193
+ >
194
+ Start Conversation
195
+ </Button>
196
+ </div>
197
+ </div>
198
+ )}
199
+ </div>
200
+ );
201
+ };
202
+
203
+ export default SimulationGraph;
components/SimulationPage.tsx ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import { ChevronDown, Plus, Info, MessageSquare, BookOpen, LogOut, PanelLeftClose, MessageCircle, AlertTriangle } from 'lucide-react';
3
+ import SimulationGraph from './SimulationGraph';
4
+
5
+ interface SimulationPageProps {
6
+ onBack: () => void;
7
+ onOpenConversation: () => void;
8
+ onOpenChat: () => void;
9
+ }
10
+
11
+ // Define the data structure for filters
12
+ const VIEW_FILTERS: Record<string, Array<{ label: string; color: string }>> = {
13
+ 'Country': [
14
+ { label: "United States", color: "bg-blue-600" },
15
+ { label: "United Kingdom", color: "bg-purple-600" },
16
+ { label: "Netherlands", color: "bg-teal-600" },
17
+ { label: "France", color: "bg-orange-600" },
18
+ { label: "India", color: "bg-pink-600" }
19
+ ],
20
+ 'Job Title': [
21
+ { label: "Founder", color: "bg-indigo-500" },
22
+ { label: "Product Manager", color: "bg-emerald-500" },
23
+ { label: "Engineer", color: "bg-rose-500" },
24
+ { label: "Investor", color: "bg-amber-500" },
25
+ { label: "Designer", color: "bg-fuchsia-500" }
26
+ ],
27
+ 'Sentiment': [
28
+ { label: "Positive", color: "bg-green-500" },
29
+ { label: "Neutral", color: "bg-gray-500" },
30
+ { label: "Negative", color: "bg-red-500" },
31
+ { label: "Mixed", color: "bg-yellow-500" }
32
+ ],
33
+ 'Activity Level': [
34
+ { label: "Power User", color: "bg-red-600" },
35
+ { label: "Daily Active", color: "bg-orange-500" },
36
+ { label: "Weekly Active", color: "bg-blue-500" },
37
+ { label: "Lurker", color: "bg-slate-600" }
38
+ ]
39
+ };
40
+
41
+ const SimulationPage: React.FC<SimulationPageProps> = ({ onBack, onOpenConversation, onOpenChat }) => {
42
+ const [society, setSociety] = useState('User Group 1');
43
+ const [viewMode, setViewMode] = useState('Job Title');
44
+ const [isBuilding, setIsBuilding] = useState(false);
45
+ const [isRightPanelOpen, setIsRightPanelOpen] = useState(true);
46
+
47
+ // Function to simulate rebuilding the graph when settings change
48
+ const handleSettingChange = (setter: (val: string) => void, value: string) => {
49
+ if (value === society || (value === viewMode && setter === setViewMode)) return; // No change
50
+
51
+ setter(value);
52
+ setIsBuilding(true);
53
+ // Simulate network delay
54
+ setTimeout(() => {
55
+ setIsBuilding(false);
56
+ }, 1500);
57
+ };
58
+
59
+ const currentFilters = VIEW_FILTERS[viewMode] || VIEW_FILTERS['Country'];
60
+
61
+ return (
62
+ <div className="flex h-screen w-screen overflow-hidden bg-black text-white font-sans">
63
+ {/* Sidebar */}
64
+ <aside className="w-[300px] flex-shrink-0 border-r border-gray-800 flex flex-col bg-[#0a0a0a] z-20">
65
+ {/* Header */}
66
+ <div className="p-4 h-16 border-b border-gray-800 flex items-center justify-between">
67
+ <div className="flex items-center gap-2 cursor-pointer" onClick={onBack}>
68
+ <div className="w-6 h-6 flex items-center justify-center font-bold text-white">Λ</div>
69
+ <span className="font-semibold tracking-tight">SyncUsers</span>
70
+ </div>
71
+ <button className="text-gray-500 hover:text-white"><PanelLeftClose size={18}/></button>
72
+ </div>
73
+
74
+ {/* Scrollable Content */}
75
+ <div className="flex-1 overflow-y-auto p-4 space-y-6">
76
+
77
+ {/* Current Focus Group Control */}
78
+ <div className="space-y-2">
79
+ <label className="text-xs text-gray-500 font-medium uppercase tracking-wider">Current Focus Group</label>
80
+ <div className="relative group">
81
+ <select
82
+ value={society}
83
+ onChange={(e) => handleSettingChange(setSociety, e.target.value)}
84
+ className="w-full appearance-none bg-[#111] border border-gray-700 text-white rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:border-teal-500 cursor-pointer"
85
+ >
86
+ <option>User Group 1</option>
87
+ </select>
88
+ <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 pointer-events-none w-4 h-4" />
89
+ </div>
90
+ </div>
91
+
92
+ {/* Current View Control */}
93
+ <div className="space-y-2">
94
+ <label className="text-xs text-gray-500 font-medium uppercase tracking-wider">Current View</label>
95
+ <div className="relative">
96
+ <select
97
+ value={viewMode}
98
+ onChange={(e) => handleSettingChange(setViewMode, e.target.value)}
99
+ className="w-full appearance-none bg-[#111] border border-gray-700 text-white rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:border-teal-500 cursor-pointer"
100
+ >
101
+ <option>Country</option>
102
+ <option>Job Title</option>
103
+ <option>Sentiment</option>
104
+ <option>Activity Level</option>
105
+ </select>
106
+ <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 pointer-events-none w-4 h-4" />
107
+ </div>
108
+ </div>
109
+
110
+ <div className="h-px bg-gray-800 my-4" />
111
+
112
+ {/* Actions */}
113
+ <button
114
+ onClick={onOpenConversation}
115
+ className="w-full flex items-center justify-between text-left text-sm text-gray-300 hover:text-white group py-2"
116
+ >
117
+ <span>Create a new test</span>
118
+ <Plus size={18} className="text-gray-500 group-hover:text-white" />
119
+ </button>
120
+
121
+ {/* Global Chat Button (Sidebar) */}
122
+ <button
123
+ onClick={onOpenChat}
124
+ className="w-full flex items-center gap-3 px-4 py-3 bg-gray-800 hover:bg-gray-700 text-white rounded-lg transition-colors border border-gray-700"
125
+ >
126
+ <MessageCircle size={18} />
127
+ <span className="font-medium text-sm">Open Global Chat</span>
128
+ </button>
129
+
130
+ {/* Setup Warning */}
131
+ <div className="bg-amber-900/30 border border-amber-700/50 rounded-xl p-4 mt-4">
132
+ <div className="flex items-center gap-2 text-amber-200 font-bold text-xs mb-1">
133
+ <AlertTriangle size={14}/>
134
+ <span>Action Required</span>
135
+ </div>
136
+ <p className="text-amber-200/70 text-[10px] leading-relaxed">
137
+ Assemble a new group and create a new test before using the chat features.
138
+ </p>
139
+ </div>
140
+
141
+ {/* History List */}
142
+ <div className="space-y-1 pt-4">
143
+ <label className="text-xs text-gray-500 font-medium uppercase tracking-wider mb-2 block">Recent Tests</label>
144
+ <div className="text-sm text-gray-400 py-2 px-2 hover:bg-gray-800/50 rounded cursor-pointer truncate">
145
+ Duality in portraits and sha...
146
+ </div>
147
+ <div className="text-sm text-gray-400 py-2 px-2 hover:bg-gray-800/50 rounded cursor-pointer truncate">
148
+ Volcanoes: Threat or Misun...
149
+ </div>
150
+ <div className="text-sm text-gray-400 py-2 px-2 hover:bg-gray-800/50 rounded cursor-pointer truncate">
151
+ Sustainable Fashion 2024
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ {/* Footer */}
157
+ <div className="border-t border-gray-800 p-4 space-y-1 bg-[#0a0a0a]">
158
+ <div className="flex justify-between items-center py-2 text-sm text-gray-400 border-b border-gray-800 mb-2 pb-4">
159
+ <span>Credits: 0</span>
160
+ <Info size={14} className="cursor-help" />
161
+ </div>
162
+
163
+ <MenuItem icon={<Plus size={16}/>} label="Start Free Trial" highlight />
164
+ <MenuItem icon={<MessageSquare size={16}/>} label="Leave Feedback" />
165
+ <MenuItem icon={<BookOpen size={16}/>} label="Product Guide" />
166
+ <MenuItem icon={<LogOut size={16}/>} label="Log Out" />
167
+
168
+ <div className="pt-4 text-[10px] text-gray-600">Version 2.1</div>
169
+ </div>
170
+ </aside>
171
+
172
+ {/* Main Content Area */}
173
+ <main className="flex-1 flex flex-col relative bg-black overflow-hidden">
174
+ {/* Top Navigation Overlay */}
175
+ <div className="absolute top-6 left-6 right-6 z-10 flex justify-center pointer-events-none">
176
+ {/* Legend / Filter Chips */}
177
+ <div className="flex flex-wrap justify-center gap-2 pointer-events-auto">
178
+ {currentFilters.map((filter, idx) => (
179
+ <FilterChip key={idx} color={filter.color} label={filter.label} />
180
+ ))}
181
+ </div>
182
+ </div>
183
+
184
+ {/* Graph Container */}
185
+ <div className="flex-1 w-full h-full">
186
+ <SimulationGraph isBuilding={isBuilding} societyType={society} onStartChat={onOpenChat} />
187
+ </div>
188
+
189
+ {/* Floating Chat Button (Bottom) */}
190
+ <div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-30">
191
+ <button
192
+ onClick={onOpenChat}
193
+ className="flex items-center gap-2 bg-black/80 backdrop-blur-md border border-gray-700 text-white px-6 py-3 rounded-full shadow-2xl hover:bg-gray-900 transition-all hover:scale-105"
194
+ >
195
+ <MessageCircle size={20} />
196
+ <span className="font-medium">Open Simulation Chat</span>
197
+ </button>
198
+ </div>
199
+ </main>
200
+
201
+ {/* Right Sidebar (Output) */}
202
+ <aside className={`w-[300px] flex-shrink-0 border-l border-gray-800 flex flex-col bg-[#0a0a0a] z-20 transition-all duration-300 ${isRightPanelOpen ? 'mr-0' : '-mr-[300px]'}`}>
203
+ <div className="p-4 h-16 border-b border-gray-800 flex items-center justify-between">
204
+ <span className="font-semibold tracking-tight uppercase text-xs text-gray-500">Output</span>
205
+ <button onClick={() => setIsRightPanelOpen(false)} className="text-gray-500 hover:text-white"><PanelLeftClose size={18} className="rotate-180"/></button>
206
+ </div>
207
+ <div className="flex-1 overflow-y-auto p-4 space-y-4">
208
+ <div className="bg-gray-900/50 border border-gray-800 rounded-xl p-4">
209
+ <p className="text-xs text-gray-500 mb-2">Simulation Results</p>
210
+ <div className="text-sm text-gray-400 italic text-center py-8">
211
+ Results will appear here after running a simulation.
212
+ </div>
213
+ </div>
214
+ </div>
215
+ </aside>
216
+ </div>
217
+ );
218
+ };
219
+
220
+ // Helper Components
221
+ interface MenuItemProps {
222
+ icon: React.ReactNode;
223
+ label: string;
224
+ highlight?: boolean;
225
+ }
226
+
227
+ const MenuItem: React.FC<MenuItemProps> = ({ icon, label, highlight = false }) => (
228
+ <button className={`w-full flex items-center gap-3 px-2 py-2.5 rounded-md text-sm transition-colors ${highlight ? 'text-teal-400 hover:bg-teal-950/30' : 'text-gray-400 hover:bg-gray-800 hover:text-white'}`}>
229
+ {icon}
230
+ <span>{label}</span>
231
+ </button>
232
+ );
233
+
234
+ interface FilterChipProps {
235
+ color: string;
236
+ label: string;
237
+ }
238
+
239
+ const FilterChip: React.FC<FilterChipProps> = ({ color, label }) => (
240
+ <button className="flex items-center gap-2 bg-gray-900/80 backdrop-blur border border-gray-700 rounded-full pl-2 pr-4 py-1.5 hover:border-gray-500 transition-colors">
241
+ <span className={`w-2.5 h-2.5 rounded-full ${color}`}></span>
242
+ <span className="text-xs font-medium text-gray-300">{label}</span>
243
+ </button>
244
+ );
245
+
246
+ export default SimulationPage;
components/Testimonials.tsx ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { TESTIMONIALS } from '../constants';
4
+ import { ArrowLeft, ArrowRight } from 'lucide-react';
5
+
6
+ const Testimonials: React.FC = () => {
7
+ return (
8
+ <section className="py-24 bg-black border-y border-gray-900">
9
+ <div className="max-w-7xl mx-auto px-6">
10
+ <div className="flex justify-center mb-8">
11
+ <span className="px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300">Testimonials</span>
12
+ </div>
13
+ <h2 className="text-3xl md:text-5xl font-semibold text-center mb-16 max-w-3xl mx-auto">
14
+ Join thousands using Agentic User Experience (AUX) to run simulations and test ideas
15
+ </h2>
16
+
17
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
18
+ {TESTIMONIALS.map((t, idx) => (
19
+ <div key={idx} className="bg-gray-900/30 border border-gray-800 p-8 rounded-2xl flex flex-col justify-between h-full">
20
+ <p className="text-xl leading-relaxed text-gray-200 mb-8 font-light">"{t.quote}"</p>
21
+ <div className="flex items-center gap-4">
22
+ <div className="w-12 h-12 rounded-full bg-white text-black flex items-center justify-center font-bold text-lg">
23
+ {t.company[0]}
24
+ </div>
25
+ <div>
26
+ <div className="font-semibold">{t.company}</div>
27
+ <div className="text-sm text-gray-400">{t.author} • {t.role}</div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ ))}
32
+ </div>
33
+
34
+ <div className="flex justify-center gap-4 mt-12">
35
+ <button className="w-12 h-12 rounded-full border border-gray-700 flex items-center justify-center hover:bg-white hover:text-black transition-colors">
36
+ <ArrowLeft size={20} />
37
+ </button>
38
+ <button className="w-12 h-12 rounded-full border border-gray-700 flex items-center justify-center hover:bg-white hover:text-black transition-colors">
39
+ <ArrowRight size={20} />
40
+ </button>
41
+ </div>
42
+ </div>
43
+ </section>
44
+ );
45
+ };
46
+
47
+ export default Testimonials;
components/TrustedBy.tsx ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { CheckCircle, Timer, Clock, ClipboardCheck, Smile, Gauge } from 'lucide-react';
3
+
4
+ const TrustedBy: React.FC = () => {
5
+ const metrics = [
6
+ {
7
+ icon: <CheckCircle size={20} />,
8
+ category: "Task Success Rate",
9
+ question: "Do users complete their goals?",
10
+ outcome: "Identify drop-off points in critical flows."
11
+ },
12
+ {
13
+ icon: <Timer size={20} />,
14
+ category: "Time-on-Task",
15
+ question: "How quickly do they finish?",
16
+ outcome: "Benchmark efficiency against previous versions."
17
+ },
18
+ {
19
+ icon: <Clock size={20} />,
20
+ category: "Session Duration",
21
+ question: "How long do they stay engaged?",
22
+ outcome: "Balance retention with efficient task completion."
23
+ },
24
+ {
25
+ icon: <ClipboardCheck size={20} />,
26
+ category: "System Usability Scale",
27
+ question: "Is the product usable?",
28
+ outcome: "Standardized scoring for overall usability."
29
+ },
30
+ {
31
+ icon: <Smile size={20} />,
32
+ category: "Customer Satisfaction",
33
+ question: "Are users happy with the experience?",
34
+ outcome: "Predict long-term retention and loyalty."
35
+ },
36
+ {
37
+ icon: <Gauge size={20} />,
38
+ category: "Customer Effort Score",
39
+ question: "Is it easy to get things done?",
40
+ outcome: "Reduce friction to increase conversion."
41
+ }
42
+ ];
43
+
44
+ return (
45
+ <section className="py-24 border-t border-gray-800 bg-black">
46
+ <div className="max-w-7xl mx-auto px-6">
47
+ <h3 className="text-center text-3xl md:text-4xl font-semibold text-white mb-6">
48
+ Quantify your UX quality programmatically.
49
+ </h3>
50
+ <p className="text-center text-gray-400 mb-16 max-w-2xl mx-auto text-lg">
51
+ Developers use SyncUsers to predict performance on core metrics without waiting for live traffic data.
52
+ </p>
53
+
54
+ {/* UX Metrics Grid */}
55
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-24">
56
+ {metrics.map((item, idx) => (
57
+ <div key={idx} className="group p-8 rounded-2xl border border-gray-800 bg-gray-900/20 hover:border-teal-900/50 hover:bg-gray-900/40 transition-all duration-300">
58
+ <div className="flex items-center gap-3 mb-5 text-gray-500 group-hover:text-teal-400 transition-colors">
59
+ {item.icon}
60
+ <span className="text-xs font-mono uppercase tracking-widest">{item.category}</span>
61
+ </div>
62
+ <div className="text-xl font-medium text-white mb-3 group-hover:translate-x-1 transition-transform duration-300">
63
+ {item.question}
64
+ </div>
65
+ <p className="text-sm text-gray-500 leading-relaxed group-hover:text-gray-400 transition-colors">
66
+ {item.outcome}
67
+ </p>
68
+ </div>
69
+ ))}
70
+ </div>
71
+
72
+ {/* Stats Grid */}
73
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-4xl mx-auto">
74
+ {[
75
+ { value: "2,000+", label: "Persona Database ready", color: "border-blue-900" },
76
+ { value: "REST & GraphQL", label: "API Available", color: "border-teal-900" },
77
+ ].map((stat, idx) => (
78
+ <div key={idx} className={`relative flex flex-col items-center justify-center py-16 rounded-full border border-t-2 ${stat.color} bg-gradient-to-b from-gray-900 to-black`}>
79
+ <h4 className="text-4xl md:text-5xl font-bold text-white mb-2">{stat.value}</h4>
80
+ <p className="text-gray-400">{stat.label}</p>
81
+ </div>
82
+ ))}
83
+ </div>
84
+ </div>
85
+ </section>
86
+ );
87
+ };
88
+
89
+ export default TrustedBy;
components/UseCases.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { USE_CASES } from '../constants';
3
+
4
+ const UseCases: React.FC = () => {
5
+ return (
6
+ <section id="use-cases" className="py-24 bg-black">
7
+ <div className="max-w-7xl mx-auto px-6">
8
+ <div className="flex justify-center mb-8">
9
+ <span className="px-4 py-1.5 rounded-full border border-gray-700 text-sm text-gray-300">Use Cases</span>
10
+ </div>
11
+ <h2 className="text-3xl md:text-5xl font-semibold text-center mb-20">Optimize any kind of message</h2>
12
+
13
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
14
+ {USE_CASES.map((useCase, idx) => (
15
+ <div key={idx} className="group p-8 border border-gray-800 rounded-2xl bg-gradient-to-br from-gray-900/50 to-black hover:border-gray-600 transition-all duration-300 hover:shadow-lg hover:shadow-gray-900/20">
16
+ <div className="mb-6">
17
+ <span className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-gray-800 text-xs font-medium text-gray-300 border border-gray-700">
18
+ <span className={`w-2 h-2 rounded-full ${useCase.color}`}></span>
19
+ {useCase.category}
20
+ </span>
21
+ </div>
22
+ <h3 className="text-2xl font-semibold mb-3 group-hover:text-white transition-colors">{useCase.title}</h3>
23
+ <p className="text-gray-400 leading-relaxed text-sm">{useCase.description}</p>
24
+ </div>
25
+ ))}
26
+ </div>
27
+ </div>
28
+ </section>
29
+ );
30
+ };
31
+
32
+ export default UseCases;
components/ui/Button.tsx ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+
3
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
5
+ size?: 'sm' | 'md' | 'lg';
6
+ }
7
+
8
+ const Button: React.FC<ButtonProps> = ({
9
+ children,
10
+ variant = 'primary',
11
+ size = 'md',
12
+ className = '',
13
+ ...props
14
+ }) => {
15
+ const baseStyles = "inline-flex items-center justify-center rounded-full font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-black disabled:opacity-50 disabled:pointer-events-none";
16
+
17
+ const variants = {
18
+ primary: "bg-white text-black hover:bg-gray-200 border border-transparent",
19
+ secondary: "bg-gray-800 text-white hover:bg-gray-700 border border-transparent",
20
+ outline: "bg-transparent text-white border border-gray-600 hover:border-gray-400 hover:text-gray-200",
21
+ ghost: "bg-transparent text-white hover:bg-white/10"
22
+ };
23
+
24
+ const sizes = {
25
+ sm: "px-4 py-1.5 text-sm",
26
+ md: "px-6 py-2.5 text-base",
27
+ lg: "px-8 py-3.5 text-lg"
28
+ };
29
+
30
+ return (
31
+ <button
32
+ className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
33
+ {...props}
34
+ >
35
+ {children}
36
+ </button>
37
+ );
38
+ };
39
+
40
+ export default Button;
constants.ts ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NavLink, UseCase, UseCaseCategory, Testimonial, FaqItem } from './types';
2
+
3
+ export const NAV_LINKS: NavLink[] = [
4
+ { label: 'Features', href: '#features' },
5
+ { label: 'Use Cases', href: '#use-cases' },
6
+ { label: 'How it Works', href: '#how-it-works' },
7
+ { label: 'Accuracy', href: '#accuracy' },
8
+ { label: 'Docs', href: '#docs' },
9
+ ];
10
+
11
+ export const USE_CASES: UseCase[] = [
12
+ {
13
+ category: UseCaseCategory.PR_COMMS,
14
+ title: "Craft Narratives",
15
+ description: "Test different communication strategies via API to deliver the right reaction",
16
+ color: "bg-purple-500"
17
+ },
18
+ {
19
+ category: UseCaseCategory.PRODUCT,
20
+ title: "Decide Features",
21
+ description: "Test how your target customers react to product ideas and new features",
22
+ color: "bg-teal-500"
23
+ },
24
+ {
25
+ category: UseCaseCategory.BRANDING,
26
+ title: "Stand Out",
27
+ description: "Test how different brand and voice ideas resonate with your ideal buyer.",
28
+ color: "bg-pink-500"
29
+ },
30
+ {
31
+ category: UseCaseCategory.MARKETING,
32
+ title: "Generate Leads",
33
+ description: "Test marketing content in a simulation of your target customers",
34
+ color: "bg-orange-500"
35
+ },
36
+ {
37
+ category: UseCaseCategory.SOCIAL_MEDIA,
38
+ title: "Make Content",
39
+ description: "Test social content in simulations of your network and audience",
40
+ color: "bg-blue-500"
41
+ },
42
+ {
43
+ category: UseCaseCategory.JOURNALISM,
44
+ title: "Capture Attention",
45
+ description: "Test headlines, thumbnails, and article content to maximise reader attention",
46
+ color: "bg-yellow-500"
47
+ }
48
+ ];
49
+
50
+ export const TESTIMONIALS: Testimonial[] = [];
51
+
52
+ export const FAQS: FaqItem[] = [
53
+ {
54
+ question: "Is the API free for developers?",
55
+ answer: "Yes. We offer a generous free tier specifically designed for developers and hobbyists to build, test, and integrate user simulations without upfront costs."
56
+ },
57
+ {
58
+ question: "What is a Focus Group?",
59
+ answer: "A Focus Group is a simulated collective of AI personas that mirror the behaviors, preferences, and interactions of a specific real-world audience."
60
+ },
61
+ {
62
+ question: "How do credits work in the free tier?",
63
+ answer: "Credits are used to run simulations. One credit equals one simulated interaction. The developer plan includes 1,000 free credits per month, refreshing automatically."
64
+ },
65
+ {
66
+ question: "Can I integrate this into my CI/CD?",
67
+ answer: "Absolutely. Our CLI tool and API are designed to run as part of your testing pipeline, allowing you to validate UX decisions before merging code."
68
+ },
69
+ {
70
+ question: "How do I get an API key?",
71
+ answer: "You need a Huggingface token with a valid account."
72
+ }
73
+ ];
dist/index.html ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>SyncUsers</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Inter', sans-serif;
14
+ background-color: #050505;
15
+ color: #ffffff;
16
+ }
17
+
18
+ /* Custom Scrollbar */
19
+
20
+ ::-webkit-scrollbar {
21
+ width: 8px;
22
+ }
23
+
24
+ ::-webkit-scrollbar-track {
25
+ background: #050505;
26
+ }
27
+
28
+ ::-webkit-scrollbar-thumb {
29
+ background: #333;
30
+ border-radius: 4px;
31
+ }
32
+
33
+ ::-webkit-scrollbar-thumb:hover {
34
+ background: #555;
35
+ }
36
+
37
+ /* Utilities for gradients */
38
+
39
+ .bg-glass {
40
+ background: rgba(255, 255, 255, 0.05);
41
+ backdrop-filter: blur(10px);
42
+ -webkit-backdrop-filter: blur(10px);
43
+ }
44
+
45
+ .gradient-text {
46
+ background: linear-gradient(to right, #fff, #aaa);
47
+ -webkit-background-clip: text;
48
+ -webkit-text-fill-color: transparent;
49
+ }
50
+
51
+ /* Blink animation for cursor */
52
+
53
+ @keyframes blink {
54
+ 0%, 100% { opacity: 1; }
55
+ 50% { opacity: 0; }
56
+ }
57
+
58
+ .animate-blink {
59
+ animation: blink 1s step-end infinite;
60
+ }
61
+ </style>
62
+ <script type="importmap">
63
+ {
64
+ "imports": {
65
+ "react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
66
+ "react/": "https://aistudiocdn.com/react@^19.2.0/",
67
+ "react": "https://aistudiocdn.com/react@^19.2.0",
68
+ "lucide-react": "https://aistudiocdn.com/lucide-react@^0.555.0",
69
+ "recharts": "https://aistudiocdn.com/recharts@^3.5.0",
70
+ "plotly.js": "https://cdn.jsdelivr.net/npm/plotly.js-dist-min@2.30.0/+esm"
71
+ }
72
+ }
73
+ </script>
74
+ </head>
75
+ <body>
76
+ <div id="root"></div>
77
+ </body>
78
+ </html>
index.html CHANGED
@@ -14,28 +14,23 @@
14
  background-color: #050505;
15
  color: #ffffff;
16
  }
17
-
18
  /* Custom Scrollbar */
19
-
20
  ::-webkit-scrollbar {
21
  width: 8px;
22
  }
23
-
24
  ::-webkit-scrollbar-track {
25
  background: #050505;
26
  }
27
-
28
  ::-webkit-scrollbar-thumb {
29
  background: #333;
30
  border-radius: 4px;
31
  }
32
-
33
  ::-webkit-scrollbar-thumb:hover {
34
  background: #555;
35
  }
36
 
37
  /* Utilities for gradients */
38
-
39
  .bg-glass {
40
  background: rgba(255, 255, 255, 0.05);
41
  backdrop-filter: blur(10px);
@@ -49,17 +44,14 @@
49
  }
50
 
51
  /* Blink animation for cursor */
52
-
53
  @keyframes blink {
54
  0%, 100% { opacity: 1; }
55
  50% { opacity: 0; }
56
  }
57
-
58
  .animate-blink {
59
  animation: blink 1s step-end infinite;
60
  }
61
  </style>
62
-
63
  <script type="importmap">
64
  {
65
  "imports": {
@@ -72,7 +64,6 @@
72
  }
73
  }
74
  </script>
75
- <script type="module" crossorigin src="/assets/index-Ca5kstnp.js"></script>
76
  </head>
77
  <body>
78
  <div id="root"></div>
 
14
  background-color: #050505;
15
  color: #ffffff;
16
  }
17
+
18
  /* Custom Scrollbar */
 
19
  ::-webkit-scrollbar {
20
  width: 8px;
21
  }
 
22
  ::-webkit-scrollbar-track {
23
  background: #050505;
24
  }
 
25
  ::-webkit-scrollbar-thumb {
26
  background: #333;
27
  border-radius: 4px;
28
  }
 
29
  ::-webkit-scrollbar-thumb:hover {
30
  background: #555;
31
  }
32
 
33
  /* Utilities for gradients */
 
34
  .bg-glass {
35
  background: rgba(255, 255, 255, 0.05);
36
  backdrop-filter: blur(10px);
 
44
  }
45
 
46
  /* Blink animation for cursor */
 
47
  @keyframes blink {
48
  0%, 100% { opacity: 1; }
49
  50% { opacity: 0; }
50
  }
 
51
  .animate-blink {
52
  animation: blink 1s step-end infinite;
53
  }
54
  </style>
 
55
  <script type="importmap">
56
  {
57
  "imports": {
 
64
  }
65
  }
66
  </script>
 
67
  </head>
68
  <body>
69
  <div id="root"></div>
index.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+
5
+ const rootElement = document.getElementById('root');
6
+ if (!rootElement) {
7
+ throw new Error("Could not find root element to mount to");
8
+ }
9
+
10
+ const root = ReactDOM.createRoot(rootElement);
11
+ root.render(
12
+ <React.StrictMode>
13
+ <App />
14
+ </React.StrictMode>
15
+ );
metadata.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "name": "SyncUsers Testing MVP",
3
+ "description": "A high-fidelity clone of the SyncUsers marketing frontend, featuring interactive simulations and data visualizations.",
4
+ "requestFramePermissions": []
5
+ }
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "syncusers-testing-mvp",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@gradio/client": "^2.0.4",
13
+ "express": "^5.2.1",
14
+ "lucide-react": "^0.555.0",
15
+ "plotly.js": "2.30.0",
16
+ "react": "^19.2.0",
17
+ "react-dom": "^19.2.0",
18
+ "react-is": "^19.2.4",
19
+ "recharts": "^3.5.0",
20
+ "vite-plugin-node-polyfills": "^0.25.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.14.0",
24
+ "@vitejs/plugin-react": "^5.0.0",
25
+ "typescript": "~5.8.2",
26
+ "vite": "^6.2.0"
27
+ }
28
+ }
server.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const path = require('path');
3
+ const app = express();
4
+ const port = 7860;
5
+
6
+ app.use(express.static(path.join(__dirname, 'dist')));
7
+
8
+ app.get('/health', (req, res) => {
9
+ res.status(200).send('OK');
10
+ });
11
+
12
+ app.get('*', (req, res) => {
13
+ res.sendFile(path.join(__dirname, 'dist', 'index.html'));
14
+ });
15
+
16
+ app.listen(port, () => {
17
+ console.log(`Server running on port ${port}`);
18
+ });
services/gradioService.ts ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Client } from "@gradio/client";
2
+
3
+ const HF_SPACE = "AUXteam/tiny_factory";
4
+
5
+ export class GradioService {
6
+ private static client: any = null;
7
+
8
+ static async getClient() {
9
+ if (!this.client) {
10
+ this.client = await Client.connect(HF_SPACE);
11
+ }
12
+ return this.client;
13
+ }
14
+
15
+ static async identifyPersonas(context: string) {
16
+ try {
17
+ const client = await this.getClient();
18
+ const result = await client.predict("/identify_personas", [context]);
19
+ return result.data;
20
+ } catch (error) {
21
+ console.error("Error identifying personas:", error);
22
+ throw error;
23
+ }
24
+ }
25
+
26
+ static async simulate(persona: string, message: string) {
27
+ try {
28
+ const client = await this.getClient();
29
+ const result = await client.predict("/simulate", [persona, message]);
30
+ return result.data;
31
+ } catch (error) {
32
+ console.error("Error in simulation:", error);
33
+ throw error;
34
+ }
35
+ }
36
+
37
+ static async helpMeCraft(content: string) {
38
+ try {
39
+ // Assuming an endpoint for help me craft or similar
40
+ const client = await this.getClient();
41
+ const result = await client.predict("/help_me_craft", [content]);
42
+ return result.data;
43
+ } catch (error) {
44
+ console.error("Error helping to craft content:", error);
45
+ // Fallback or generic error handling
46
+ return "Unable to craft content at this time.";
47
+ }
48
+ }
49
+ }
tsconfig.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "experimentalDecorators": true,
5
+ "useDefineForClassFields": false,
6
+ "module": "ESNext",
7
+ "lib": [
8
+ "ES2022",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "skipLibCheck": true,
13
+ "types": [
14
+ "node"
15
+ ],
16
+ "moduleResolution": "bundler",
17
+ "isolatedModules": true,
18
+ "moduleDetection": "force",
19
+ "allowJs": true,
20
+ "jsx": "react-jsx",
21
+ "paths": {
22
+ "@/*": [
23
+ "./*"
24
+ ]
25
+ },
26
+ "allowImportingTsExtensions": true,
27
+ "noEmit": true
28
+ }
29
+ }
types.ts ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export enum UseCaseCategory {
2
+ PR_COMMS = "PR & Comms",
3
+ PRODUCT = "Product",
4
+ BRANDING = "Branding",
5
+ MARKETING = "Marketing",
6
+ SOCIAL_MEDIA = "Social Media",
7
+ JOURNALISM = "Journalism"
8
+ }
9
+
10
+ export interface UseCase {
11
+ category: UseCaseCategory;
12
+ title: string;
13
+ description: string;
14
+ color: string;
15
+ }
16
+
17
+ export interface NavLink {
18
+ label: string;
19
+ href: string;
20
+ }
21
+
22
+ export interface Testimonial {
23
+ quote: string;
24
+ author: string;
25
+ role: string;
26
+ company: string;
27
+ }
28
+
29
+ export interface FaqItem {
30
+ question: string;
31
+ answer: string;
32
+ }
vite.config.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import path from 'path';
2
+ import { defineConfig, loadEnv } from 'vite';
3
+ import react from '@vitejs/plugin-react';
4
+ import { nodePolyfills } from 'vite-plugin-node-polyfills';
5
+
6
+ export default defineConfig(({ mode }) => {
7
+ const env = loadEnv(mode, '.', '');
8
+ return {
9
+ server: {
10
+ port: 3000,
11
+ host: '0.0.0.0',
12
+ },
13
+ plugins: [
14
+ react(),
15
+ nodePolyfills({
16
+ include: ['buffer', 'process', 'util', 'stream'],
17
+ globals: {
18
+ Buffer: true,
19
+ process: true,
20
+ },
21
+ }),
22
+ ],
23
+ define: {
24
+ 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
25
+ 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
26
+ },
27
+ resolve: {
28
+ alias: {
29
+ '@': path.resolve(__dirname, '.'),
30
+ }
31
+ }
32
+ };
33
+ });