Pulastya B commited on
Commit
259c45d
Β·
1 Parent(s): c0e18bf

Added Sign in and Sign Up page

Browse files
FRRONTEEEND/App.tsx CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import React, { useState, useEffect } from 'react';
3
  import { HeroGeometric } from './components/HeroGeometric';
4
  import ProblemSolution from './components/ProblemSolution';
5
  import KeyCapabilities from './components/KeyCapabilities';
@@ -11,12 +11,12 @@ import { Logo } from './components/Logo';
11
  import { ChatInterface } from './components/ChatInterface';
12
  import { AuthPage } from './components/AuthPage';
13
  import { AuthProvider, useAuth } from './lib/AuthContext';
14
- import { User, LogOut } from 'lucide-react';
15
 
16
  // Inner app component that uses auth context
17
  const AppContent: React.FC = () => {
18
  const [view, setView] = useState<'landing' | 'chat' | 'auth'>('landing');
19
- const { user, isAuthenticated, loading, signOut } = useAuth();
20
  const [showUserMenu, setShowUserMenu] = useState(false);
21
 
22
  // Handle launch console - redirect to auth if not logged in
@@ -25,6 +25,21 @@ const AppContent: React.FC = () => {
25
  setView('chat');
26
  };
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  // Show auth page
29
  if (view === 'auth') {
30
  return (
@@ -51,7 +66,7 @@ const AppContent: React.FC = () => {
51
  </div>
52
 
53
  <div className="flex items-center gap-3">
54
- {/* User menu */}
55
  {isAuthenticated ? (
56
  <div className="relative">
57
  <button
@@ -130,4 +145,4 @@ const App: React.FC = () => {
130
  );
131
  };
132
 
133
- export default App;
 
1
 
2
+ import React, { useState } from 'react';
3
  import { HeroGeometric } from './components/HeroGeometric';
4
  import ProblemSolution from './components/ProblemSolution';
5
  import KeyCapabilities from './components/KeyCapabilities';
 
11
  import { ChatInterface } from './components/ChatInterface';
12
  import { AuthPage } from './components/AuthPage';
13
  import { AuthProvider, useAuth } from './lib/AuthContext';
14
+ import { User, LogOut, Loader2 } from 'lucide-react';
15
 
16
  // Inner app component that uses auth context
17
  const AppContent: React.FC = () => {
18
  const [view, setView] = useState<'landing' | 'chat' | 'auth'>('landing');
19
+ const { user, isAuthenticated, loading, signOut, isConfigured } = useAuth();
20
  const [showUserMenu, setShowUserMenu] = useState(false);
21
 
22
  // Handle launch console - redirect to auth if not logged in
 
25
  setView('chat');
26
  };
27
 
28
+ // Show loading state only briefly
29
+ if (loading) {
30
+ return (
31
+ <div className="min-h-screen bg-[#030303] flex items-center justify-center">
32
+ <div className="flex flex-col items-center gap-4">
33
+ <Logo className="w-16 h-16 animate-pulse" />
34
+ <div className="flex items-center gap-2 text-white/50">
35
+ <Loader2 className="w-4 h-4 animate-spin" />
36
+ <span>Loading...</span>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ );
41
+ }
42
+
43
  // Show auth page
44
  if (view === 'auth') {
45
  return (
 
66
  </div>
67
 
68
  <div className="flex items-center gap-3">
69
+ {/* User menu - only show if Supabase is configured */}
70
  {isAuthenticated ? (
71
  <div className="relative">
72
  <button
 
145
  );
146
  };
147
 
148
+ export default App;
FRRONTEEEND/components/AuthPage.tsx CHANGED
@@ -1,8 +1,61 @@
1
- import React, { useState } from 'react';
2
- import { motion, AnimatePresence } from 'framer-motion';
3
- import { Mail, Lock, User, Github, ArrowLeft, Loader2, AlertCircle, CheckCircle } from 'lucide-react';
4
- import { useAuth } from '../lib/AuthContext';
5
- import { Logo } from './Logo';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  interface AuthPageProps {
8
  onSuccess?: () => void;
@@ -12,43 +65,90 @@ interface AuthPageProps {
12
  export const AuthPage: React.FC<AuthPageProps> = ({ onSuccess, onSkip }) => {
13
  const { signIn, signUp, signInWithGoogle, signInWithGithub } = useAuth();
14
  const [mode, setMode] = useState<'signin' | 'signup'>('signin');
15
- const [email, setEmail] = useState('');
16
- const [password, setPassword] = useState('');
17
- const [loading, setLoading] = useState(false);
18
  const [error, setError] = useState<string | null>(null);
19
  const [success, setSuccess] = useState<string | null>(null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- const handleSubmit = async (e: React.FormEvent) => {
 
 
 
 
 
 
 
 
 
 
 
 
22
  e.preventDefault();
23
- setLoading(true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  setError(null);
25
- setSuccess(null);
 
 
 
 
 
26
 
27
  try {
28
- if (mode === 'signup') {
29
- const { error } = await signUp(email, password);
30
- if (error) {
31
- setError(error.message);
32
- } else {
33
- setSuccess('Check your email to confirm your account!');
34
- }
35
  } else {
36
- const { error } = await signIn(email, password);
37
- if (error) {
38
- setError(error.message);
39
- } else {
40
  onSuccess?.();
41
- }
42
  }
43
  } catch (err: any) {
44
  setError(err.message || 'An error occurred');
45
  } finally {
46
- setLoading(false);
47
  }
48
  };
49
 
50
  const handleOAuthSignIn = async (provider: 'google' | 'github') => {
51
- setLoading(true);
52
  setError(null);
53
 
54
  try {
@@ -59,204 +159,652 @@ export const AuthPage: React.FC<AuthPageProps> = ({ onSuccess, onSkip }) => {
59
  } catch (err: any) {
60
  setError(err.message || 'An error occurred');
61
  } finally {
62
- setLoading(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
  };
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  return (
67
- <div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center p-4">
68
- {/* Background gradient */}
69
  <div className="absolute inset-0 overflow-hidden">
70
  <div className="absolute -top-40 -right-40 w-80 h-80 bg-indigo-500/10 rounded-full blur-3xl" />
71
  <div className="absolute -bottom-40 -left-40 w-80 h-80 bg-purple-500/10 rounded-full blur-3xl" />
72
  </div>
73
 
74
- <motion.div
75
- initial={{ opacity: 0, y: 20 }}
76
- animate={{ opacity: 1, y: 0 }}
77
- className="relative w-full max-w-md"
78
- >
79
- {/* Card */}
80
- <div className="bg-white/[0.03] border border-white/10 rounded-2xl p-8 backdrop-blur-xl shadow-2xl">
81
- {/* Logo */}
82
- <div className="flex justify-center mb-6">
83
- <Logo />
84
- </div>
85
-
86
- {/* Title */}
87
- <div className="text-center mb-8">
88
- <h1 className="text-2xl font-bold text-white mb-2">
89
- {mode === 'signin' ? 'Welcome Back' : 'Create Account'}
90
- </h1>
91
- <p className="text-white/50 text-sm">
92
- {mode === 'signin'
93
- ? 'Sign in to access your data science workflows'
94
- : 'Join us to start analyzing your data'}
95
- </p>
96
- </div>
97
-
98
- {/* OAuth Buttons */}
99
- <div className="space-y-3 mb-6">
100
- <button
101
- onClick={() => handleOAuthSignIn('google')}
102
- disabled={loading}
103
- className="w-full flex items-center justify-center gap-3 px-4 py-3 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl transition-all text-white font-medium disabled:opacity-50"
104
- >
105
- <svg className="w-5 h-5" viewBox="0 0 24 24">
106
- <path
107
- fill="currentColor"
108
- d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
109
- />
110
- <path
111
- fill="currentColor"
112
- d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
113
- />
114
- <path
115
- fill="currentColor"
116
- d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
117
- />
118
- <path
119
- fill="currentColor"
120
- d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
121
  />
122
- </svg>
123
- Continue with Google
124
- </button>
125
-
126
- <button
127
- onClick={() => handleOAuthSignIn('github')}
128
- disabled={loading}
129
- className="w-full flex items-center justify-center gap-3 px-4 py-3 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl transition-all text-white font-medium disabled:opacity-50"
130
- >
131
- <Github className="w-5 h-5" />
132
- Continue with GitHub
133
- </button>
134
  </div>
135
-
136
- {/* Divider */}
137
- <div className="relative mb-6">
138
- <div className="absolute inset-0 flex items-center">
139
- <div className="w-full border-t border-white/10"></div>
140
- </div>
141
- <div className="relative flex justify-center text-sm">
142
- <span className="px-4 bg-[#0a0a0a] text-white/40">or continue with email</span>
143
- </div>
144
  </div>
 
145
 
146
- {/* Form */}
147
- <form onSubmit={handleSubmit} className="space-y-4">
 
 
 
 
148
  <div>
149
- <label className="block text-sm font-medium text-white/70 mb-2">Email</label>
150
- <div className="relative">
151
- <Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
152
- <input
153
- type="email"
154
- value={email}
155
- onChange={(e) => setEmail(e.target.value)}
156
- placeholder="you@example.com"
157
- required
158
- className="w-full pl-11 pr-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/50 transition-all"
159
- />
160
- </div>
161
- </div>
162
-
163
- <div>
164
- <label className="block text-sm font-medium text-white/70 mb-2">Password</label>
165
- <div className="relative">
166
- <Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-white/30" />
167
- <input
168
- type="password"
169
- value={password}
170
- onChange={(e) => setPassword(e.target.value)}
171
- placeholder="β€’β€’β€’β€’β€’β€’β€’β€’"
172
- required
173
- minLength={6}
174
- className="w-full pl-11 pr-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder-white/30 focus:outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/50 transition-all"
175
- />
176
- </div>
177
- </div>
178
-
179
- {/* Error Message */}
180
- <AnimatePresence>
181
- {error && (
182
  <motion.div
183
- initial={{ opacity: 0, y: -10 }}
184
- animate={{ opacity: 1, y: 0 }}
185
- exit={{ opacity: 0 }}
186
- className="flex items-center gap-2 p-3 bg-red-500/10 border border-red-500/20 rounded-xl text-red-400 text-sm"
 
187
  >
188
- <AlertCircle className="w-4 h-4 flex-shrink-0" />
189
- {error}
190
- </motion.div>
191
- )}
192
- </AnimatePresence>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
- {/* Success Message */}
195
- <AnimatePresence>
196
- {success && (
197
- <motion.div
198
- initial={{ opacity: 0, y: -10 }}
199
- animate={{ opacity: 1, y: 0 }}
200
- exit={{ opacity: 0 }}
201
- className="flex items-center gap-2 p-3 bg-green-500/10 border border-green-500/20 rounded-xl text-green-400 text-sm"
202
- >
203
- <CheckCircle className="w-4 h-4 flex-shrink-0" />
204
- {success}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  </motion.div>
206
- )}
207
- </AnimatePresence>
208
-
209
- {/* Submit Button */}
210
- <button
211
- type="submit"
212
- disabled={loading}
213
- className="w-full py-3 bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-500 hover:to-purple-500 text-white font-medium rounded-xl transition-all disabled:opacity-50 flex items-center justify-center gap-2"
214
- >
215
- {loading ? (
216
- <Loader2 className="w-5 h-5 animate-spin" />
217
- ) : mode === 'signin' ? (
218
- 'Sign In'
219
- ) : (
220
- 'Create Account'
221
- )}
222
- </button>
223
- </form>
224
-
225
- {/* Toggle Mode */}
226
- <div className="mt-6 text-center">
227
- <p className="text-white/50 text-sm">
228
- {mode === 'signin' ? "Don't have an account? " : 'Already have an account? '}
229
- <button
230
- onClick={() => {
231
- setMode(mode === 'signin' ? 'signup' : 'signin');
232
- setError(null);
233
- setSuccess(null);
234
- }}
235
- className="text-indigo-400 hover:text-indigo-300 font-medium"
236
- >
237
- {mode === 'signin' ? 'Sign Up' : 'Sign In'}
238
- </button>
239
- </p>
240
- </div>
241
 
242
- {/* Skip Button */}
243
- {onSkip && (
244
- <div className="mt-4 text-center">
245
- <button
246
- onClick={onSkip}
247
- className="text-white/30 hover:text-white/50 text-sm transition-colors"
248
- >
249
- Continue without signing in β†’
250
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  </div>
252
- )}
253
- </div>
254
 
255
- {/* Footer */}
256
- <p className="text-center text-white/30 text-xs mt-6">
257
- By signing in, you agree to our Terms of Service and Privacy Policy
258
- </p>
259
- </motion.div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  </div>
261
  );
262
  };
 
 
 
1
+ "use client";
2
+
3
+ import React, { useState } from "react";
4
+ import { motion, AnimatePresence } from "framer-motion";
5
+ import { ChevronLeft, ChevronRight, Check, Loader2, Eye, EyeOff, Github, Mail, Lock, User, Target, Briefcase } from "lucide-react";
6
+ import { Button } from "./ui/button";
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardFooter,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "./ui/card";
15
+ import { Input } from "./ui/input";
16
+ import { Label } from "./ui/label";
17
+ import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
18
+ import { Textarea } from "./ui/textarea";
19
+ import {
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue,
25
+ } from "./ui/select";
26
+ import { cn } from "../lib/utils";
27
+ import { useAuth } from "../lib/AuthContext";
28
+ import { Logo } from "./Logo";
29
+
30
+ const steps = [
31
+ { id: "personal", title: "Personal Info" },
32
+ { id: "goals", title: "Data Science Goals" },
33
+ { id: "professional", title: "Professional" },
34
+ ];
35
+
36
+ interface FormData {
37
+ name: string;
38
+ email: string;
39
+ password: string;
40
+ confirmPassword: string;
41
+ primaryGoal: string;
42
+ targetOutcome: string;
43
+ dataTypes: string[];
44
+ profession: string;
45
+ experience: string;
46
+ industry: string;
47
+ }
48
+
49
+ const fadeInUp = {
50
+ hidden: { opacity: 0, y: 20 },
51
+ visible: { opacity: 1, y: 0, transition: { duration: 0.3 } },
52
+ };
53
+
54
+ const contentVariants = {
55
+ hidden: { opacity: 0, x: 50 },
56
+ visible: { opacity: 1, x: 0, transition: { duration: 0.3 } },
57
+ exit: { opacity: 0, x: -50, transition: { duration: 0.2 } },
58
+ };
59
 
60
  interface AuthPageProps {
61
  onSuccess?: () => void;
 
65
  export const AuthPage: React.FC<AuthPageProps> = ({ onSuccess, onSkip }) => {
66
  const { signIn, signUp, signInWithGoogle, signInWithGithub } = useAuth();
67
  const [mode, setMode] = useState<'signin' | 'signup'>('signin');
68
+ const [currentStep, setCurrentStep] = useState(0);
69
+ const [isSubmitting, setIsSubmitting] = useState(false);
70
+ const [showPassword, setShowPassword] = useState(false);
71
  const [error, setError] = useState<string | null>(null);
72
  const [success, setSuccess] = useState<string | null>(null);
73
+
74
+ const [formData, setFormData] = useState<FormData>({
75
+ name: "",
76
+ email: "",
77
+ password: "",
78
+ confirmPassword: "",
79
+ primaryGoal: "",
80
+ targetOutcome: "",
81
+ dataTypes: [],
82
+ profession: "",
83
+ experience: "",
84
+ industry: "",
85
+ });
86
+
87
+ const updateFormData = (field: keyof FormData, value: string) => {
88
+ setFormData((prev) => ({ ...prev, [field]: value }));
89
+ setError(null);
90
+ };
91
 
92
+ const nextStep = () => {
93
+ if (currentStep < steps.length - 1) {
94
+ setCurrentStep((prev) => prev + 1);
95
+ }
96
+ };
97
+
98
+ const prevStep = () => {
99
+ if (currentStep > 0) {
100
+ setCurrentStep((prev) => prev - 1);
101
+ }
102
+ };
103
+
104
+ const handleSignIn = async (e: React.FormEvent) => {
105
  e.preventDefault();
106
+ setIsSubmitting(true);
107
+ setError(null);
108
+
109
+ try {
110
+ const { error } = await signIn(formData.email, formData.password);
111
+ if (error) {
112
+ setError(error.message);
113
+ } else {
114
+ onSuccess?.();
115
+ }
116
+ } catch (err: any) {
117
+ setError(err.message || 'An error occurred');
118
+ } finally {
119
+ setIsSubmitting(false);
120
+ }
121
+ };
122
+
123
+ const handleSignUp = async () => {
124
+ setIsSubmitting(true);
125
  setError(null);
126
+
127
+ if (formData.password !== formData.confirmPassword) {
128
+ setError("Passwords don't match");
129
+ setIsSubmitting(false);
130
+ return;
131
+ }
132
 
133
  try {
134
+ const { error } = await signUp(formData.email, formData.password);
135
+ if (error) {
136
+ setError(error.message);
 
 
 
 
137
  } else {
138
+ setSuccess('Account created! Check your email to confirm your account.');
139
+ setTimeout(() => {
 
 
140
  onSuccess?.();
141
+ }, 2000);
142
  }
143
  } catch (err: any) {
144
  setError(err.message || 'An error occurred');
145
  } finally {
146
+ setIsSubmitting(false);
147
  }
148
  };
149
 
150
  const handleOAuthSignIn = async (provider: 'google' | 'github') => {
151
+ setIsSubmitting(true);
152
  setError(null);
153
 
154
  try {
 
159
  } catch (err: any) {
160
  setError(err.message || 'An error occurred');
161
  } finally {
162
+ setIsSubmitting(false);
163
+ }
164
+ };
165
+
166
+ const isStepValid = () => {
167
+ switch (currentStep) {
168
+ case 0:
169
+ return formData.name.trim() !== "" && formData.email.trim() !== "" &&
170
+ formData.password.length >= 6 && formData.password === formData.confirmPassword;
171
+ case 1:
172
+ return formData.primaryGoal !== "";
173
+ case 2:
174
+ return formData.profession.trim() !== "" && formData.industry !== "";
175
+ default:
176
+ return true;
177
  }
178
  };
179
 
180
+ // Sign In Form
181
+ if (mode === 'signin') {
182
+ return (
183
+ <div className="min-h-screen bg-[#030303] flex items-center justify-center p-4">
184
+ <div className="absolute inset-0 overflow-hidden">
185
+ <div className="absolute -top-40 -right-40 w-80 h-80 bg-indigo-500/10 rounded-full blur-3xl" />
186
+ <div className="absolute -bottom-40 -left-40 w-80 h-80 bg-purple-500/10 rounded-full blur-3xl" />
187
+ </div>
188
+
189
+ <motion.div
190
+ initial={{ opacity: 0, y: 20 }}
191
+ animate={{ opacity: 1, y: 0 }}
192
+ className="relative w-full max-w-md"
193
+ >
194
+ <Card className="border-white/10 bg-white/[0.03] backdrop-blur-xl shadow-2xl pb-0">
195
+ <CardHeader className="space-y-1 text-center mb-2 mt-4">
196
+ <div className="flex justify-center mb-2">
197
+ <Logo className="w-12 h-12" />
198
+ </div>
199
+ <div>
200
+ <h2 className="text-2xl font-semibold text-white">Sign in to Data Science Agent</h2>
201
+ <p className="text-white/50 text-sm mt-1">
202
+ Welcome back! Please enter your details.
203
+ </p>
204
+ </div>
205
+ </CardHeader>
206
+ <CardContent className="space-y-4 px-8">
207
+ <div className="space-y-3">
208
+ <Button
209
+ type="button"
210
+ variant="outline"
211
+ onClick={() => handleOAuthSignIn('google')}
212
+ disabled={isSubmitting}
213
+ className="w-full bg-white/5 hover:bg-white/10 border-white/10 text-white h-11"
214
+ >
215
+ <svg className="w-5 h-5 mr-2" viewBox="0 0 24 24">
216
+ <path fill="currentColor" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
217
+ <path fill="currentColor" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
218
+ <path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
219
+ <path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
220
+ </svg>
221
+ Continue with Google
222
+ </Button>
223
+
224
+ <Button
225
+ type="button"
226
+ variant="outline"
227
+ onClick={() => handleOAuthSignIn('github')}
228
+ disabled={isSubmitting}
229
+ className="w-full bg-white/5 hover:bg-white/10 border-white/10 text-white h-11"
230
+ >
231
+ <Github className="w-5 h-5 mr-2" />
232
+ Continue with GitHub
233
+ </Button>
234
+ </div>
235
+
236
+ <div className="relative my-6">
237
+ <div className="absolute inset-0 flex items-center">
238
+ <div className="w-full border-t border-white/10"></div>
239
+ </div>
240
+ <div className="relative flex justify-center text-sm">
241
+ <span className="px-4 bg-[#030303] text-white/40">or continue with email</span>
242
+ </div>
243
+ </div>
244
+
245
+ <form onSubmit={handleSignIn} className="space-y-4">
246
+ <div className="space-y-2">
247
+ <Label htmlFor="email" className="text-white/70">Email address</Label>
248
+ <div className="relative">
249
+ <Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
250
+ <Input
251
+ id="email"
252
+ type="email"
253
+ placeholder="you@example.com"
254
+ value={formData.email}
255
+ onChange={(e) => updateFormData("email", e.target.value)}
256
+ className="pl-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
257
+ />
258
+ </div>
259
+ </div>
260
+
261
+ <div className="space-y-2">
262
+ <Label htmlFor="password" className="text-white/70">Password</Label>
263
+ <div className="relative">
264
+ <Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
265
+ <Input
266
+ id="password"
267
+ type={showPassword ? "text" : "password"}
268
+ placeholder="β€’β€’β€’β€’β€’β€’β€’β€’"
269
+ value={formData.password}
270
+ onChange={(e) => updateFormData("password", e.target.value)}
271
+ className="pl-10 pr-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
272
+ />
273
+ <button
274
+ type="button"
275
+ onClick={() => setShowPassword(!showPassword)}
276
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-white/30 hover:text-white/50"
277
+ >
278
+ {showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
279
+ </button>
280
+ </div>
281
+ </div>
282
+
283
+ <AnimatePresence>
284
+ {error && (
285
+ <motion.div
286
+ initial={{ opacity: 0, y: -10 }}
287
+ animate={{ opacity: 1, y: 0 }}
288
+ exit={{ opacity: 0 }}
289
+ className="p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm"
290
+ >
291
+ {error}
292
+ </motion.div>
293
+ )}
294
+ </AnimatePresence>
295
+
296
+ <Button
297
+ type="submit"
298
+ disabled={isSubmitting || !formData.email || !formData.password}
299
+ className="w-full bg-indigo-600 hover:bg-indigo-700 text-white h-11"
300
+ >
301
+ {isSubmitting ? (
302
+ <>
303
+ <Loader2 className="w-4 h-4 mr-2 animate-spin" /> Signing in...
304
+ </>
305
+ ) : (
306
+ "Sign In"
307
+ )}
308
+ </Button>
309
+ </form>
310
+
311
+ <Button
312
+ type="button"
313
+ variant="ghost"
314
+ onClick={onSkip}
315
+ className="w-full text-white/50 hover:text-white hover:bg-white/5"
316
+ >
317
+ Continue as Guest
318
+ </Button>
319
+ </CardContent>
320
+ <CardFooter className="flex justify-center border-t border-white/10 py-4">
321
+ <p className="text-center text-sm text-white/50">
322
+ New to Data Science Agent?{" "}
323
+ <button
324
+ onClick={() => {
325
+ setMode('signup');
326
+ setError(null);
327
+ setCurrentStep(0);
328
+ }}
329
+ className="text-indigo-400 hover:text-indigo-300 hover:underline"
330
+ >
331
+ Sign up
332
+ </button>
333
+ </p>
334
+ </CardFooter>
335
+ </Card>
336
+ </motion.div>
337
+ </div>
338
+ );
339
+ }
340
+
341
+ // Sign Up Multi-Step Form
342
  return (
343
+ <div className="min-h-screen bg-[#030303] flex items-center justify-center p-4">
 
344
  <div className="absolute inset-0 overflow-hidden">
345
  <div className="absolute -top-40 -right-40 w-80 h-80 bg-indigo-500/10 rounded-full blur-3xl" />
346
  <div className="absolute -bottom-40 -left-40 w-80 h-80 bg-purple-500/10 rounded-full blur-3xl" />
347
  </div>
348
 
349
+ <div className="relative w-full max-w-lg py-8">
350
+ <motion.div
351
+ className="mb-8"
352
+ initial={{ opacity: 0, y: -20 }}
353
+ animate={{ opacity: 1, y: 0 }}
354
+ transition={{ duration: 0.5 }}
355
+ >
356
+ <div className="flex justify-between mb-2">
357
+ {steps.map((step, index) => (
358
+ <motion.div
359
+ key={index}
360
+ className="flex flex-col items-center"
361
+ whileHover={{ scale: 1.1 }}
362
+ >
363
+ <motion.div
364
+ className={cn(
365
+ "w-4 h-4 rounded-full cursor-pointer transition-colors duration-300",
366
+ index < currentStep
367
+ ? "bg-indigo-500"
368
+ : index === currentStep
369
+ ? "bg-indigo-500 ring-4 ring-indigo-500/20"
370
+ : "bg-white/20",
371
+ )}
372
+ onClick={() => {
373
+ if (index <= currentStep) {
374
+ setCurrentStep(index);
375
+ }
376
+ }}
377
+ whileTap={{ scale: 0.95 }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  />
379
+ <motion.span
380
+ className={cn(
381
+ "text-xs mt-1.5 hidden sm:block",
382
+ index === currentStep
383
+ ? "text-indigo-400 font-medium"
384
+ : "text-white/40",
385
+ )}
386
+ >
387
+ {step.title}
388
+ </motion.span>
389
+ </motion.div>
390
+ ))}
391
  </div>
392
+ <div className="w-full bg-white/10 h-1.5 rounded-full overflow-hidden mt-2">
393
+ <motion.div
394
+ className="h-full bg-indigo-500"
395
+ initial={{ width: 0 }}
396
+ animate={{ width: `${(currentStep / (steps.length - 1)) * 100}%` }}
397
+ transition={{ duration: 0.3 }}
398
+ />
 
 
399
  </div>
400
+ </motion.div>
401
 
402
+ <motion.div
403
+ initial={{ opacity: 0, y: 20 }}
404
+ animate={{ opacity: 1, y: 0 }}
405
+ transition={{ duration: 0.5, delay: 0.2 }}
406
+ >
407
+ <Card className="border-white/10 bg-white/[0.03] backdrop-blur-xl shadow-2xl overflow-hidden">
408
  <div>
409
+ <AnimatePresence mode="wait">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410
  <motion.div
411
+ key={currentStep}
412
+ initial="hidden"
413
+ animate="visible"
414
+ exit="exit"
415
+ variants={contentVariants}
416
  >
417
+ {currentStep === 0 && (
418
+ <>
419
+ <CardHeader>
420
+ <div className="flex justify-center mb-2">
421
+ <Logo className="w-10 h-10" />
422
+ </div>
423
+ <CardTitle className="text-white text-center">Create your account</CardTitle>
424
+ <CardDescription className="text-white/50 text-center">
425
+ Let's start with some basic information
426
+ </CardDescription>
427
+ </CardHeader>
428
+ <CardContent className="space-y-4">
429
+ <motion.div variants={fadeInUp} className="space-y-2">
430
+ <Label htmlFor="name" className="text-white/70">Full Name</Label>
431
+ <div className="relative">
432
+ <User className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
433
+ <Input
434
+ id="name"
435
+ placeholder="John Doe"
436
+ value={formData.name}
437
+ onChange={(e) => updateFormData("name", e.target.value)}
438
+ className="pl-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
439
+ />
440
+ </div>
441
+ </motion.div>
442
+ <motion.div variants={fadeInUp} className="space-y-2">
443
+ <Label htmlFor="signup-email" className="text-white/70">Email Address</Label>
444
+ <div className="relative">
445
+ <Mail className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
446
+ <Input
447
+ id="signup-email"
448
+ type="email"
449
+ placeholder="john@example.com"
450
+ value={formData.email}
451
+ onChange={(e) => updateFormData("email", e.target.value)}
452
+ className="pl-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
453
+ />
454
+ </div>
455
+ </motion.div>
456
+ <motion.div variants={fadeInUp} className="space-y-2">
457
+ <Label htmlFor="signup-password" className="text-white/70">Password</Label>
458
+ <div className="relative">
459
+ <Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
460
+ <Input
461
+ id="signup-password"
462
+ type={showPassword ? "text" : "password"}
463
+ placeholder="β€’β€’β€’β€’β€’β€’β€’β€’"
464
+ value={formData.password}
465
+ onChange={(e) => updateFormData("password", e.target.value)}
466
+ className="pl-10 pr-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
467
+ />
468
+ <button
469
+ type="button"
470
+ onClick={() => setShowPassword(!showPassword)}
471
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-white/30 hover:text-white/50"
472
+ >
473
+ {showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
474
+ </button>
475
+ </div>
476
+ <p className="text-xs text-white/40">Minimum 6 characters</p>
477
+ </motion.div>
478
+ <motion.div variants={fadeInUp} className="space-y-2">
479
+ <Label htmlFor="confirm-password" className="text-white/70">Confirm Password</Label>
480
+ <div className="relative">
481
+ <Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30" />
482
+ <Input
483
+ id="confirm-password"
484
+ type={showPassword ? "text" : "password"}
485
+ placeholder="β€’β€’β€’β€’β€’β€’β€’β€’"
486
+ value={formData.confirmPassword}
487
+ onChange={(e) => updateFormData("confirmPassword", e.target.value)}
488
+ className={cn(
489
+ "pl-10 bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50",
490
+ formData.confirmPassword && formData.password !== formData.confirmPassword && "border-red-500/50"
491
+ )}
492
+ />
493
+ </div>
494
+ {formData.confirmPassword && formData.password !== formData.confirmPassword && (
495
+ <p className="text-xs text-red-400">Passwords don't match</p>
496
+ )}
497
+ </motion.div>
498
 
499
+ <div className="relative my-4">
500
+ <div className="absolute inset-0 flex items-center">
501
+ <div className="w-full border-t border-white/10"></div>
502
+ </div>
503
+ <div className="relative flex justify-center text-sm">
504
+ <span className="px-4 bg-[#0a0a0a] text-white/40">or sign up with</span>
505
+ </div>
506
+ </div>
507
+
508
+ <div className="flex gap-3">
509
+ <Button
510
+ type="button"
511
+ variant="outline"
512
+ onClick={() => handleOAuthSignIn('google')}
513
+ disabled={isSubmitting}
514
+ className="flex-1 bg-white/5 hover:bg-white/10 border-white/10 text-white"
515
+ >
516
+ <svg className="w-4 h-4 mr-2" viewBox="0 0 24 24">
517
+ <path fill="currentColor" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
518
+ <path fill="currentColor" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
519
+ <path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
520
+ <path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
521
+ </svg>
522
+ Google
523
+ </Button>
524
+ <Button
525
+ type="button"
526
+ variant="outline"
527
+ onClick={() => handleOAuthSignIn('github')}
528
+ disabled={isSubmitting}
529
+ className="flex-1 bg-white/5 hover:bg-white/10 border-white/10 text-white"
530
+ >
531
+ <Github className="w-4 h-4 mr-2" />
532
+ GitHub
533
+ </Button>
534
+ </div>
535
+ </CardContent>
536
+ </>
537
+ )}
538
+
539
+ {currentStep === 1 && (
540
+ <>
541
+ <CardHeader>
542
+ <div className="flex justify-center mb-2">
543
+ <div className="w-12 h-12 bg-indigo-500/10 rounded-full flex items-center justify-center">
544
+ <Target className="w-6 h-6 text-indigo-400" />
545
+ </div>
546
+ </div>
547
+ <CardTitle className="text-white text-center">Data Science Goals</CardTitle>
548
+ <CardDescription className="text-white/50 text-center">
549
+ What are you trying to achieve with data science?
550
+ </CardDescription>
551
+ </CardHeader>
552
+ <CardContent className="space-y-4">
553
+ <motion.div variants={fadeInUp} className="space-y-2">
554
+ <Label className="text-white/70">What's your primary goal?</Label>
555
+ <RadioGroup
556
+ value={formData.primaryGoal}
557
+ onValueChange={(value) => updateFormData("primaryGoal", value)}
558
+ className="space-y-2"
559
+ >
560
+ {[
561
+ { value: "explore-data", label: "Explore and understand my data", icon: "πŸ”" },
562
+ { value: "build-models", label: "Build predictive models", icon: "πŸ€–" },
563
+ { value: "automate-analysis", label: "Automate data analysis workflows", icon: "⚑" },
564
+ { value: "visualize", label: "Create data visualizations", icon: "πŸ“Š" },
565
+ { value: "learn", label: "Learn data science concepts", icon: "πŸ“š" },
566
+ ].map((goal, index) => (
567
+ <motion.div
568
+ key={goal.value}
569
+ className={cn(
570
+ "flex items-center space-x-3 rounded-lg border p-3 cursor-pointer transition-colors",
571
+ formData.primaryGoal === goal.value
572
+ ? "border-indigo-500/50 bg-indigo-500/10"
573
+ : "border-white/10 bg-white/5 hover:bg-white/10"
574
+ )}
575
+ whileHover={{ scale: 1.02 }}
576
+ whileTap={{ scale: 0.98 }}
577
+ onClick={() => updateFormData("primaryGoal", goal.value)}
578
+ initial={{ opacity: 0, x: -10 }}
579
+ animate={{
580
+ opacity: 1,
581
+ x: 0,
582
+ transition: { delay: 0.1 * index, duration: 0.3 },
583
+ }}
584
+ >
585
+ <RadioGroupItem value={goal.value} id={`goal-${index}`} className="border-white/30" />
586
+ <span className="text-lg">{goal.icon}</span>
587
+ <Label htmlFor={`goal-${index}`} className="cursor-pointer w-full text-white/80">
588
+ {goal.label}
589
+ </Label>
590
+ </motion.div>
591
+ ))}
592
+ </RadioGroup>
593
+ </motion.div>
594
+ <motion.div variants={fadeInUp} className="space-y-2">
595
+ <Label htmlFor="targetOutcome" className="text-white/70">
596
+ What outcome are you hoping to achieve?
597
+ </Label>
598
+ <Textarea
599
+ id="targetOutcome"
600
+ placeholder="E.g., Predict customer churn, automate reporting, understand trends..."
601
+ value={formData.targetOutcome}
602
+ onChange={(e) => updateFormData("targetOutcome", e.target.value)}
603
+ className="min-h-[80px] bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
604
+ />
605
+ </motion.div>
606
+ </CardContent>
607
+ </>
608
+ )}
609
+
610
+ {currentStep === 2 && (
611
+ <>
612
+ <CardHeader>
613
+ <div className="flex justify-center mb-2">
614
+ <div className="w-12 h-12 bg-indigo-500/10 rounded-full flex items-center justify-center">
615
+ <Briefcase className="w-6 h-6 text-indigo-400" />
616
+ </div>
617
+ </div>
618
+ <CardTitle className="text-white text-center">Professional Background</CardTitle>
619
+ <CardDescription className="text-white/50 text-center">
620
+ Tell us about your professional experience
621
+ </CardDescription>
622
+ </CardHeader>
623
+ <CardContent className="space-y-4">
624
+ <motion.div variants={fadeInUp} className="space-y-2">
625
+ <Label htmlFor="profession" className="text-white/70">What's your profession?</Label>
626
+ <Input
627
+ id="profession"
628
+ placeholder="e.g. Data Analyst, Business Manager, Researcher"
629
+ value={formData.profession}
630
+ onChange={(e) => updateFormData("profession", e.target.value)}
631
+ className="bg-white/5 border-white/10 text-white placeholder:text-white/30 focus:border-indigo-500/50"
632
+ />
633
+ </motion.div>
634
+
635
+ <motion.div variants={fadeInUp} className="space-y-2">
636
+ <Label htmlFor="industry" className="text-white/70">What industry do you work in?</Label>
637
+ <Select
638
+ value={formData.industry}
639
+ onValueChange={(value) => updateFormData("industry", value)}
640
+ >
641
+ <SelectTrigger
642
+ id="industry"
643
+ className="bg-white/5 border-white/10 text-white focus:border-indigo-500/50"
644
+ >
645
+ <SelectValue placeholder="Select an industry" />
646
+ </SelectTrigger>
647
+ <SelectContent className="bg-[#1a1a1a] border-white/10">
648
+ <SelectItem value="technology" className="text-white hover:bg-white/10">Technology</SelectItem>
649
+ <SelectItem value="finance" className="text-white hover:bg-white/10">Finance & Banking</SelectItem>
650
+ <SelectItem value="healthcare" className="text-white hover:bg-white/10">Healthcare</SelectItem>
651
+ <SelectItem value="education" className="text-white hover:bg-white/10">Education</SelectItem>
652
+ <SelectItem value="retail" className="text-white hover:bg-white/10">Retail & E-commerce</SelectItem>
653
+ <SelectItem value="manufacturing" className="text-white hover:bg-white/10">Manufacturing</SelectItem>
654
+ <SelectItem value="consulting" className="text-white hover:bg-white/10">Consulting</SelectItem>
655
+ <SelectItem value="research" className="text-white hover:bg-white/10">Research & Academia</SelectItem>
656
+ <SelectItem value="marketing" className="text-white hover:bg-white/10">Marketing & Advertising</SelectItem>
657
+ <SelectItem value="other" className="text-white hover:bg-white/10">Other</SelectItem>
658
+ </SelectContent>
659
+ </Select>
660
+ </motion.div>
661
+
662
+ <motion.div variants={fadeInUp} className="space-y-2">
663
+ <Label className="text-white/70">Experience with data science</Label>
664
+ <RadioGroup
665
+ value={formData.experience}
666
+ onValueChange={(value) => updateFormData("experience", value)}
667
+ className="space-y-2"
668
+ >
669
+ {[
670
+ { value: "beginner", label: "Beginner - Just getting started" },
671
+ { value: "intermediate", label: "Intermediate - Some experience" },
672
+ { value: "advanced", label: "Advanced - Experienced practitioner" },
673
+ { value: "expert", label: "Expert - Professional data scientist" },
674
+ ].map((level, index) => (
675
+ <motion.div
676
+ key={level.value}
677
+ className={cn(
678
+ "flex items-center space-x-3 rounded-lg border p-3 cursor-pointer transition-colors",
679
+ formData.experience === level.value
680
+ ? "border-indigo-500/50 bg-indigo-500/10"
681
+ : "border-white/10 bg-white/5 hover:bg-white/10"
682
+ )}
683
+ whileHover={{ scale: 1.02 }}
684
+ whileTap={{ scale: 0.98 }}
685
+ onClick={() => updateFormData("experience", level.value)}
686
+ initial={{ opacity: 0, y: 10 }}
687
+ animate={{
688
+ opacity: 1,
689
+ y: 0,
690
+ transition: { delay: 0.1 * index, duration: 0.3 },
691
+ }}
692
+ >
693
+ <RadioGroupItem value={level.value} id={`exp-${index}`} className="border-white/30" />
694
+ <Label htmlFor={`exp-${index}`} className="cursor-pointer w-full text-white/80">
695
+ {level.label}
696
+ </Label>
697
+ </motion.div>
698
+ ))}
699
+ </RadioGroup>
700
+ </motion.div>
701
+
702
+ <AnimatePresence>
703
+ {error && (
704
+ <motion.div
705
+ initial={{ opacity: 0, y: -10 }}
706
+ animate={{ opacity: 1, y: 0 }}
707
+ exit={{ opacity: 0 }}
708
+ className="p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm"
709
+ >
710
+ {error}
711
+ </motion.div>
712
+ )}
713
+ {success && (
714
+ <motion.div
715
+ initial={{ opacity: 0, y: -10 }}
716
+ animate={{ opacity: 1, y: 0 }}
717
+ exit={{ opacity: 0 }}
718
+ className="p-3 bg-green-500/10 border border-green-500/20 rounded-lg text-green-400 text-sm"
719
+ >
720
+ {success}
721
+ </motion.div>
722
+ )}
723
+ </AnimatePresence>
724
+ </CardContent>
725
+ </>
726
+ )}
727
  </motion.div>
728
+ </AnimatePresence>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
 
730
+ <CardFooter className="flex justify-between pt-6 pb-4">
731
+ <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
732
+ {currentStep === 0 ? (
733
+ <Button
734
+ type="button"
735
+ variant="outline"
736
+ onClick={() => {
737
+ setMode('signin');
738
+ setError(null);
739
+ }}
740
+ className="flex items-center gap-1 bg-white/5 hover:bg-white/10 border-white/10 text-white rounded-xl"
741
+ >
742
+ <ChevronLeft className="h-4 w-4" /> Sign In
743
+ </Button>
744
+ ) : (
745
+ <Button
746
+ type="button"
747
+ variant="outline"
748
+ onClick={prevStep}
749
+ className="flex items-center gap-1 bg-white/5 hover:bg-white/10 border-white/10 text-white rounded-xl"
750
+ >
751
+ <ChevronLeft className="h-4 w-4" /> Back
752
+ </Button>
753
+ )}
754
+ </motion.div>
755
+ <motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
756
+ <Button
757
+ type="button"
758
+ onClick={currentStep === steps.length - 1 ? handleSignUp : nextStep}
759
+ disabled={!isStepValid() || isSubmitting}
760
+ className="flex items-center gap-1 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl"
761
+ >
762
+ {isSubmitting ? (
763
+ <>
764
+ <Loader2 className="h-4 w-4 animate-spin" /> Creating...
765
+ </>
766
+ ) : (
767
+ <>
768
+ {currentStep === steps.length - 1 ? "Create Account" : "Next"}
769
+ {currentStep === steps.length - 1 ? (
770
+ <Check className="h-4 w-4" />
771
+ ) : (
772
+ <ChevronRight className="h-4 w-4" />
773
+ )}
774
+ </>
775
+ )}
776
+ </Button>
777
+ </motion.div>
778
+ </CardFooter>
779
  </div>
780
+ </Card>
781
+ </motion.div>
782
 
783
+ <motion.div
784
+ className="mt-4 text-center"
785
+ initial={{ opacity: 0 }}
786
+ animate={{ opacity: 1 }}
787
+ transition={{ duration: 0.5, delay: 0.4 }}
788
+ >
789
+ <button
790
+ onClick={onSkip}
791
+ className="text-sm text-white/40 hover:text-white/60 transition-colors"
792
+ >
793
+ Skip for now and continue as guest
794
+ </button>
795
+ </motion.div>
796
+
797
+ <motion.div
798
+ className="mt-4 text-center text-sm text-white/40"
799
+ initial={{ opacity: 0 }}
800
+ animate={{ opacity: 1 }}
801
+ transition={{ duration: 0.5, delay: 0.4 }}
802
+ >
803
+ Step {currentStep + 1} of {steps.length}: {steps[currentStep].title}
804
+ </motion.div>
805
+ </div>
806
  </div>
807
  );
808
  };
809
+
810
+ export default AuthPage;
FRRONTEEEND/components/ui/button.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { cn } from "../../lib/utils"
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 hover:underline",
20
+ },
21
+ size: {
22
+ default: "h-10 px-4 py-2",
23
+ sm: "h-9 rounded-md px-3",
24
+ lg: "h-11 rounded-md px-8",
25
+ icon: "h-10 w-10",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ size: "default",
31
+ },
32
+ },
33
+ )
34
+
35
+ export interface ButtonProps
36
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
37
+ VariantProps<typeof buttonVariants> {
38
+ asChild?: boolean
39
+ }
40
+
41
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
42
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
43
+ const Comp = asChild ? Slot : "button"
44
+ return (
45
+ <Comp
46
+ className={cn(buttonVariants({ variant, size, className }))}
47
+ ref={ref}
48
+ {...props}
49
+ />
50
+ )
51
+ },
52
+ )
53
+ Button.displayName = "Button"
54
+
55
+ export { Button, buttonVariants }
FRRONTEEEND/components/ui/card.tsx ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn(
11
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
12
+ className,
13
+ )}
14
+ {...props}
15
+ />
16
+ ))
17
+ Card.displayName = "Card"
18
+
19
+ const CardHeader = React.forwardRef<
20
+ HTMLDivElement,
21
+ React.HTMLAttributes<HTMLDivElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <div
24
+ ref={ref}
25
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
26
+ {...props}
27
+ />
28
+ ))
29
+ CardHeader.displayName = "CardHeader"
30
+
31
+ const CardTitle = React.forwardRef<
32
+ HTMLParagraphElement,
33
+ React.HTMLAttributes<HTMLHeadingElement>
34
+ >(({ className, ...props }, ref) => (
35
+ <h3
36
+ ref={ref}
37
+ className={cn(
38
+ "text-2xl font-semibold leading-none tracking-tight",
39
+ className,
40
+ )}
41
+ {...props}
42
+ />
43
+ ))
44
+ CardTitle.displayName = "CardTitle"
45
+
46
+ const CardDescription = React.forwardRef<
47
+ HTMLParagraphElement,
48
+ React.HTMLAttributes<HTMLParagraphElement>
49
+ >(({ className, ...props }, ref) => (
50
+ <p
51
+ ref={ref}
52
+ className={cn("text-sm text-muted-foreground", className)}
53
+ {...props}
54
+ />
55
+ ))
56
+ CardDescription.displayName = "CardDescription"
57
+
58
+ const CardContent = React.forwardRef<
59
+ HTMLDivElement,
60
+ React.HTMLAttributes<HTMLDivElement>
61
+ >(({ className, ...props }, ref) => (
62
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
63
+ ))
64
+ CardContent.displayName = "CardContent"
65
+
66
+ const CardFooter = React.forwardRef<
67
+ HTMLDivElement,
68
+ React.HTMLAttributes<HTMLDivElement>
69
+ >(({ className, ...props }, ref) => (
70
+ <div
71
+ ref={ref}
72
+ className={cn("flex items-center p-6 pt-0", className)}
73
+ {...props}
74
+ />
75
+ ))
76
+ CardFooter.displayName = "CardFooter"
77
+
78
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
FRRONTEEEND/components/ui/checkbox.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5
+ import { Check } from "lucide-react"
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const Checkbox = React.forwardRef<
9
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
11
+ >(({ className, ...props }, ref) => (
12
+ <CheckboxPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ <CheckboxPrimitive.Indicator
21
+ className={cn("flex items-center justify-center text-current")}
22
+ >
23
+ <Check className="h-4 w-4" />
24
+ </CheckboxPrimitive.Indicator>
25
+ </CheckboxPrimitive.Root>
26
+ ))
27
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
28
+
29
+ export { Checkbox }
FRRONTEEEND/components/ui/input.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ export interface InputProps
5
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
6
+
7
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
+ ({ className, type, ...props }, ref) => {
9
+ return (
10
+ <input
11
+ type={type}
12
+ className={cn(
13
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
14
+ className
15
+ )}
16
+ ref={ref}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+ )
22
+ Input.displayName = "Input"
23
+
24
+ export { Input }
FRRONTEEEND/components/ui/label.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as LabelPrimitive from "@radix-ui/react-label"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const labelVariants = cva(
9
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
10
+ )
11
+
12
+ const Label = React.forwardRef<
13
+ React.ElementRef<typeof LabelPrimitive.Root>,
14
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
15
+ VariantProps<typeof labelVariants>
16
+ >(({ className, ...props }, ref) => (
17
+ <LabelPrimitive.Root
18
+ ref={ref}
19
+ className={cn(labelVariants(), className)}
20
+ {...props}
21
+ />
22
+ ))
23
+ Label.displayName = LabelPrimitive.Root.displayName
24
+
25
+ export { Label }
FRRONTEEEND/components/ui/radio-group.tsx ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5
+ import { Circle } from "lucide-react"
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const RadioGroup = React.forwardRef<
9
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
11
+ >(({ className, ...props }, ref) => {
12
+ return (
13
+ <RadioGroupPrimitive.Root
14
+ className={cn("grid gap-2", className)}
15
+ {...props}
16
+ ref={ref}
17
+ />
18
+ )
19
+ })
20
+ RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
21
+
22
+ const RadioGroupItem = React.forwardRef<
23
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
24
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
25
+ >(({ className, ...props }, ref) => {
26
+ return (
27
+ <RadioGroupPrimitive.Item
28
+ ref={ref}
29
+ className={cn(
30
+ "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
31
+ className,
32
+ )}
33
+ {...props}
34
+ >
35
+ <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
36
+ <Circle className="h-2.5 w-2.5 fill-current text-current" />
37
+ </RadioGroupPrimitive.Indicator>
38
+ </RadioGroupPrimitive.Item>
39
+ )
40
+ })
41
+ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
42
+
43
+ export { RadioGroup, RadioGroupItem }
FRRONTEEEND/components/ui/select.tsx ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SelectPrimitive from "@radix-ui/react-select"
5
+ import { Check, ChevronDown, ChevronUp } from "lucide-react"
6
+ import { cn } from "../../lib/utils"
7
+
8
+ const Select = SelectPrimitive.Root
9
+
10
+ const SelectGroup = SelectPrimitive.Group
11
+
12
+ const SelectValue = SelectPrimitive.Value
13
+
14
+ const SelectTrigger = React.forwardRef<
15
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
16
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
17
+ >(({ className, children, ...props }, ref) => (
18
+ <SelectPrimitive.Trigger
19
+ ref={ref}
20
+ className={cn(
21
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
22
+ className,
23
+ )}
24
+ {...props}
25
+ >
26
+ {children}
27
+ <SelectPrimitive.Icon asChild>
28
+ <ChevronDown className="h-4 w-4 opacity-50" />
29
+ </SelectPrimitive.Icon>
30
+ </SelectPrimitive.Trigger>
31
+ ))
32
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
33
+
34
+ const SelectScrollUpButton = React.forwardRef<
35
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
36
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
37
+ >(({ className, ...props }, ref) => (
38
+ <SelectPrimitive.ScrollUpButton
39
+ ref={ref}
40
+ className={cn(
41
+ "flex cursor-default items-center justify-center py-1",
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <ChevronUp className="h-4 w-4" />
47
+ </SelectPrimitive.ScrollUpButton>
48
+ ))
49
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
50
+
51
+ const SelectScrollDownButton = React.forwardRef<
52
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
53
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
54
+ >(({ className, ...props }, ref) => (
55
+ <SelectPrimitive.ScrollDownButton
56
+ ref={ref}
57
+ className={cn(
58
+ "flex cursor-default items-center justify-center py-1",
59
+ className,
60
+ )}
61
+ {...props}
62
+ >
63
+ <ChevronDown className="h-4 w-4" />
64
+ </SelectPrimitive.ScrollDownButton>
65
+ ))
66
+ SelectScrollDownButton.displayName =
67
+ SelectPrimitive.ScrollDownButton.displayName
68
+
69
+ const SelectContent = React.forwardRef<
70
+ React.ElementRef<typeof SelectPrimitive.Content>,
71
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
72
+ >(({ className, children, position = "popper", ...props }, ref) => (
73
+ <SelectPrimitive.Portal>
74
+ <SelectPrimitive.Content
75
+ ref={ref}
76
+ className={cn(
77
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
78
+ position === "popper" &&
79
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
80
+ className,
81
+ )}
82
+ position={position}
83
+ {...props}
84
+ >
85
+ <SelectScrollUpButton />
86
+ <SelectPrimitive.Viewport
87
+ className={cn(
88
+ "p-1",
89
+ position === "popper" &&
90
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
91
+ )}
92
+ >
93
+ {children}
94
+ </SelectPrimitive.Viewport>
95
+ <SelectScrollDownButton />
96
+ </SelectPrimitive.Content>
97
+ </SelectPrimitive.Portal>
98
+ ))
99
+ SelectContent.displayName = SelectPrimitive.Content.displayName
100
+
101
+ const SelectLabel = React.forwardRef<
102
+ React.ElementRef<typeof SelectPrimitive.Label>,
103
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
104
+ >(({ className, ...props }, ref) => (
105
+ <SelectPrimitive.Label
106
+ ref={ref}
107
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
108
+ {...props}
109
+ />
110
+ ))
111
+ SelectLabel.displayName = SelectPrimitive.Label.displayName
112
+
113
+ const SelectItem = React.forwardRef<
114
+ React.ElementRef<typeof SelectPrimitive.Item>,
115
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
116
+ >(({ className, children, ...props }, ref) => (
117
+ <SelectPrimitive.Item
118
+ ref={ref}
119
+ className={cn(
120
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
121
+ className,
122
+ )}
123
+ {...props}
124
+ >
125
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
126
+ <SelectPrimitive.ItemIndicator>
127
+ <Check className="h-4 w-4" />
128
+ </SelectPrimitive.ItemIndicator>
129
+ </span>
130
+
131
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
132
+ </SelectPrimitive.Item>
133
+ ))
134
+ SelectItem.displayName = SelectPrimitive.Item.displayName
135
+
136
+ const SelectSeparator = React.forwardRef<
137
+ React.ElementRef<typeof SelectPrimitive.Separator>,
138
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
139
+ >(({ className, ...props }, ref) => (
140
+ <SelectPrimitive.Separator
141
+ ref={ref}
142
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
143
+ {...props}
144
+ />
145
+ ))
146
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
147
+
148
+ export {
149
+ Select,
150
+ SelectGroup,
151
+ SelectValue,
152
+ SelectTrigger,
153
+ SelectContent,
154
+ SelectLabel,
155
+ SelectItem,
156
+ SelectSeparator,
157
+ SelectScrollUpButton,
158
+ SelectScrollDownButton,
159
+ }
FRRONTEEEND/components/ui/textarea.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ export interface TextareaProps
5
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
6
+
7
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
8
+ ({ className, ...props }, ref) => {
9
+ return (
10
+ <textarea
11
+ className={cn(
12
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
13
+ className
14
+ )}
15
+ ref={ref}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+ )
21
+ Textarea.displayName = "Textarea"
22
+
23
+ export { Textarea }
FRRONTEEEND/index.html CHANGED
@@ -23,6 +23,29 @@
23
  }
24
  </script>
25
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  body {
27
  margin: 0;
28
  background-color: #030303;
@@ -35,6 +58,38 @@
35
  background-color: rgba(99, 102, 241, 0.3);
36
  color: white;
37
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </style>
39
  <script type="importmap">
40
  {
 
23
  }
24
  </script>
25
  <style>
26
+ :root {
27
+ --background: 0 0% 1%;
28
+ --foreground: 0 0% 98%;
29
+ --card: 0 0% 3%;
30
+ --card-foreground: 0 0% 98%;
31
+ --popover: 0 0% 6%;
32
+ --popover-foreground: 0 0% 98%;
33
+ --primary: 239 84% 67%;
34
+ --primary-foreground: 0 0% 100%;
35
+ --secondary: 0 0% 10%;
36
+ --secondary-foreground: 0 0% 98%;
37
+ --muted: 0 0% 15%;
38
+ --muted-foreground: 0 0% 64%;
39
+ --accent: 0 0% 15%;
40
+ --accent-foreground: 0 0% 98%;
41
+ --destructive: 0 62.8% 30.6%;
42
+ --destructive-foreground: 0 0% 98%;
43
+ --border: 0 0% 14.9%;
44
+ --input: 0 0% 14.9%;
45
+ --ring: 239 84% 67%;
46
+ --radius: 0.75rem;
47
+ }
48
+
49
  body {
50
  margin: 0;
51
  background-color: #030303;
 
58
  background-color: rgba(99, 102, 241, 0.3);
59
  color: white;
60
  }
61
+
62
+ /* Shadcn color utilities */
63
+ .bg-background { background-color: hsl(var(--background)); }
64
+ .bg-card { background-color: hsl(var(--card)); }
65
+ .bg-popover { background-color: hsl(var(--popover)); }
66
+ .bg-primary { background-color: hsl(var(--primary)); }
67
+ .bg-secondary { background-color: hsl(var(--secondary)); }
68
+ .bg-muted { background-color: hsl(var(--muted)); }
69
+ .bg-accent { background-color: hsl(var(--accent)); }
70
+ .bg-destructive { background-color: hsl(var(--destructive)); }
71
+
72
+ .text-foreground { color: hsl(var(--foreground)); }
73
+ .text-card-foreground { color: hsl(var(--card-foreground)); }
74
+ .text-popover-foreground { color: hsl(var(--popover-foreground)); }
75
+ .text-primary { color: hsl(var(--primary)); }
76
+ .text-primary-foreground { color: hsl(var(--primary-foreground)); }
77
+ .text-secondary-foreground { color: hsl(var(--secondary-foreground)); }
78
+ .text-muted-foreground { color: hsl(var(--muted-foreground)); }
79
+ .text-accent-foreground { color: hsl(var(--accent-foreground)); }
80
+ .text-destructive { color: hsl(var(--destructive)); }
81
+ .text-destructive-foreground { color: hsl(var(--destructive-foreground)); }
82
+
83
+ .border-border { border-color: hsl(var(--border)); }
84
+ .border-input { border-color: hsl(var(--input)); }
85
+ .border-primary { border-color: hsl(var(--primary)); }
86
+
87
+ .ring-ring { --tw-ring-color: hsl(var(--ring)); }
88
+ .ring-offset-background { --tw-ring-offset-color: hsl(var(--background)); }
89
+
90
+ /* Focus ring styles */
91
+ .focus-visible\\:ring-ring:focus-visible { --tw-ring-color: hsl(var(--ring)); }
92
+ .focus-visible\\:ring-offset-2:focus-visible { --tw-ring-offset-width: 2px; }
93
  </style>
94
  <script type="importmap">
95
  {
FRRONTEEEND/lib/AuthContext.tsx CHANGED
@@ -1,6 +1,6 @@
1
  import React, { createContext, useContext, useEffect, useState } from 'react';
2
  import { User, Session, AuthChangeEvent } from '@supabase/supabase-js';
3
- import { supabase, startUserSession, endUserSession } from './supabase';
4
 
5
  interface AuthContextType {
6
  user: User | null;
@@ -13,6 +13,7 @@ interface AuthContextType {
13
  signInWithGithub: () => Promise<{ error: any }>;
14
  signOut: () => Promise<void>;
15
  isAuthenticated: boolean;
 
16
  }
17
 
18
  const AuthContext = createContext<AuthContextType | undefined>(undefined);
@@ -22,8 +23,15 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
22
  const [session, setSession] = useState<Session | null>(null);
23
  const [dbSessionId, setDbSessionId] = useState<string | null>(null);
24
  const [loading, setLoading] = useState(true);
 
25
 
26
  useEffect(() => {
 
 
 
 
 
 
27
  // Get initial session
28
  supabase.auth.getSession().then(({ data: { session } }) => {
29
  setSession(session);
@@ -38,6 +46,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
38
  }
39
  });
40
  }
 
 
 
41
  });
42
 
43
  // Listen for auth changes
@@ -71,7 +82,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
71
  endUserSession(dbSessionId);
72
  }
73
  };
74
- }, []);
75
 
76
  const signIn = async (email: string, password: string) => {
77
  const { error } = await supabase.auth.signInWithPassword({ email, password });
@@ -123,7 +134,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
123
  signInWithGoogle,
124
  signInWithGithub,
125
  signOut,
126
- isAuthenticated: !!user
 
127
  }}
128
  >
129
  {children}
 
1
  import React, { createContext, useContext, useEffect, useState } from 'react';
2
  import { User, Session, AuthChangeEvent } from '@supabase/supabase-js';
3
+ import { supabase, startUserSession, endUserSession, isSupabaseConfigured } from './supabase';
4
 
5
  interface AuthContextType {
6
  user: User | null;
 
13
  signInWithGithub: () => Promise<{ error: any }>;
14
  signOut: () => Promise<void>;
15
  isAuthenticated: boolean;
16
+ isConfigured: boolean;
17
  }
18
 
19
  const AuthContext = createContext<AuthContextType | undefined>(undefined);
 
23
  const [session, setSession] = useState<Session | null>(null);
24
  const [dbSessionId, setDbSessionId] = useState<string | null>(null);
25
  const [loading, setLoading] = useState(true);
26
+ const configured = isSupabaseConfigured();
27
 
28
  useEffect(() => {
29
+ // If Supabase is not configured, skip auth initialization
30
+ if (!configured) {
31
+ setLoading(false);
32
+ return;
33
+ }
34
+
35
  // Get initial session
36
  supabase.auth.getSession().then(({ data: { session } }) => {
37
  setSession(session);
 
46
  }
47
  });
48
  }
49
+ }).catch((err) => {
50
+ console.error('Failed to get session:', err);
51
+ setLoading(false);
52
  });
53
 
54
  // Listen for auth changes
 
82
  endUserSession(dbSessionId);
83
  }
84
  };
85
+ }, [configured]);
86
 
87
  const signIn = async (email: string, password: string) => {
88
  const { error } = await supabase.auth.signInWithPassword({ email, password });
 
134
  signInWithGoogle,
135
  signInWithGithub,
136
  signOut,
137
+ isAuthenticated: !!user,
138
+ isConfigured: configured
139
  }}
140
  >
141
  {children}
FRRONTEEEND/lib/supabase.ts CHANGED
@@ -4,8 +4,13 @@ import { createClient } from '@supabase/supabase-js';
4
  const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || '';
5
  const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY || '';
6
 
 
 
 
 
 
7
  // Create Supabase client
8
- export const supabase = createClient(supabaseUrl, supabaseAnonKey);
9
 
10
  // Types for our analytics
11
  export interface UsageAnalytics {
 
4
  const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || '';
5
  const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY || '';
6
 
7
+ // Check if Supabase is configured
8
+ export const isSupabaseConfigured = () => {
9
+ return !!(supabaseUrl && supabaseAnonKey && supabaseUrl.includes('supabase'));
10
+ };
11
+
12
  // Create Supabase client
13
+ export const supabase = createClient(supabaseUrl || 'https://placeholder.supabase.co', supabaseAnonKey || 'placeholder');
14
 
15
  // Types for our analytics
16
  export interface UsageAnalytics {
FRRONTEEEND/package-lock.json CHANGED
@@ -8,13 +8,20 @@
8
  "name": "data-science-agent",
9
  "version": "0.0.0",
10
  "dependencies": {
 
 
 
 
 
11
  "@supabase/supabase-js": "^2.93.3",
 
12
  "clsx": "^2.1.1",
13
  "framer-motion": "^12.23.26",
14
  "lucide-react": "^0.562.0",
15
  "react": "^19.2.3",
16
  "react-dom": "^19.2.3",
17
  "react-markdown": "^9.0.1",
 
18
  "tailwind-merge": "^3.4.0"
19
  },
20
  "devDependencies": {
@@ -749,6 +756,44 @@
749
  "node": ">=18"
750
  }
751
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
  "node_modules/@jridgewell/gen-mapping": {
753
  "version": "0.3.13",
754
  "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -799,6 +844,719 @@
799
  "@jridgewell/sourcemap-codec": "^1.4.14"
800
  }
801
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
802
  "node_modules/@rolldown/pluginutils": {
803
  "version": "1.0.0-beta.53",
804
  "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
@@ -1354,6 +2112,18 @@
1354
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1355
  }
1356
  },
 
 
 
 
 
 
 
 
 
 
 
 
1357
  "node_modules/bail": {
1358
  "version": "2.0.2",
1359
  "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@@ -1480,6 +2250,18 @@
1480
  "url": "https://github.com/sponsors/wooorm"
1481
  }
1482
  },
 
 
 
 
 
 
 
 
 
 
 
 
1483
  "node_modules/clsx": {
1484
  "version": "2.1.1",
1485
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -1551,6 +2333,12 @@
1551
  "node": ">=6"
1552
  }
1553
  },
 
 
 
 
 
 
1554
  "node_modules/devlop": {
1555
  "version": "1.1.0",
1556
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
@@ -1709,6 +2497,15 @@
1709
  "node": ">=6.9.0"
1710
  }
1711
  },
 
 
 
 
 
 
 
 
 
1712
  "node_modules/hast-util-to-jsx-runtime": {
1713
  "version": "2.3.6",
1714
  "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
@@ -2679,6 +3476,75 @@
2679
  "node": ">=0.10.0"
2680
  }
2681
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2682
  "node_modules/remark-parse": {
2683
  "version": "11.0.0",
2684
  "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -2770,6 +3636,16 @@
2770
  "semver": "bin/semver.js"
2771
  }
2772
  },
 
 
 
 
 
 
 
 
 
 
2773
  "node_modules/source-map-js": {
2774
  "version": "1.2.1",
2775
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -3013,6 +3889,49 @@
3013
  "browserslist": ">= 4.21.0"
3014
  }
3015
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3016
  "node_modules/vfile": {
3017
  "version": "6.0.3",
3018
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
 
8
  "name": "data-science-agent",
9
  "version": "0.0.0",
10
  "dependencies": {
11
+ "@radix-ui/react-checkbox": "^1.3.3",
12
+ "@radix-ui/react-label": "^2.1.8",
13
+ "@radix-ui/react-radio-group": "^1.3.8",
14
+ "@radix-ui/react-select": "^2.2.6",
15
+ "@radix-ui/react-slot": "^1.2.4",
16
  "@supabase/supabase-js": "^2.93.3",
17
+ "class-variance-authority": "^0.7.1",
18
  "clsx": "^2.1.1",
19
  "framer-motion": "^12.23.26",
20
  "lucide-react": "^0.562.0",
21
  "react": "^19.2.3",
22
  "react-dom": "^19.2.3",
23
  "react-markdown": "^9.0.1",
24
+ "sonner": "^2.0.7",
25
  "tailwind-merge": "^3.4.0"
26
  },
27
  "devDependencies": {
 
756
  "node": ">=18"
757
  }
758
  },
759
+ "node_modules/@floating-ui/core": {
760
+ "version": "1.7.4",
761
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
762
+ "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
763
+ "license": "MIT",
764
+ "dependencies": {
765
+ "@floating-ui/utils": "^0.2.10"
766
+ }
767
+ },
768
+ "node_modules/@floating-ui/dom": {
769
+ "version": "1.7.5",
770
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
771
+ "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
772
+ "license": "MIT",
773
+ "dependencies": {
774
+ "@floating-ui/core": "^1.7.4",
775
+ "@floating-ui/utils": "^0.2.10"
776
+ }
777
+ },
778
+ "node_modules/@floating-ui/react-dom": {
779
+ "version": "2.1.7",
780
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz",
781
+ "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==",
782
+ "license": "MIT",
783
+ "dependencies": {
784
+ "@floating-ui/dom": "^1.7.5"
785
+ },
786
+ "peerDependencies": {
787
+ "react": ">=16.8.0",
788
+ "react-dom": ">=16.8.0"
789
+ }
790
+ },
791
+ "node_modules/@floating-ui/utils": {
792
+ "version": "0.2.10",
793
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
794
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
795
+ "license": "MIT"
796
+ },
797
  "node_modules/@jridgewell/gen-mapping": {
798
  "version": "0.3.13",
799
  "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
 
844
  "@jridgewell/sourcemap-codec": "^1.4.14"
845
  }
846
  },
847
+ "node_modules/@radix-ui/number": {
848
+ "version": "1.1.1",
849
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
850
+ "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
851
+ "license": "MIT"
852
+ },
853
+ "node_modules/@radix-ui/primitive": {
854
+ "version": "1.1.3",
855
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
856
+ "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
857
+ "license": "MIT"
858
+ },
859
+ "node_modules/@radix-ui/react-arrow": {
860
+ "version": "1.1.7",
861
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
862
+ "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
863
+ "license": "MIT",
864
+ "dependencies": {
865
+ "@radix-ui/react-primitive": "2.1.3"
866
+ },
867
+ "peerDependencies": {
868
+ "@types/react": "*",
869
+ "@types/react-dom": "*",
870
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
871
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
872
+ },
873
+ "peerDependenciesMeta": {
874
+ "@types/react": {
875
+ "optional": true
876
+ },
877
+ "@types/react-dom": {
878
+ "optional": true
879
+ }
880
+ }
881
+ },
882
+ "node_modules/@radix-ui/react-checkbox": {
883
+ "version": "1.3.3",
884
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
885
+ "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
886
+ "license": "MIT",
887
+ "dependencies": {
888
+ "@radix-ui/primitive": "1.1.3",
889
+ "@radix-ui/react-compose-refs": "1.1.2",
890
+ "@radix-ui/react-context": "1.1.2",
891
+ "@radix-ui/react-presence": "1.1.5",
892
+ "@radix-ui/react-primitive": "2.1.3",
893
+ "@radix-ui/react-use-controllable-state": "1.2.2",
894
+ "@radix-ui/react-use-previous": "1.1.1",
895
+ "@radix-ui/react-use-size": "1.1.1"
896
+ },
897
+ "peerDependencies": {
898
+ "@types/react": "*",
899
+ "@types/react-dom": "*",
900
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
901
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
902
+ },
903
+ "peerDependenciesMeta": {
904
+ "@types/react": {
905
+ "optional": true
906
+ },
907
+ "@types/react-dom": {
908
+ "optional": true
909
+ }
910
+ }
911
+ },
912
+ "node_modules/@radix-ui/react-collection": {
913
+ "version": "1.1.7",
914
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
915
+ "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
916
+ "license": "MIT",
917
+ "dependencies": {
918
+ "@radix-ui/react-compose-refs": "1.1.2",
919
+ "@radix-ui/react-context": "1.1.2",
920
+ "@radix-ui/react-primitive": "2.1.3",
921
+ "@radix-ui/react-slot": "1.2.3"
922
+ },
923
+ "peerDependencies": {
924
+ "@types/react": "*",
925
+ "@types/react-dom": "*",
926
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
927
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
928
+ },
929
+ "peerDependenciesMeta": {
930
+ "@types/react": {
931
+ "optional": true
932
+ },
933
+ "@types/react-dom": {
934
+ "optional": true
935
+ }
936
+ }
937
+ },
938
+ "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
939
+ "version": "1.2.3",
940
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
941
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
942
+ "license": "MIT",
943
+ "dependencies": {
944
+ "@radix-ui/react-compose-refs": "1.1.2"
945
+ },
946
+ "peerDependencies": {
947
+ "@types/react": "*",
948
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
949
+ },
950
+ "peerDependenciesMeta": {
951
+ "@types/react": {
952
+ "optional": true
953
+ }
954
+ }
955
+ },
956
+ "node_modules/@radix-ui/react-compose-refs": {
957
+ "version": "1.1.2",
958
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
959
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
960
+ "license": "MIT",
961
+ "peerDependencies": {
962
+ "@types/react": "*",
963
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
964
+ },
965
+ "peerDependenciesMeta": {
966
+ "@types/react": {
967
+ "optional": true
968
+ }
969
+ }
970
+ },
971
+ "node_modules/@radix-ui/react-context": {
972
+ "version": "1.1.2",
973
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
974
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
975
+ "license": "MIT",
976
+ "peerDependencies": {
977
+ "@types/react": "*",
978
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
979
+ },
980
+ "peerDependenciesMeta": {
981
+ "@types/react": {
982
+ "optional": true
983
+ }
984
+ }
985
+ },
986
+ "node_modules/@radix-ui/react-direction": {
987
+ "version": "1.1.1",
988
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
989
+ "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
990
+ "license": "MIT",
991
+ "peerDependencies": {
992
+ "@types/react": "*",
993
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
994
+ },
995
+ "peerDependenciesMeta": {
996
+ "@types/react": {
997
+ "optional": true
998
+ }
999
+ }
1000
+ },
1001
+ "node_modules/@radix-ui/react-dismissable-layer": {
1002
+ "version": "1.1.11",
1003
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
1004
+ "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
1005
+ "license": "MIT",
1006
+ "dependencies": {
1007
+ "@radix-ui/primitive": "1.1.3",
1008
+ "@radix-ui/react-compose-refs": "1.1.2",
1009
+ "@radix-ui/react-primitive": "2.1.3",
1010
+ "@radix-ui/react-use-callback-ref": "1.1.1",
1011
+ "@radix-ui/react-use-escape-keydown": "1.1.1"
1012
+ },
1013
+ "peerDependencies": {
1014
+ "@types/react": "*",
1015
+ "@types/react-dom": "*",
1016
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1017
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1018
+ },
1019
+ "peerDependenciesMeta": {
1020
+ "@types/react": {
1021
+ "optional": true
1022
+ },
1023
+ "@types/react-dom": {
1024
+ "optional": true
1025
+ }
1026
+ }
1027
+ },
1028
+ "node_modules/@radix-ui/react-focus-guards": {
1029
+ "version": "1.1.3",
1030
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
1031
+ "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
1032
+ "license": "MIT",
1033
+ "peerDependencies": {
1034
+ "@types/react": "*",
1035
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1036
+ },
1037
+ "peerDependenciesMeta": {
1038
+ "@types/react": {
1039
+ "optional": true
1040
+ }
1041
+ }
1042
+ },
1043
+ "node_modules/@radix-ui/react-focus-scope": {
1044
+ "version": "1.1.7",
1045
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
1046
+ "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
1047
+ "license": "MIT",
1048
+ "dependencies": {
1049
+ "@radix-ui/react-compose-refs": "1.1.2",
1050
+ "@radix-ui/react-primitive": "2.1.3",
1051
+ "@radix-ui/react-use-callback-ref": "1.1.1"
1052
+ },
1053
+ "peerDependencies": {
1054
+ "@types/react": "*",
1055
+ "@types/react-dom": "*",
1056
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1057
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1058
+ },
1059
+ "peerDependenciesMeta": {
1060
+ "@types/react": {
1061
+ "optional": true
1062
+ },
1063
+ "@types/react-dom": {
1064
+ "optional": true
1065
+ }
1066
+ }
1067
+ },
1068
+ "node_modules/@radix-ui/react-id": {
1069
+ "version": "1.1.1",
1070
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
1071
+ "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
1072
+ "license": "MIT",
1073
+ "dependencies": {
1074
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1075
+ },
1076
+ "peerDependencies": {
1077
+ "@types/react": "*",
1078
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1079
+ },
1080
+ "peerDependenciesMeta": {
1081
+ "@types/react": {
1082
+ "optional": true
1083
+ }
1084
+ }
1085
+ },
1086
+ "node_modules/@radix-ui/react-label": {
1087
+ "version": "2.1.8",
1088
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz",
1089
+ "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==",
1090
+ "license": "MIT",
1091
+ "dependencies": {
1092
+ "@radix-ui/react-primitive": "2.1.4"
1093
+ },
1094
+ "peerDependencies": {
1095
+ "@types/react": "*",
1096
+ "@types/react-dom": "*",
1097
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1098
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1099
+ },
1100
+ "peerDependenciesMeta": {
1101
+ "@types/react": {
1102
+ "optional": true
1103
+ },
1104
+ "@types/react-dom": {
1105
+ "optional": true
1106
+ }
1107
+ }
1108
+ },
1109
+ "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
1110
+ "version": "2.1.4",
1111
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz",
1112
+ "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==",
1113
+ "license": "MIT",
1114
+ "dependencies": {
1115
+ "@radix-ui/react-slot": "1.2.4"
1116
+ },
1117
+ "peerDependencies": {
1118
+ "@types/react": "*",
1119
+ "@types/react-dom": "*",
1120
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1121
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1122
+ },
1123
+ "peerDependenciesMeta": {
1124
+ "@types/react": {
1125
+ "optional": true
1126
+ },
1127
+ "@types/react-dom": {
1128
+ "optional": true
1129
+ }
1130
+ }
1131
+ },
1132
+ "node_modules/@radix-ui/react-popper": {
1133
+ "version": "1.2.8",
1134
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
1135
+ "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
1136
+ "license": "MIT",
1137
+ "dependencies": {
1138
+ "@floating-ui/react-dom": "^2.0.0",
1139
+ "@radix-ui/react-arrow": "1.1.7",
1140
+ "@radix-ui/react-compose-refs": "1.1.2",
1141
+ "@radix-ui/react-context": "1.1.2",
1142
+ "@radix-ui/react-primitive": "2.1.3",
1143
+ "@radix-ui/react-use-callback-ref": "1.1.1",
1144
+ "@radix-ui/react-use-layout-effect": "1.1.1",
1145
+ "@radix-ui/react-use-rect": "1.1.1",
1146
+ "@radix-ui/react-use-size": "1.1.1",
1147
+ "@radix-ui/rect": "1.1.1"
1148
+ },
1149
+ "peerDependencies": {
1150
+ "@types/react": "*",
1151
+ "@types/react-dom": "*",
1152
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1153
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1154
+ },
1155
+ "peerDependenciesMeta": {
1156
+ "@types/react": {
1157
+ "optional": true
1158
+ },
1159
+ "@types/react-dom": {
1160
+ "optional": true
1161
+ }
1162
+ }
1163
+ },
1164
+ "node_modules/@radix-ui/react-portal": {
1165
+ "version": "1.1.9",
1166
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
1167
+ "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
1168
+ "license": "MIT",
1169
+ "dependencies": {
1170
+ "@radix-ui/react-primitive": "2.1.3",
1171
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1172
+ },
1173
+ "peerDependencies": {
1174
+ "@types/react": "*",
1175
+ "@types/react-dom": "*",
1176
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1177
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1178
+ },
1179
+ "peerDependenciesMeta": {
1180
+ "@types/react": {
1181
+ "optional": true
1182
+ },
1183
+ "@types/react-dom": {
1184
+ "optional": true
1185
+ }
1186
+ }
1187
+ },
1188
+ "node_modules/@radix-ui/react-presence": {
1189
+ "version": "1.1.5",
1190
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
1191
+ "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
1192
+ "license": "MIT",
1193
+ "dependencies": {
1194
+ "@radix-ui/react-compose-refs": "1.1.2",
1195
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1196
+ },
1197
+ "peerDependencies": {
1198
+ "@types/react": "*",
1199
+ "@types/react-dom": "*",
1200
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1201
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1202
+ },
1203
+ "peerDependenciesMeta": {
1204
+ "@types/react": {
1205
+ "optional": true
1206
+ },
1207
+ "@types/react-dom": {
1208
+ "optional": true
1209
+ }
1210
+ }
1211
+ },
1212
+ "node_modules/@radix-ui/react-primitive": {
1213
+ "version": "2.1.3",
1214
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
1215
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
1216
+ "license": "MIT",
1217
+ "dependencies": {
1218
+ "@radix-ui/react-slot": "1.2.3"
1219
+ },
1220
+ "peerDependencies": {
1221
+ "@types/react": "*",
1222
+ "@types/react-dom": "*",
1223
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1224
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1225
+ },
1226
+ "peerDependenciesMeta": {
1227
+ "@types/react": {
1228
+ "optional": true
1229
+ },
1230
+ "@types/react-dom": {
1231
+ "optional": true
1232
+ }
1233
+ }
1234
+ },
1235
+ "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
1236
+ "version": "1.2.3",
1237
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
1238
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
1239
+ "license": "MIT",
1240
+ "dependencies": {
1241
+ "@radix-ui/react-compose-refs": "1.1.2"
1242
+ },
1243
+ "peerDependencies": {
1244
+ "@types/react": "*",
1245
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1246
+ },
1247
+ "peerDependenciesMeta": {
1248
+ "@types/react": {
1249
+ "optional": true
1250
+ }
1251
+ }
1252
+ },
1253
+ "node_modules/@radix-ui/react-radio-group": {
1254
+ "version": "1.3.8",
1255
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
1256
+ "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
1257
+ "license": "MIT",
1258
+ "dependencies": {
1259
+ "@radix-ui/primitive": "1.1.3",
1260
+ "@radix-ui/react-compose-refs": "1.1.2",
1261
+ "@radix-ui/react-context": "1.1.2",
1262
+ "@radix-ui/react-direction": "1.1.1",
1263
+ "@radix-ui/react-presence": "1.1.5",
1264
+ "@radix-ui/react-primitive": "2.1.3",
1265
+ "@radix-ui/react-roving-focus": "1.1.11",
1266
+ "@radix-ui/react-use-controllable-state": "1.2.2",
1267
+ "@radix-ui/react-use-previous": "1.1.1",
1268
+ "@radix-ui/react-use-size": "1.1.1"
1269
+ },
1270
+ "peerDependencies": {
1271
+ "@types/react": "*",
1272
+ "@types/react-dom": "*",
1273
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1274
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1275
+ },
1276
+ "peerDependenciesMeta": {
1277
+ "@types/react": {
1278
+ "optional": true
1279
+ },
1280
+ "@types/react-dom": {
1281
+ "optional": true
1282
+ }
1283
+ }
1284
+ },
1285
+ "node_modules/@radix-ui/react-roving-focus": {
1286
+ "version": "1.1.11",
1287
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
1288
+ "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==",
1289
+ "license": "MIT",
1290
+ "dependencies": {
1291
+ "@radix-ui/primitive": "1.1.3",
1292
+ "@radix-ui/react-collection": "1.1.7",
1293
+ "@radix-ui/react-compose-refs": "1.1.2",
1294
+ "@radix-ui/react-context": "1.1.2",
1295
+ "@radix-ui/react-direction": "1.1.1",
1296
+ "@radix-ui/react-id": "1.1.1",
1297
+ "@radix-ui/react-primitive": "2.1.3",
1298
+ "@radix-ui/react-use-callback-ref": "1.1.1",
1299
+ "@radix-ui/react-use-controllable-state": "1.2.2"
1300
+ },
1301
+ "peerDependencies": {
1302
+ "@types/react": "*",
1303
+ "@types/react-dom": "*",
1304
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1305
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1306
+ },
1307
+ "peerDependenciesMeta": {
1308
+ "@types/react": {
1309
+ "optional": true
1310
+ },
1311
+ "@types/react-dom": {
1312
+ "optional": true
1313
+ }
1314
+ }
1315
+ },
1316
+ "node_modules/@radix-ui/react-select": {
1317
+ "version": "2.2.6",
1318
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
1319
+ "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==",
1320
+ "license": "MIT",
1321
+ "dependencies": {
1322
+ "@radix-ui/number": "1.1.1",
1323
+ "@radix-ui/primitive": "1.1.3",
1324
+ "@radix-ui/react-collection": "1.1.7",
1325
+ "@radix-ui/react-compose-refs": "1.1.2",
1326
+ "@radix-ui/react-context": "1.1.2",
1327
+ "@radix-ui/react-direction": "1.1.1",
1328
+ "@radix-ui/react-dismissable-layer": "1.1.11",
1329
+ "@radix-ui/react-focus-guards": "1.1.3",
1330
+ "@radix-ui/react-focus-scope": "1.1.7",
1331
+ "@radix-ui/react-id": "1.1.1",
1332
+ "@radix-ui/react-popper": "1.2.8",
1333
+ "@radix-ui/react-portal": "1.1.9",
1334
+ "@radix-ui/react-primitive": "2.1.3",
1335
+ "@radix-ui/react-slot": "1.2.3",
1336
+ "@radix-ui/react-use-callback-ref": "1.1.1",
1337
+ "@radix-ui/react-use-controllable-state": "1.2.2",
1338
+ "@radix-ui/react-use-layout-effect": "1.1.1",
1339
+ "@radix-ui/react-use-previous": "1.1.1",
1340
+ "@radix-ui/react-visually-hidden": "1.2.3",
1341
+ "aria-hidden": "^1.2.4",
1342
+ "react-remove-scroll": "^2.6.3"
1343
+ },
1344
+ "peerDependencies": {
1345
+ "@types/react": "*",
1346
+ "@types/react-dom": "*",
1347
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1348
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1349
+ },
1350
+ "peerDependenciesMeta": {
1351
+ "@types/react": {
1352
+ "optional": true
1353
+ },
1354
+ "@types/react-dom": {
1355
+ "optional": true
1356
+ }
1357
+ }
1358
+ },
1359
+ "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
1360
+ "version": "1.2.3",
1361
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
1362
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
1363
+ "license": "MIT",
1364
+ "dependencies": {
1365
+ "@radix-ui/react-compose-refs": "1.1.2"
1366
+ },
1367
+ "peerDependencies": {
1368
+ "@types/react": "*",
1369
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1370
+ },
1371
+ "peerDependenciesMeta": {
1372
+ "@types/react": {
1373
+ "optional": true
1374
+ }
1375
+ }
1376
+ },
1377
+ "node_modules/@radix-ui/react-slot": {
1378
+ "version": "1.2.4",
1379
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
1380
+ "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
1381
+ "license": "MIT",
1382
+ "dependencies": {
1383
+ "@radix-ui/react-compose-refs": "1.1.2"
1384
+ },
1385
+ "peerDependencies": {
1386
+ "@types/react": "*",
1387
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1388
+ },
1389
+ "peerDependenciesMeta": {
1390
+ "@types/react": {
1391
+ "optional": true
1392
+ }
1393
+ }
1394
+ },
1395
+ "node_modules/@radix-ui/react-use-callback-ref": {
1396
+ "version": "1.1.1",
1397
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
1398
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
1399
+ "license": "MIT",
1400
+ "peerDependencies": {
1401
+ "@types/react": "*",
1402
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1403
+ },
1404
+ "peerDependenciesMeta": {
1405
+ "@types/react": {
1406
+ "optional": true
1407
+ }
1408
+ }
1409
+ },
1410
+ "node_modules/@radix-ui/react-use-controllable-state": {
1411
+ "version": "1.2.2",
1412
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
1413
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
1414
+ "license": "MIT",
1415
+ "dependencies": {
1416
+ "@radix-ui/react-use-effect-event": "0.0.2",
1417
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1418
+ },
1419
+ "peerDependencies": {
1420
+ "@types/react": "*",
1421
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1422
+ },
1423
+ "peerDependenciesMeta": {
1424
+ "@types/react": {
1425
+ "optional": true
1426
+ }
1427
+ }
1428
+ },
1429
+ "node_modules/@radix-ui/react-use-effect-event": {
1430
+ "version": "0.0.2",
1431
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
1432
+ "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
1433
+ "license": "MIT",
1434
+ "dependencies": {
1435
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1436
+ },
1437
+ "peerDependencies": {
1438
+ "@types/react": "*",
1439
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1440
+ },
1441
+ "peerDependenciesMeta": {
1442
+ "@types/react": {
1443
+ "optional": true
1444
+ }
1445
+ }
1446
+ },
1447
+ "node_modules/@radix-ui/react-use-escape-keydown": {
1448
+ "version": "1.1.1",
1449
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
1450
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
1451
+ "license": "MIT",
1452
+ "dependencies": {
1453
+ "@radix-ui/react-use-callback-ref": "1.1.1"
1454
+ },
1455
+ "peerDependencies": {
1456
+ "@types/react": "*",
1457
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1458
+ },
1459
+ "peerDependenciesMeta": {
1460
+ "@types/react": {
1461
+ "optional": true
1462
+ }
1463
+ }
1464
+ },
1465
+ "node_modules/@radix-ui/react-use-layout-effect": {
1466
+ "version": "1.1.1",
1467
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
1468
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
1469
+ "license": "MIT",
1470
+ "peerDependencies": {
1471
+ "@types/react": "*",
1472
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1473
+ },
1474
+ "peerDependenciesMeta": {
1475
+ "@types/react": {
1476
+ "optional": true
1477
+ }
1478
+ }
1479
+ },
1480
+ "node_modules/@radix-ui/react-use-previous": {
1481
+ "version": "1.1.1",
1482
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
1483
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
1484
+ "license": "MIT",
1485
+ "peerDependencies": {
1486
+ "@types/react": "*",
1487
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1488
+ },
1489
+ "peerDependenciesMeta": {
1490
+ "@types/react": {
1491
+ "optional": true
1492
+ }
1493
+ }
1494
+ },
1495
+ "node_modules/@radix-ui/react-use-rect": {
1496
+ "version": "1.1.1",
1497
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
1498
+ "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
1499
+ "license": "MIT",
1500
+ "dependencies": {
1501
+ "@radix-ui/rect": "1.1.1"
1502
+ },
1503
+ "peerDependencies": {
1504
+ "@types/react": "*",
1505
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1506
+ },
1507
+ "peerDependenciesMeta": {
1508
+ "@types/react": {
1509
+ "optional": true
1510
+ }
1511
+ }
1512
+ },
1513
+ "node_modules/@radix-ui/react-use-size": {
1514
+ "version": "1.1.1",
1515
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
1516
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
1517
+ "license": "MIT",
1518
+ "dependencies": {
1519
+ "@radix-ui/react-use-layout-effect": "1.1.1"
1520
+ },
1521
+ "peerDependencies": {
1522
+ "@types/react": "*",
1523
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1524
+ },
1525
+ "peerDependenciesMeta": {
1526
+ "@types/react": {
1527
+ "optional": true
1528
+ }
1529
+ }
1530
+ },
1531
+ "node_modules/@radix-ui/react-visually-hidden": {
1532
+ "version": "1.2.3",
1533
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
1534
+ "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
1535
+ "license": "MIT",
1536
+ "dependencies": {
1537
+ "@radix-ui/react-primitive": "2.1.3"
1538
+ },
1539
+ "peerDependencies": {
1540
+ "@types/react": "*",
1541
+ "@types/react-dom": "*",
1542
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
1543
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
1544
+ },
1545
+ "peerDependenciesMeta": {
1546
+ "@types/react": {
1547
+ "optional": true
1548
+ },
1549
+ "@types/react-dom": {
1550
+ "optional": true
1551
+ }
1552
+ }
1553
+ },
1554
+ "node_modules/@radix-ui/rect": {
1555
+ "version": "1.1.1",
1556
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
1557
+ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
1558
+ "license": "MIT"
1559
+ },
1560
  "node_modules/@rolldown/pluginutils": {
1561
  "version": "1.0.0-beta.53",
1562
  "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
 
2112
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
2113
  }
2114
  },
2115
+ "node_modules/aria-hidden": {
2116
+ "version": "1.2.6",
2117
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
2118
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
2119
+ "license": "MIT",
2120
+ "dependencies": {
2121
+ "tslib": "^2.0.0"
2122
+ },
2123
+ "engines": {
2124
+ "node": ">=10"
2125
+ }
2126
+ },
2127
  "node_modules/bail": {
2128
  "version": "2.0.2",
2129
  "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
 
2250
  "url": "https://github.com/sponsors/wooorm"
2251
  }
2252
  },
2253
+ "node_modules/class-variance-authority": {
2254
+ "version": "0.7.1",
2255
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
2256
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
2257
+ "license": "Apache-2.0",
2258
+ "dependencies": {
2259
+ "clsx": "^2.1.1"
2260
+ },
2261
+ "funding": {
2262
+ "url": "https://polar.sh/cva"
2263
+ }
2264
+ },
2265
  "node_modules/clsx": {
2266
  "version": "2.1.1",
2267
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
 
2333
  "node": ">=6"
2334
  }
2335
  },
2336
+ "node_modules/detect-node-es": {
2337
+ "version": "1.1.0",
2338
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
2339
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
2340
+ "license": "MIT"
2341
+ },
2342
  "node_modules/devlop": {
2343
  "version": "1.1.0",
2344
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
 
2497
  "node": ">=6.9.0"
2498
  }
2499
  },
2500
+ "node_modules/get-nonce": {
2501
+ "version": "1.0.1",
2502
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
2503
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
2504
+ "license": "MIT",
2505
+ "engines": {
2506
+ "node": ">=6"
2507
+ }
2508
+ },
2509
  "node_modules/hast-util-to-jsx-runtime": {
2510
  "version": "2.3.6",
2511
  "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
 
3476
  "node": ">=0.10.0"
3477
  }
3478
  },
3479
+ "node_modules/react-remove-scroll": {
3480
+ "version": "2.7.2",
3481
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
3482
+ "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
3483
+ "license": "MIT",
3484
+ "dependencies": {
3485
+ "react-remove-scroll-bar": "^2.3.7",
3486
+ "react-style-singleton": "^2.2.3",
3487
+ "tslib": "^2.1.0",
3488
+ "use-callback-ref": "^1.3.3",
3489
+ "use-sidecar": "^1.1.3"
3490
+ },
3491
+ "engines": {
3492
+ "node": ">=10"
3493
+ },
3494
+ "peerDependencies": {
3495
+ "@types/react": "*",
3496
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
3497
+ },
3498
+ "peerDependenciesMeta": {
3499
+ "@types/react": {
3500
+ "optional": true
3501
+ }
3502
+ }
3503
+ },
3504
+ "node_modules/react-remove-scroll-bar": {
3505
+ "version": "2.3.8",
3506
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
3507
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
3508
+ "license": "MIT",
3509
+ "dependencies": {
3510
+ "react-style-singleton": "^2.2.2",
3511
+ "tslib": "^2.0.0"
3512
+ },
3513
+ "engines": {
3514
+ "node": ">=10"
3515
+ },
3516
+ "peerDependencies": {
3517
+ "@types/react": "*",
3518
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
3519
+ },
3520
+ "peerDependenciesMeta": {
3521
+ "@types/react": {
3522
+ "optional": true
3523
+ }
3524
+ }
3525
+ },
3526
+ "node_modules/react-style-singleton": {
3527
+ "version": "2.2.3",
3528
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
3529
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
3530
+ "license": "MIT",
3531
+ "dependencies": {
3532
+ "get-nonce": "^1.0.0",
3533
+ "tslib": "^2.0.0"
3534
+ },
3535
+ "engines": {
3536
+ "node": ">=10"
3537
+ },
3538
+ "peerDependencies": {
3539
+ "@types/react": "*",
3540
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
3541
+ },
3542
+ "peerDependenciesMeta": {
3543
+ "@types/react": {
3544
+ "optional": true
3545
+ }
3546
+ }
3547
+ },
3548
  "node_modules/remark-parse": {
3549
  "version": "11.0.0",
3550
  "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
 
3636
  "semver": "bin/semver.js"
3637
  }
3638
  },
3639
+ "node_modules/sonner": {
3640
+ "version": "2.0.7",
3641
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz",
3642
+ "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==",
3643
+ "license": "MIT",
3644
+ "peerDependencies": {
3645
+ "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
3646
+ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
3647
+ }
3648
+ },
3649
  "node_modules/source-map-js": {
3650
  "version": "1.2.1",
3651
  "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
 
3889
  "browserslist": ">= 4.21.0"
3890
  }
3891
  },
3892
+ "node_modules/use-callback-ref": {
3893
+ "version": "1.3.3",
3894
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
3895
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
3896
+ "license": "MIT",
3897
+ "dependencies": {
3898
+ "tslib": "^2.0.0"
3899
+ },
3900
+ "engines": {
3901
+ "node": ">=10"
3902
+ },
3903
+ "peerDependencies": {
3904
+ "@types/react": "*",
3905
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
3906
+ },
3907
+ "peerDependenciesMeta": {
3908
+ "@types/react": {
3909
+ "optional": true
3910
+ }
3911
+ }
3912
+ },
3913
+ "node_modules/use-sidecar": {
3914
+ "version": "1.1.3",
3915
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
3916
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
3917
+ "license": "MIT",
3918
+ "dependencies": {
3919
+ "detect-node-es": "^1.1.0",
3920
+ "tslib": "^2.0.0"
3921
+ },
3922
+ "engines": {
3923
+ "node": ">=10"
3924
+ },
3925
+ "peerDependencies": {
3926
+ "@types/react": "*",
3927
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
3928
+ },
3929
+ "peerDependenciesMeta": {
3930
+ "@types/react": {
3931
+ "optional": true
3932
+ }
3933
+ }
3934
+ },
3935
  "node_modules/vfile": {
3936
  "version": "6.0.3",
3937
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
FRRONTEEEND/package.json CHANGED
@@ -9,13 +9,20 @@
9
  "preview": "vite preview"
10
  },
11
  "dependencies": {
 
 
 
 
 
12
  "@supabase/supabase-js": "^2.93.3",
 
13
  "clsx": "^2.1.1",
14
  "framer-motion": "^12.23.26",
15
  "lucide-react": "^0.562.0",
16
  "react": "^19.2.3",
17
  "react-dom": "^19.2.3",
18
  "react-markdown": "^9.0.1",
 
19
  "tailwind-merge": "^3.4.0"
20
  },
21
  "devDependencies": {
 
9
  "preview": "vite preview"
10
  },
11
  "dependencies": {
12
+ "@radix-ui/react-checkbox": "^1.3.3",
13
+ "@radix-ui/react-label": "^2.1.8",
14
+ "@radix-ui/react-radio-group": "^1.3.8",
15
+ "@radix-ui/react-select": "^2.2.6",
16
+ "@radix-ui/react-slot": "^1.2.4",
17
  "@supabase/supabase-js": "^2.93.3",
18
+ "class-variance-authority": "^0.7.1",
19
  "clsx": "^2.1.1",
20
  "framer-motion": "^12.23.26",
21
  "lucide-react": "^0.562.0",
22
  "react": "^19.2.3",
23
  "react-dom": "^19.2.3",
24
  "react-markdown": "^9.0.1",
25
+ "sonner": "^2.0.7",
26
  "tailwind-merge": "^3.4.0"
27
  },
28
  "devDependencies": {
idx.html ADDED
@@ -0,0 +1,1281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cute Lamp Login - Ultimate Edition</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ justify-content: center;
22
+ overflow-x: hidden;
23
+ position: relative;
24
+ padding: 20px;
25
+ }
26
+
27
+ /* Enhanced Animated background particles */
28
+ .particles {
29
+ position: fixed;
30
+ width: 100%;
31
+ height: 100%;
32
+ overflow: hidden;
33
+ z-index: 0;
34
+ top: 0;
35
+ left: 0;
36
+ }
37
+
38
+ .particle {
39
+ position: absolute;
40
+ width: 4px;
41
+ height: 4px;
42
+ background: radial-gradient(circle, #ffa500, transparent);
43
+ border-radius: 50%;
44
+ animation: float 15s infinite ease-in-out;
45
+ box-shadow: 0 0 10px #ffa500;
46
+ }
47
+
48
+ @keyframes float {
49
+ 0% { transform: translate(0, 100vh) scale(0); opacity: 0; }
50
+ 10% { opacity: 1; }
51
+ 90% { opacity: 1; }
52
+ 100% { transform: translate(var(--tx), -100vh) scale(1); opacity: 0; }
53
+ }
54
+
55
+ /* Sparkles */
56
+ .sparkle {
57
+ position: absolute;
58
+ width: 3px;
59
+ height: 3px;
60
+ background: #fff;
61
+ border-radius: 50%;
62
+ animation: sparkle 3s infinite;
63
+ box-shadow: 0 0 8px #fff;
64
+ }
65
+
66
+ @keyframes sparkle {
67
+ 0%, 100% { opacity: 0; transform: scale(0); }
68
+ 50% { opacity: 1; transform: scale(1.5); }
69
+ }
70
+
71
+ .title {
72
+ font-size: 52px;
73
+ font-weight: 700;
74
+ background: linear-gradient(90deg, #ffa500, #ff6b35, #ffa500);
75
+ background-size: 200% auto;
76
+ -webkit-background-clip: text;
77
+ -webkit-text-fill-color: transparent;
78
+ background-clip: text;
79
+ margin-bottom: 50px;
80
+ text-align: center;
81
+ z-index: 2;
82
+ text-shadow: 0 0 30px rgba(255, 165, 0, 0.5);
83
+ animation: gradientMove 3s linear infinite, fadeInDown 1s ease;
84
+ letter-spacing: 2px;
85
+ }
86
+
87
+ @keyframes gradientMove {
88
+ 0% { background-position: 0% center; }
89
+ 100% { background-position: 200% center; }
90
+ }
91
+
92
+ @keyframes fadeInDown {
93
+ from {
94
+ opacity: 0;
95
+ transform: translateY(-50px);
96
+ }
97
+ to {
98
+ opacity: 1;
99
+ transform: translateY(0);
100
+ }
101
+ }
102
+
103
+ .container {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ gap: 80px;
108
+ background: rgba(30, 40, 60, 0.5);
109
+ padding: 70px;
110
+ border-radius: 30px;
111
+ border: 2px solid rgba(255, 255, 255, 0.1);
112
+ backdrop-filter: blur(20px);
113
+ box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6), inset 0 0 40px rgba(255, 165, 0, 0.05);
114
+ z-index: 2;
115
+ animation: fadeIn 1.2s ease 0.3s backwards;
116
+ position: relative;
117
+ max-width: 1200px;
118
+ }
119
+
120
+ @keyframes fadeIn {
121
+ from {
122
+ opacity: 0;
123
+ transform: scale(0.8) rotateX(10deg);
124
+ }
125
+ to {
126
+ opacity: 1;
127
+ transform: scale(1) rotateX(0);
128
+ }
129
+ }
130
+
131
+ /* Enhanced Lamp Section */
132
+ .lamp-section {
133
+ position: relative;
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ width: 300px;
138
+ height: 400px;
139
+ }
140
+
141
+ .lamp-container {
142
+ position: relative;
143
+ cursor: pointer;
144
+ transition: transform 0.3s ease;
145
+ }
146
+
147
+ .lamp-container:hover {
148
+ transform: scale(1.05);
149
+ }
150
+
151
+ .lamp {
152
+ position: relative;
153
+ width: 200px;
154
+ height: 320px;
155
+ display: flex;
156
+ align-items: flex-end;
157
+ justify-content: center;
158
+ animation: lampFloat 4s ease-in-out infinite;
159
+ filter: drop-shadow(0 10px 30px rgba(0, 0, 0, 0.5));
160
+ }
161
+
162
+ @keyframes lampFloat {
163
+ 0%, 100% { transform: translateY(0) rotate(0deg); }
164
+ 25% { transform: translateY(-12px) rotate(-2deg); }
165
+ 75% { transform: translateY(-8px) rotate(2deg); }
166
+ }
167
+
168
+ /* Electric Cord */
169
+ .cord {
170
+ position: absolute;
171
+ width: 4px;
172
+ height: 80px;
173
+ background: linear-gradient(180deg, #555, #333);
174
+ top: -80px;
175
+ left: 50%;
176
+ transform: translateX(-50%);
177
+ border-radius: 2px;
178
+ animation: cordSwing 4s ease-in-out infinite;
179
+ transform-origin: top center;
180
+ }
181
+
182
+ @keyframes cordSwing {
183
+ 0%, 100% { transform: translateX(-50%) rotate(0deg); }
184
+ 25% { transform: translateX(-50%) rotate(-3deg); }
185
+ 75% { transform: translateX(-50%) rotate(3deg); }
186
+ }
187
+
188
+ .cord-plug {
189
+ position: absolute;
190
+ top: -10px;
191
+ left: 50%;
192
+ transform: translateX(-50%);
193
+ width: 12px;
194
+ height: 12px;
195
+ background: #444;
196
+ border-radius: 50%;
197
+ box-shadow: 0 2px 5px rgba(0,0,0,0.5);
198
+ }
199
+
200
+ .lamp-stand {
201
+ width: 10px;
202
+ height: 160px;
203
+ background: linear-gradient(180deg, #e8e8e8, #fff, #e8e8e8);
204
+ border-radius: 5px;
205
+ position: absolute;
206
+ bottom: 0;
207
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.4), inset 0 0 10px rgba(255, 255, 255, 0.5);
208
+ }
209
+
210
+ .lamp-base {
211
+ width: 80px;
212
+ height: 25px;
213
+ background: linear-gradient(180deg, #d0d0d0, #f8f8f8, #d0d0d0);
214
+ border-radius: 15px;
215
+ position: absolute;
216
+ bottom: 0;
217
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.5), inset 0 -3px 10px rgba(0, 0, 0, 0.2);
218
+ }
219
+
220
+ .lamp-shade {
221
+ width: 140px;
222
+ height: 110px;
223
+ background: linear-gradient(180deg, #95d4a8, #a8d5ba, #8bc9a8);
224
+ clip-path: polygon(22% 0%, 78% 0%, 100% 100%, 0% 100%);
225
+ position: absolute;
226
+ top: 40px;
227
+ border-radius: 10px 10px 0 0;
228
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), inset 0 -15px 40px rgba(255, 255, 255, 0.4);
229
+ animation: shadeGlow 3s ease-in-out infinite;
230
+ transition: all 0.3s ease;
231
+ }
232
+
233
+ .lamp-shade.happy {
234
+ filter: brightness(1.2);
235
+ }
236
+
237
+ .lamp-shade.sad {
238
+ filter: brightness(0.8);
239
+ }
240
+
241
+ @keyframes shadeGlow {
242
+ 0%, 100% {
243
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4), inset 0 -15px 40px rgba(255, 255, 255, 0.4);
244
+ filter: brightness(1);
245
+ }
246
+ 50% {
247
+ box-shadow: 0 10px 40px rgba(168, 213, 186, 0.7), inset 0 -15px 50px rgba(255, 255, 255, 0.6);
248
+ filter: brightness(1.15);
249
+ }
250
+ }
251
+
252
+ /* Enhanced Lamp Face with expressions */
253
+ .lamp-face {
254
+ position: absolute;
255
+ top: 75px;
256
+ width: 140px;
257
+ height: 90px;
258
+ z-index: 2;
259
+ transition: all 0.3s ease;
260
+ }
261
+
262
+ .lamp-eye {
263
+ width: 14px;
264
+ height: 20px;
265
+ background: #2d3436;
266
+ border-radius: 50%;
267
+ position: absolute;
268
+ top: 20px;
269
+ transition: all 0.3s ease;
270
+ animation: blink 5s infinite;
271
+ }
272
+
273
+ .lamp-eye.left {
274
+ left: 35px;
275
+ }
276
+
277
+ .lamp-eye.right {
278
+ right: 35px;
279
+ }
280
+
281
+ .lamp-eye::before {
282
+ content: '';
283
+ position: absolute;
284
+ width: 5px;
285
+ height: 5px;
286
+ background: #fff;
287
+ border-radius: 50%;
288
+ top: 4px;
289
+ left: 3px;
290
+ animation: eyeShine 3s infinite;
291
+ }
292
+
293
+ @keyframes eyeShine {
294
+ 0%, 100% { opacity: 0.8; }
295
+ 50% { opacity: 1; }
296
+ }
297
+
298
+ @keyframes blink {
299
+ 0%, 46%, 50%, 100% { height: 20px; }
300
+ 48% { height: 2px; }
301
+ }
302
+
303
+ /* Lamp Tongue (when light is on) */
304
+ .lamp-tongue {
305
+ position: absolute;
306
+ width: 20px;
307
+ height: 25px;
308
+ background: #ff6b6b;
309
+ border-radius: 0 0 10px 10px;
310
+ bottom: 15px;
311
+ left: 50%;
312
+ transform: translateX(-50%) scaleY(0);
313
+ transform-origin: top;
314
+ opacity: 0;
315
+ transition: all 0.3s ease;
316
+ }
317
+
318
+ .lamp-tongue.show {
319
+ transform: translateX(-50%) scaleY(1);
320
+ opacity: 1;
321
+ animation: tongueWiggle 0.5s ease-in-out;
322
+ }
323
+
324
+ @keyframes tongueWiggle {
325
+ 0%, 100% { transform: translateX(-50%) scaleY(1) rotate(0deg); }
326
+ 25% { transform: translateX(-50%) scaleY(1) rotate(-5deg); }
327
+ 75% { transform: translateX(-50%) scaleY(1) rotate(5deg); }
328
+ }
329
+
330
+ .lamp-mouth {
331
+ width: 35px;
332
+ height: 18px;
333
+ border: 3px solid #e74c3c;
334
+ border-top: none;
335
+ border-radius: 0 0 35px 35px;
336
+ position: absolute;
337
+ top: 48px;
338
+ left: 50%;
339
+ transform: translateX(-50%);
340
+ transition: all 0.3s ease;
341
+ animation: smile 4s ease-in-out infinite;
342
+ }
343
+
344
+ .lamp-mouth.sad {
345
+ border-radius: 35px 35px 0 0;
346
+ border-top: 3px solid #e74c3c;
347
+ border-bottom: none;
348
+ top: 55px;
349
+ }
350
+
351
+ @keyframes smile {
352
+ 0%, 100% { width: 35px; }
353
+ 50% { width: 40px; }
354
+ }
355
+
356
+ .lamp-blush {
357
+ width: 22px;
358
+ height: 14px;
359
+ background: rgba(231, 76, 60, 0.5);
360
+ border-radius: 50%;
361
+ position: absolute;
362
+ top: 40px;
363
+ animation: blushPulse 3s ease-in-out infinite;
364
+ }
365
+
366
+ @keyframes blushPulse {
367
+ 0%, 100% { opacity: 0.5; }
368
+ 50% { opacity: 0.8; }
369
+ }
370
+
371
+ .lamp-blush.left {
372
+ left: 15px;
373
+ }
374
+
375
+ .lamp-blush.right {
376
+ right: 15px;
377
+ }
378
+
379
+ /* Enhanced Light glow effect */
380
+ .lamp-glow {
381
+ position: absolute;
382
+ bottom: -50px;
383
+ left: 50%;
384
+ transform: translateX(-50%);
385
+ width: 280px;
386
+ height: 280px;
387
+ background: radial-gradient(circle, rgba(255, 243, 176, 0.6) 0%, rgba(255, 220, 100, 0.3) 40%, transparent 70%);
388
+ border-radius: 50%;
389
+ animation: glowPulse 3s ease-in-out infinite;
390
+ pointer-events: none;
391
+ filter: blur(20px);
392
+ }
393
+
394
+ @keyframes glowPulse {
395
+ 0%, 100% {
396
+ opacity: 0.7;
397
+ transform: translateX(-50%) scale(1);
398
+ }
399
+ 50% {
400
+ opacity: 1;
401
+ transform: translateX(-50%) scale(1.15);
402
+ }
403
+ }
404
+
405
+ /* Enhanced Login Form */
406
+ .login-box {
407
+ background: linear-gradient(135deg, rgba(40, 50, 70, 0.8), rgba(30, 40, 60, 0.9));
408
+ padding: 50px 55px;
409
+ border-radius: 25px;
410
+ border: 2px solid rgba(76, 209, 55, 0.6);
411
+ box-shadow: 0 0 50px rgba(76, 209, 55, 0.4), inset 0 0 30px rgba(0, 0, 0, 0.3);
412
+ width: 420px;
413
+ backdrop-filter: blur(15px);
414
+ position: relative;
415
+ overflow: hidden;
416
+ }
417
+
418
+ .login-box::before {
419
+ content: '';
420
+ position: absolute;
421
+ top: -50%;
422
+ left: -50%;
423
+ width: 200%;
424
+ height: 200%;
425
+ background: linear-gradient(45deg, transparent, rgba(76, 209, 55, 0.1), transparent);
426
+ animation: shimmer 3s infinite;
427
+ }
428
+
429
+ @keyframes shimmer {
430
+ 0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
431
+ 100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
432
+ }
433
+
434
+ .login-box > * {
435
+ position: relative;
436
+ z-index: 1;
437
+ }
438
+
439
+ .login-box h2 {
440
+ text-align: center;
441
+ color: #fff;
442
+ font-size: 36px;
443
+ margin-bottom: 35px;
444
+ font-weight: 700;
445
+ text-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
446
+ animation: textGlow 2s ease-in-out infinite;
447
+ }
448
+
449
+ @keyframes textGlow {
450
+ 0%, 100% { text-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); }
451
+ 50% { text-shadow: 0 4px 20px rgba(76, 209, 55, 0.5); }
452
+ }
453
+
454
+ .input-group {
455
+ margin-bottom: 28px;
456
+ position: relative;
457
+ }
458
+
459
+ .input-group label {
460
+ display: block;
461
+ color: #b8c5d8;
462
+ font-size: 14px;
463
+ margin-bottom: 10px;
464
+ font-weight: 600;
465
+ letter-spacing: 0.5px;
466
+ }
467
+
468
+ .input-wrapper {
469
+ position: relative;
470
+ }
471
+
472
+ .input-group input {
473
+ width: 100%;
474
+ padding: 16px 50px 16px 20px;
475
+ background: rgba(20, 30, 50, 0.7);
476
+ border: 2px solid rgba(255, 255, 255, 0.1);
477
+ border-radius: 12px;
478
+ color: #fff;
479
+ font-size: 15px;
480
+ outline: none;
481
+ transition: all 0.4s ease;
482
+ font-family: inherit;
483
+ }
484
+
485
+ .input-group input::placeholder {
486
+ color: #6c7a89;
487
+ }
488
+
489
+ .input-group input:focus {
490
+ border-color: #4cd137;
491
+ box-shadow: 0 0 20px rgba(76, 209, 55, 0.4), inset 0 0 10px rgba(76, 209, 55, 0.1);
492
+ background: rgba(20, 30, 50, 0.9);
493
+ transform: translateY(-2px);
494
+ }
495
+
496
+ .input-group input.error {
497
+ border-color: #e74c3c;
498
+ animation: shake 0.5s;
499
+ }
500
+
501
+ @keyframes shake {
502
+ 0%, 100% { transform: translateX(0); }
503
+ 25% { transform: translateX(-10px); }
504
+ 75% { transform: translateX(10px); }
505
+ }
506
+
507
+ /* Password Toggle Eye */
508
+ .toggle-password {
509
+ position: absolute;
510
+ right: 15px;
511
+ top: 50%;
512
+ transform: translateY(-50%);
513
+ cursor: pointer;
514
+ color: #6c7a89;
515
+ font-size: 20px;
516
+ transition: color 0.3s ease;
517
+ user-select: none;
518
+ }
519
+
520
+ .toggle-password:hover {
521
+ color: #4cd137;
522
+ }
523
+
524
+ /* Social Login Buttons */
525
+ .social-login {
526
+ display: flex;
527
+ gap: 12px;
528
+ margin-bottom: 25px;
529
+ }
530
+
531
+ .social-btn {
532
+ flex: 1;
533
+ padding: 12px;
534
+ border: 2px solid rgba(255, 255, 255, 0.2);
535
+ background: rgba(30, 40, 60, 0.6);
536
+ border-radius: 10px;
537
+ color: #fff;
538
+ cursor: pointer;
539
+ transition: all 0.3s ease;
540
+ display: flex;
541
+ align-items: center;
542
+ justify-content: center;
543
+ gap: 8px;
544
+ font-size: 14px;
545
+ font-weight: 600;
546
+ }
547
+
548
+ .social-btn:hover {
549
+ transform: translateY(-3px);
550
+ border-color: #4cd137;
551
+ box-shadow: 0 5px 15px rgba(76, 209, 55, 0.3);
552
+ }
553
+
554
+ .divider {
555
+ text-align: center;
556
+ margin: 25px 0;
557
+ color: #6c7a89;
558
+ position: relative;
559
+ }
560
+
561
+ .divider::before,
562
+ .divider::after {
563
+ content: '';
564
+ position: absolute;
565
+ top: 50%;
566
+ width: 40%;
567
+ height: 1px;
568
+ background: rgba(255, 255, 255, 0.1);
569
+ }
570
+
571
+ .divider::before {
572
+ left: 0;
573
+ }
574
+
575
+ .divider::after {
576
+ right: 0;
577
+ }
578
+
579
+ .login-btn {
580
+ width: 100%;
581
+ padding: 17px;
582
+ background: linear-gradient(135deg, #4cd137, #44bd32, #3da82a);
583
+ background-size: 200% auto;
584
+ border: none;
585
+ border-radius: 12px;
586
+ color: #fff;
587
+ font-size: 18px;
588
+ font-weight: 700;
589
+ cursor: pointer;
590
+ transition: all 0.4s ease;
591
+ box-shadow: 0 6px 25px rgba(76, 209, 55, 0.5);
592
+ margin-top: 12px;
593
+ position: relative;
594
+ overflow: hidden;
595
+ letter-spacing: 1px;
596
+ }
597
+
598
+ .login-btn::before {
599
+ content: '';
600
+ position: absolute;
601
+ top: 0;
602
+ left: -100%;
603
+ width: 100%;
604
+ height: 100%;
605
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
606
+ transition: left 0.5s;
607
+ }
608
+
609
+ .login-btn:hover::before {
610
+ left: 100%;
611
+ }
612
+
613
+ .login-btn:hover {
614
+ transform: translateY(-4px);
615
+ box-shadow: 0 10px 30px rgba(76, 209, 55, 0.7);
616
+ background-position: right center;
617
+ }
618
+
619
+ .login-btn:active {
620
+ transform: translateY(-2px);
621
+ }
622
+
623
+ .login-btn.loading {
624
+ pointer-events: none;
625
+ opacity: 0.8;
626
+ }
627
+
628
+ .login-btn.loading::after {
629
+ content: '';
630
+ position: absolute;
631
+ width: 20px;
632
+ height: 20px;
633
+ border: 3px solid rgba(255, 255, 255, 0.3);
634
+ border-top-color: #fff;
635
+ border-radius: 50%;
636
+ animation: spin 0.8s linear infinite;
637
+ top: 50%;
638
+ left: 50%;
639
+ transform: translate(-50%, -50%);
640
+ }
641
+
642
+ @keyframes spin {
643
+ to { transform: translate(-50%, -50%) rotate(360deg); }
644
+ }
645
+
646
+ .forgot-password {
647
+ text-align: center;
648
+ margin-top: 22px;
649
+ }
650
+
651
+ .forgot-password a {
652
+ color: #7c8a9e;
653
+ text-decoration: none;
654
+ font-size: 14px;
655
+ transition: all 0.3s ease;
656
+ position: relative;
657
+ }
658
+
659
+ .forgot-password a::after {
660
+ content: '';
661
+ position: absolute;
662
+ bottom: -2px;
663
+ left: 0;
664
+ width: 0;
665
+ height: 2px;
666
+ background: #4cd137;
667
+ transition: width 0.3s ease;
668
+ }
669
+
670
+ .forgot-password a:hover {
671
+ color: #4cd137;
672
+ }
673
+
674
+ .forgot-password a:hover::after {
675
+ width: 100%;
676
+ }
677
+
678
+ /* Toast Notification */
679
+ .toast {
680
+ position: fixed;
681
+ top: 30px;
682
+ right: 30px;
683
+ background: linear-gradient(135deg, rgba(76, 209, 55, 0.95), rgba(68, 189, 50, 0.95));
684
+ color: #fff;
685
+ padding: 18px 28px;
686
+ border-radius: 12px;
687
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
688
+ z-index: 1000;
689
+ transform: translateX(400px);
690
+ opacity: 0;
691
+ transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
692
+ display: flex;
693
+ align-items: center;
694
+ gap: 12px;
695
+ font-weight: 600;
696
+ backdrop-filter: blur(10px);
697
+ }
698
+
699
+ .toast.show {
700
+ transform: translateX(0);
701
+ opacity: 1;
702
+ }
703
+
704
+ .toast.error {
705
+ background: linear-gradient(135deg, rgba(231, 76, 60, 0.95), rgba(192, 57, 43, 0.95));
706
+ }
707
+
708
+ .toast-icon {
709
+ font-size: 24px;
710
+ }
711
+
712
+ /* Code Display Section */
713
+ .code-section {
714
+ margin-top: 60px;
715
+ width: 100%;
716
+ max-width: 1200px;
717
+ z-index: 2;
718
+ animation: fadeIn 1.5s ease 1s backwards;
719
+ }
720
+
721
+ .code-header {
722
+ text-align: center;
723
+ margin-bottom: 30px;
724
+ }
725
+
726
+ .code-header h3 {
727
+ font-size: 28px;
728
+ color: #ffa500;
729
+ margin-bottom: 10px;
730
+ text-shadow: 0 0 20px rgba(255, 165, 0, 0.5);
731
+ }
732
+
733
+ .code-header p {
734
+ color: #b0b8c8;
735
+ font-size: 16px;
736
+ }
737
+
738
+ .code-display {
739
+ display: grid;
740
+ grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));
741
+ gap: 25px;
742
+ padding: 0 20px;
743
+ }
744
+
745
+ .code-block {
746
+ background: rgba(20, 25, 35, 0.9);
747
+ border: 1px solid rgba(76, 209, 55, 0.3);
748
+ border-radius: 15px;
749
+ padding: 25px;
750
+ position: relative;
751
+ overflow: hidden;
752
+ backdrop-filter: blur(10px);
753
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
754
+ transition: all 0.3s ease;
755
+ }
756
+
757
+ .code-block:hover {
758
+ transform: translateY(-5px);
759
+ box-shadow: 0 12px 35px rgba(76, 209, 55, 0.3);
760
+ border-color: #4cd137;
761
+ }
762
+
763
+ .code-block::before {
764
+ content: 'style.css';
765
+ position: absolute;
766
+ top: 0;
767
+ left: 0;
768
+ background: rgba(76, 209, 55, 0.2);
769
+ padding: 6px 15px;
770
+ font-size: 12px;
771
+ color: #4cd137;
772
+ border-radius: 0 0 10px 0;
773
+ font-family: 'Courier New', monospace;
774
+ }
775
+
776
+ .code-block pre {
777
+ margin-top: 30px;
778
+ color: #a8b2d1;
779
+ font-family: 'Courier New', monospace;
780
+ font-size: 13px;
781
+ line-height: 1.6;
782
+ overflow-x: auto;
783
+ }
784
+
785
+ .code-block pre code {
786
+ display: block;
787
+ }
788
+
789
+ .keyword { color: #c792ea; }
790
+ .property { color: #82aaff; }
791
+ .value { color: #c3e88d; }
792
+ .important { color: #ff6b6b; }
793
+ .comment { color: #676e95; font-style: italic; }
794
+
795
+ /* Responsive */
796
+ @media (max-width: 1200px) {
797
+ .container {
798
+ gap: 50px;
799
+ padding: 50px 40px;
800
+ }
801
+
802
+ .code-display {
803
+ grid-template-columns: 1fr;
804
+ }
805
+ }
806
+
807
+ @media (max-width: 1024px) {
808
+ .container {
809
+ flex-direction: column;
810
+ gap: 40px;
811
+ padding: 40px 30px;
812
+ }
813
+
814
+ .title {
815
+ font-size: 42px;
816
+ }
817
+
818
+ .lamp-section {
819
+ width: 250px;
820
+ height: 350px;
821
+ }
822
+ }
823
+
824
+ @media (max-width: 600px) {
825
+ .title {
826
+ font-size: 32px;
827
+ margin-bottom: 30px;
828
+ }
829
+
830
+ .container {
831
+ padding: 30px 20px;
832
+ margin: 20px;
833
+ }
834
+
835
+ .login-box {
836
+ width: 100%;
837
+ padding: 35px 25px;
838
+ }
839
+
840
+ .lamp {
841
+ transform: scale(0.75);
842
+ }
843
+
844
+ .social-login {
845
+ flex-direction: column;
846
+ }
847
+
848
+ .code-display {
849
+ padding: 0;
850
+ }
851
+
852
+ .code-block {
853
+ padding: 20px 15px;
854
+ }
855
+
856
+ .code-block pre {
857
+ font-size: 11px;
858
+ }
859
+ }
860
+ </style>
861
+ </head>
862
+ <body>
863
+ <!-- Animated particles -->
864
+ <div class="particles" id="particles"></div>
865
+
866
+ <!-- Title -->
867
+ <h1 class="title">✨ Cute Lamp Login ✨</h1>
868
+
869
+ <!-- Main Container -->
870
+ <div class="container">
871
+ <!-- Enhanced Lamp Section -->
872
+ <div class="lamp-section">
873
+ <div class="lamp-glow"></div>
874
+ <div class="lamp-container" id="lampContainer">
875
+ <div class="lamp" id="lamp">
876
+ <div class="cord">
877
+ <div class="cord-plug"></div>
878
+ </div>
879
+ <div class="lamp-shade" id="lampShade"></div>
880
+ <div class="lamp-face" id="lampFace">
881
+ <div class="lamp-eye left" id="leftEye"></div>
882
+ <div class="lamp-eye right" id="rightEye"></div>
883
+ <div class="lamp-mouth" id="lampMouth"></div>
884
+ <div class="lamp-tongue" id="lampTongue"></div>
885
+ <div class="lamp-blush left"></div>
886
+ <div class="lamp-blush right"></div>
887
+ </div>
888
+ <div class="lamp-stand"></div>
889
+ <div class="lamp-base"></div>
890
+ </div>
891
+ </div>
892
+ </div>
893
+
894
+ <!-- Enhanced Login Form -->
895
+ <div class="login-box">
896
+ <h2>Welcome Back</h2>
897
+
898
+ <!-- Social Login -->
899
+ <div class="social-login">
900
+ <button class="social-btn" onclick="socialLogin('Google')">
901
+ <span>πŸ”</span>
902
+ <span>Google</span>
903
+ </button>
904
+ <button class="social-btn" onclick="socialLogin('GitHub')">
905
+ <span>πŸ’»</span>
906
+ <span>GitHub</span>
907
+ </button>
908
+ </div>
909
+
910
+ <div class="divider">OR</div>
911
+
912
+ <form id="loginForm" onsubmit="handleLogin(event)">
913
+ <div class="input-group">
914
+ <label for="username">Username</label>
915
+ <div class="input-wrapper">
916
+ <input type="text" id="username" placeholder="Enter your username" required>
917
+ </div>
918
+ </div>
919
+ <div class="input-group">
920
+ <label for="password">Password</label>
921
+ <div class="input-wrapper">
922
+ <input type="password" id="password" placeholder="Enter your password" required>
923
+ <span class="toggle-password" id="togglePassword" onclick="togglePassword()">πŸ‘οΈ</span>
924
+ </div>
925
+ </div>
926
+ <button type="submit" class="login-btn" id="loginBtn">Login</button>
927
+ </form>
928
+ <div class="forgot-password">
929
+ <a href="#" onclick="forgotPassword(); return false;">Forgot Password?</a>
930
+ </div>
931
+ </div>
932
+ </div>
933
+
934
+ <!-- Code Display Section -->
935
+ <div class="code-section">
936
+ <div class="code-header">
937
+ <h3>πŸ’» Comment "cute" for code</h3>
938
+ <p>Here's a peek at the magic behind this adorable login page</p>
939
+ </div>
940
+ <div class="code-display">
941
+ <div class="code-block">
942
+ <pre><code><span class="keyword">.lamp</span> {
943
+ <span class="property">display</span>: <span class="value">flex</span>;
944
+ <span class="property">height</span>: <span class="value">320px</span>;
945
+ <span class="property">overflow</span>: <span class="value">visible</span> <span class="important">!important</span>;
946
+ }
947
+
948
+ <span class="keyword">.cord</span> {
949
+ <span class="property">stroke</span>: <span class="value">var(--cord)</span>;
950
+ }
951
+
952
+ <span class="keyword">.cord--nip</span> {
953
+ <span class="property">display</span>: <span class="value">none</span>;
954
+ }
955
+
956
+ <span class="keyword">.lamp__tongue</span> {
957
+ <span class="property">fill</span>: <span class="value">var(--tongue)</span>;
958
+ }</code></pre>
959
+ </div>
960
+
961
+ <div class="code-block">
962
+ <pre><code><span class="keyword">.login-btn:active</span> {
963
+ <span class="property">transform</span>: <span class="value">translateY(0px)</span>;
964
+ }
965
+
966
+ <span class="keyword">.form-footer</span> {
967
+ <span class="property">margin-top</span>: <span class="value">1.5rem</span>;
968
+ <span class="property">text-align</span>: <span class="value">center</span>;
969
+ }
970
+
971
+ <span class="keyword">.forgot-link</span> {
972
+ <span class="property">color</span>: <span class="value">#888</span>;
973
+ <span class="property">font-size</span>: <span class="value">0.9rem</span>;
974
+ <span class="property">text-decoration</span>: <span class="value">none</span>;
975
+ <span class="property">transition</span>: <span class="value">all 0.3s ease</span>;
976
+ }</code></pre>
977
+ </div>
978
+ </div>
979
+ </div>
980
+
981
+ <!-- Toast Notification -->
982
+ <div class="toast" id="toast">
983
+ <span class="toast-icon" id="toastIcon">✨</span>
984
+ <span id="toastMessage">Welcome!</span>
985
+ </div>
986
+
987
+ <script>
988
+ // Enhanced Particle System
989
+ const particlesContainer = document.getElementById('particles');
990
+
991
+ function createParticles() {
992
+ for (let i = 0; i < 40; i++) {
993
+ const particle = document.createElement('div');
994
+ particle.className = 'particle';
995
+ particle.style.left = Math.random() * 100 + '%';
996
+ particle.style.setProperty('--tx', (Math.random() - 0.5) * 200 + 'px');
997
+ particle.style.animationDelay = Math.random() * 15 + 's';
998
+ particle.style.animationDuration = (10 + Math.random() * 10) + 's';
999
+ particlesContainer.appendChild(particle);
1000
+ }
1001
+
1002
+ // Add sparkles
1003
+ for (let i = 0; i < 20; i++) {
1004
+ const sparkle = document.createElement('div');
1005
+ sparkle.className = 'sparkle';
1006
+ sparkle.style.left = Math.random() * 100 + '%';
1007
+ sparkle.style.top = Math.random() * 100 + '%';
1008
+ sparkle.style.animationDelay = Math.random() * 3 + 's';
1009
+ particlesContainer.appendChild(sparkle);
1010
+ }
1011
+ }
1012
+
1013
+ createParticles();
1014
+
1015
+ // Lamp Elements
1016
+ const lamp = document.getElementById('lamp');
1017
+ const lampShade = document.getElementById('lampShade');
1018
+ const lampFace = document.getElementById('lampFace');
1019
+ const lampMouth = document.getElementById('lampMouth');
1020
+ const lampTongue = document.getElementById('lampTongue');
1021
+ const leftEye = document.getElementById('leftEye');
1022
+ const rightEye = document.getElementById('rightEye');
1023
+ const lampContainer = document.getElementById('lampContainer');
1024
+
1025
+ // Form Elements
1026
+ const loginForm = document.getElementById('loginForm');
1027
+ const usernameInput = document.getElementById('username');
1028
+ const passwordInput = document.getElementById('password');
1029
+ const loginBtn = document.getElementById('loginBtn');
1030
+ const toast = document.getElementById('toast');
1031
+ const toastIcon = document.getElementById('toastIcon');
1032
+ const toastMessage = document.getElementById('toastMessage');
1033
+
1034
+ // Lamp Expressions
1035
+ function setLampExpression(expression) {
1036
+ lampShade.classList.remove('happy', 'sad');
1037
+ lampMouth.classList.remove('sad');
1038
+ lampTongue.classList.remove('show');
1039
+
1040
+ switch(expression) {
1041
+ case 'happy':
1042
+ lampShade.classList.add('happy');
1043
+ lampTongue.classList.add('show');
1044
+ leftEye.style.background = '#4cd137';
1045
+ rightEye.style.background = '#4cd137';
1046
+ break;
1047
+ case 'sad':
1048
+ lampShade.classList.add('sad');
1049
+ lampMouth.classList.add('sad');
1050
+ leftEye.style.background = '#3498db';
1051
+ rightEye.style.background = '#3498db';
1052
+ break;
1053
+ case 'neutral':
1054
+ leftEye.style.background = '#2d3436';
1055
+ rightEye.style.background = '#2d3436';
1056
+ break;
1057
+ }
1058
+ }
1059
+
1060
+ // Eye following cursor
1061
+ let isFollowingCursor = true;
1062
+
1063
+ document.addEventListener('mousemove', (e) => {
1064
+ if (!isFollowingCursor) return;
1065
+
1066
+ const lampRect = lamp.getBoundingClientRect();
1067
+ const lampCenterX = lampRect.left + lampRect.width / 2;
1068
+ const lampCenterY = lampRect.top + lampRect.height / 2;
1069
+
1070
+ const angle = Math.atan2(e.clientY - lampCenterY, e.clientX - lampCenterX);
1071
+ const distance = Math.min(3, Math.hypot(e.clientX - lampCenterX, e.clientY - lampCenterY) / 100);
1072
+
1073
+ const moveX = Math.cos(angle) * distance;
1074
+ const moveY = Math.sin(angle) * distance;
1075
+
1076
+ leftEye.style.transform = `translate(${moveX}px, ${moveY}px)`;
1077
+ rightEye.style.transform = `translate(${moveX}px, ${moveY}px)`;
1078
+ });
1079
+
1080
+ // Input Focus Effects
1081
+ const inputs = [usernameInput, passwordInput];
1082
+
1083
+ inputs.forEach(input => {
1084
+ input.addEventListener('focus', () => {
1085
+ setLampExpression('happy');
1086
+ isFollowingCursor = false;
1087
+ });
1088
+
1089
+ input.addEventListener('blur', () => {
1090
+ if (!usernameInput.value && !passwordInput.value) {
1091
+ setLampExpression('neutral');
1092
+ isFollowingCursor = true;
1093
+ }
1094
+ });
1095
+
1096
+ input.addEventListener('input', () => {
1097
+ if (input.classList.contains('error')) {
1098
+ input.classList.remove('error');
1099
+ setLampExpression('happy');
1100
+ }
1101
+ });
1102
+ });
1103
+
1104
+ // Toast Notification Function
1105
+ function showToast(message, type = 'success') {
1106
+ toast.className = 'toast';
1107
+ if (type === 'error') {
1108
+ toast.classList.add('error');
1109
+ toastIcon.textContent = '❌';
1110
+ } else {
1111
+ toastIcon.textContent = '✨';
1112
+ }
1113
+
1114
+ toastMessage.textContent = message;
1115
+
1116
+ setTimeout(() => {
1117
+ toast.classList.add('show');
1118
+ }, 100);
1119
+
1120
+ setTimeout(() => {
1121
+ toast.classList.remove('show');
1122
+ }, 3000);
1123
+ }
1124
+
1125
+ // Password Toggle
1126
+ function togglePassword() {
1127
+ const type = passwordInput.type === 'password' ? 'text' : 'password';
1128
+ passwordInput.type = type;
1129
+ document.getElementById('togglePassword').textContent = type === 'password' ? 'πŸ‘οΈ' : 'πŸ™ˆ';
1130
+ }
1131
+
1132
+ // Form Validation
1133
+ function validateForm() {
1134
+ let isValid = true;
1135
+
1136
+ if (usernameInput.value.trim().length < 3) {
1137
+ usernameInput.classList.add('error');
1138
+ isValid = false;
1139
+ }
1140
+
1141
+ if (passwordInput.value.trim().length < 6) {
1142
+ passwordInput.classList.add('error');
1143
+ isValid = false;
1144
+ }
1145
+
1146
+ if (!isValid) {
1147
+ setLampExpression('sad');
1148
+ showToast('Please check your credentials! 😒', 'error');
1149
+ }
1150
+
1151
+ return isValid;
1152
+ }
1153
+
1154
+ // Handle Login
1155
+ function handleLogin(e) {
1156
+ e.preventDefault();
1157
+
1158
+ if (!validateForm()) {
1159
+ return;
1160
+ }
1161
+
1162
+ // Show loading state
1163
+ loginBtn.classList.add('loading');
1164
+ loginBtn.textContent = '';
1165
+ setLampExpression('happy');
1166
+
1167
+ // Simulate API call
1168
+ setTimeout(() => {
1169
+ loginBtn.classList.remove('loading');
1170
+ loginBtn.textContent = 'Login';
1171
+
1172
+ const username = usernameInput.value;
1173
+ showToast(`Welcome back, ${username}! πŸŽ‰`, 'success');
1174
+
1175
+ // Reset form after success
1176
+ setTimeout(() => {
1177
+ loginForm.reset();
1178
+ setLampExpression('neutral');
1179
+ isFollowingCursor = true;
1180
+ }, 2000);
1181
+ }, 2000);
1182
+ }
1183
+
1184
+ // Social Login
1185
+ function socialLogin(platform) {
1186
+ setLampExpression('happy');
1187
+ showToast(`Logging in with ${platform}... πŸš€`, 'success');
1188
+
1189
+ setTimeout(() => {
1190
+ showToast(`${platform} login successful! 🎊`, 'success');
1191
+ }, 1500);
1192
+ }
1193
+
1194
+ // Forgot Password
1195
+ function forgotPassword() {
1196
+ setLampExpression('sad');
1197
+ showToast('Password reset link sent to your email! πŸ“§', 'success');
1198
+
1199
+ setTimeout(() => {
1200
+ setLampExpression('neutral');
1201
+ }, 2000);
1202
+ }
1203
+
1204
+ // Lamp Click Interaction
1205
+ lampContainer.addEventListener('click', () => {
1206
+ setLampExpression('happy');
1207
+ lampContainer.style.transform = 'scale(1.1)';
1208
+
1209
+ setTimeout(() => {
1210
+ lampContainer.style.transform = 'scale(1)';
1211
+ }, 300);
1212
+
1213
+ setTimeout(() => {
1214
+ if (!usernameInput.value && !passwordInput.value) {
1215
+ setLampExpression('neutral');
1216
+ }
1217
+ }, 2000);
1218
+ });
1219
+
1220
+ // Welcome Animation
1221
+ window.addEventListener('load', () => {
1222
+ setTimeout(() => {
1223
+ showToast('Hello! Ready to login? πŸ‘‹', 'success');
1224
+ }, 1000);
1225
+ });
1226
+
1227
+ // Easter Egg: Konami Code
1228
+ let konamiCode = [];
1229
+ const konamiSequence = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
1230
+
1231
+ document.addEventListener('keydown', (e) => {
1232
+ konamiCode.push(e.key);
1233
+ konamiCode = konamiCode.slice(-10);
1234
+
1235
+ if (konamiCode.join(',') === konamiSequence.join(',')) {
1236
+ lampShade.style.background = 'linear-gradient(180deg, #ff6b9d, #c44569)';
1237
+ showToast('πŸŽ‰ Rainbow Lamp Mode Activated! 🌈', 'success');
1238
+
1239
+ let hue = 0;
1240
+ const rainbowInterval = setInterval(() => {
1241
+ lampShade.style.background = `hsl(${hue}, 70%, 60%)`;
1242
+ hue = (hue + 5) % 360;
1243
+ }, 100);
1244
+
1245
+ setTimeout(() => {
1246
+ clearInterval(rainbowInterval);
1247
+ lampShade.style.background = '';
1248
+ showToast('Back to normal! ✨', 'success');
1249
+ }, 5000);
1250
+ }
1251
+ });
1252
+
1253
+ // Keyboard shortcuts
1254
+ document.addEventListener('keydown', (e) => {
1255
+ // Ctrl/Cmd + Enter to submit
1256
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
1257
+ if (usernameInput.value || passwordInput.value) {
1258
+ loginForm.dispatchEvent(new Event('submit'));
1259
+ }
1260
+ }
1261
+ });
1262
+
1263
+ // Auto-focus username on load
1264
+ window.addEventListener('load', () => {
1265
+ setTimeout(() => {
1266
+ usernameInput.focus();
1267
+ }, 500);
1268
+ });
1269
+
1270
+ // Add accessibility: ESC to clear form
1271
+ document.addEventListener('keydown', (e) => {
1272
+ if (e.key === 'Escape') {
1273
+ loginForm.reset();
1274
+ setLampExpression('neutral');
1275
+ isFollowingCursor = true;
1276
+ showToast('Form cleared! 🧹', 'success');
1277
+ }
1278
+ });
1279
+ </script>
1280
+ </body>
1281
+ </html>