1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <!-- Google tag (gtag.js) -->
vendor: 68 bytes, lines 4-5
4 5 <script async src="https://www.googletagmanager.com/gtag/js?id=
5G-0CQSNRPTVZ
vendor: 16 bytes, lines 5-6
5"></script> 6
6<script> 7
vendor: 146 bytes, lines 7-11
7window.dataLayer = window.dataLayer || []; 8 function gtag(){dataLayer.push(arguments);} 9 gtag('js', new Date()); 10 11 gtag('config', '
11G-0CQSNRPTVZ
vendor: 8 bytes, lines 11-12
11'); 12
12</script>
12 13 <meta charset="UTF-8"> 14 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 15 <title>Sign Up - KEZEN</title> 16 17 <!-- Favicon --> 18 <link rel="icon" type="image/x-icon" href="/favicon.ico"> 19 <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> 20 <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> 21 <link rel="icon" type="image/png" sizes="64x64" href="/favicon-64x64.png"> 22 <link rel="apple-touch-icon" sizes="128x128" href="/favicon-128x128.png"> 23 <link rel="apple-touch-icon" sizes="256x256" href="/favicon-256x256.png"> 24 25 <!-- Tailwind CSS --> 26
26<script src="https://cdn.tailwindcss.com"></script>
26 27 28 <!-- Alpine.js --> 29
29<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
29 30 31 <!-- Font Awesome --> 32 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> 33 34 <style> 35 .emerald-gradient { 36 background: linear-gradient(to right, #10b981, #059669); 37 } 38 39 .emerald-gradient:hover { 40 background: linear-gradient(to right, #059669, #047857); 41 } 42 </style> 43 44 <!-- Meta Pixel Code --> 45
45<script>
vendor: 435 bytes, lines 45-54
45 46 !function(f,b,e,v,n,t,s) 47 {if(f.fbq)return;n=f.fbq=function(){n.callMethod? 48 n.callMethod.apply(n,arguments):n.queue.push(arguments)}; 49 if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0'; 50 n.queue=[];t=b.createElement(e);t.async=!0; 51 t.src=v;s=b.getElementsByTagName(e)[0]; 52 s.parentNode.insertBefore(t,s)}(window, document,'script', 53 'https://connect.facebook.net/en_US/fbevents.js'); 54 fbq('init', '
541763902164190191
vendor: 38 bytes, lines 54-56
54'); 55 fbq('track', 'PageView'); 56
56</script>
56 57 <noscript><img height="1" width="1" style="display:none" 58 src="https://www.facebook.com/tr?id=1763902164190191&ev=PageView&noscript=1" 59 /></noscript> 60 <!-- End Meta Pixel Code --> 61</head> 62<body class="bg-gray-50" x-data="signupApp()"> 63 <div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8"> 64 <div class="max-w-md w-full space-y-8"> 65 <!-- Logo and Header --> 66 <div class="text-center"> 67 <a href="/" class="inline-flex items-center space-x-2 mb-6"> 68 <i class="fas fa-paint-brush text-3xl text-emerald-600"></i> 69 <span class="text-2xl font-bold text-gray-900">KEZEN</span> 70 </a> 71 72 <h2 class="text-3xl font-bold text-gray-900" x-text="inviteToken ? 'Accept Invitation' : 'Create Account'"></h2> 73 <p class="mt-2 text-sm text-gray-600"> 74 Already have an account? 75 <a href="/" class="font-medium text-emerald-600 hover:text-emerald-500">Sign in</a> 76 </p> 77 78 <!-- Invite Info --> 79 <div x-show="inviteToken" x-cloak class="mt-4 p-4 bg-emerald-50 rounded-lg"> 80 <i class="fas fa-gift text-emerald-600 mb-2"></i> 81 <p class="text-sm text-emerald-800">You've been invited to join KEZEN!</p> 82 </div> 83 </div> 84 85 <!-- Registration Form --> 86 <form class="mt-8 space-y-6" @submit.prevent="register()"> 87 <!-- Error Display --> 88 <div x-show="error" x-cloak class="bg-red-50 border-l-4 border-red-500 p-4 rounded"> 89 <div class="flex"> 90 <i class="fas fa-exclamation-circle text-red-500 mt-0.5 mr-3"></i> 91 <p class="text-red-700" x-text="error"></p> 92 </div> 93 </div> 94 95 <!-- Success Display --> 96 <div x-show="success" x-cloak class="bg-green-50 border-l-4 border-green-500 p-4 rounded"> 97 <div class="flex"> 98 <i class="fas fa-check-circle text-green-500 mt-0.5 mr-3"></i> 99 <p class="text-green-700" x-text="success"></p> 100 </div> 101 </div> 102 103 <div class="space-y-4"> 104 <!-- Username --> 105 <div> 106 <label for="username" class="block text-sm font-medium text-gray-700">Username</label> 107 <input 108 id="username" 109 name="username" 110 type="text" 111 x-model="username" 112 required 113 class="mt-1 block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500" 114 placeholder="johndoe"> 115 </div> 116 117 <!-- Email --> 118 <div> 119 <label for="email" class="block text-sm font-medium text-gray-700">Email Address</label> 120 <input 121 id="email" 122 name="email" 123 type="email" 124 x-model="email" 125 required 126 class="mt-1 block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500" 127 placeholder="[email protected]"> 128 </div> 129 130 <!-- Password --> 131 <div> 132 <label for="password" class="block text-sm font-medium text-gray-700">Password</label> 133 <input 134 id="password" 135 name="password" 136 type="password" 137 x-model="password" 138 required 139 minlength="8" 140 class="mt-1 block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500" 141 placeholder="At least 8 characters"> 142 <p class="mt-1 text-xs text-gray-500">Must be at least 8 characters</p> 143 </div> 144 145 <!-- Confirm Password --> 146 <div> 147 <label for="confirmPassword" class="block text-sm font-medium text-gray-700">Confirm Password</label> 148 <input 149 id="confirmPassword" 150 name="confirmPassword" 151 type="password" 152 x-model="confirmPassword" 153 required 154 class="mt-1 block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500" 155 placeholder="Confirm your password"> 156 </div> 157 </div> 158 159 <!-- Submit Button --> 160 <button 161 type="submit" 162 :disabled="loading" 163 class="w-full flex justify-center py-3 px-4 border border-transparent roun
163ded-lg shadow-sm text-sm font-medium text-white emerald-gradient hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-500 disabled:opacity-50 disabled:cursor-not-allowed" 164 > 165 <span x-show="!loading">Create Account</span> 166 <span x-show="loading" class="flex items-center"> 167 <div class="animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"></div> 168 Creating Account... 169 </span> 170 </button> 171 172 <!-- Terms --> 173 <p class="text-center text-xs text-gray-500"> 174 By signing up, you agree to our 175 <a href="/terms-of-service" class="text-emerald-600 hover:text-emerald-500">Terms of Service</a> and 176 <a href="/privacy-policy" class="text-emerald-600 hover:text-emerald-500">Privacy Policy</a> 177 </p> 178 </form> 179 </div> 180 </div> 181 182
182<script> 183 function signupApp() { 184 return { 185 username: '', 186 email: '', 187 password: '', 188 confirmPassword: '', 189 inviteToken: null, 190 loading: false, 191 error: '', 192 success: '', 193 194 init() { 195 // Check for invite token in URL 196 const urlParams = new URLSearchParams(window.location.search); 197 this.inviteToken = urlParams.get('invite'); 198 199 if (this.inviteToken) { 200 // Pre-fill email if provided 201 const email = urlParams.get('email'); 202 if (email) { 203 this.email = decodeURIComponent(email); 204 } 205 } 206 }, 207 208 async register() { 209 // Reset messages 210 this.error = ''; 211 this.success = ''; 212 213 // Validate passwords match 214 if (this.password !== this.confirmPassword) { 215 this.error = 'Passwords do not match'; 216 return; 217 } 218 219 // Validate password length 220 if (this.password.length < 8) { 221 this.error = 'Password must be at least 8 characters'; 222 return; 223 } 224 225 this.loading = true; 226 227 try { 228 const response = await fetch('/api/auth/register', { 229 method: 'POST', 230 headers: { 231 'Content-Type': 'application/json' 232 }, 233 body: JSON.stringify({ 234 username: this.username, 235 email: this.email, 236 password: this.password, 237 inviteToken: this.inviteToken 238 }) 239 }); 240 241 const data = await response.json(); 242 243 if (response.ok) { 244 // Track Facebook CompleteRegistration conversion 245 if (typeof fbq !== 'undefined') { 246 fbq('track', 'CompleteRegistration', { 247 content_name: 'Account Registration', 248 value: 0.00, 249 currency: 'USD', 250 status: true 251 }); 252 } 253 254 // Check if email verification is required 255 if (data.requiresVerification) { 256 // Email verification required 257 this.success = data.message || 'Account created! Please check your email to verify your account before logging in.'; 258 259 // If email failed to send and we have a verification link, show it 260 if (data.verifyLink) { 261 this.error = ''; 262 this.success = ` 263 <div style="text-align: left;"> 264 <p style="margin-bottom: 10px;">Account created! We couldn't send the verification email automatically.</p> 265 <p style="margin-bottom: 15px;">Please copy this link and open it in your browser to verify your email:</p> 266 <div style="background: #f5f5f5; padding: 10px; border-radius: 5px; word-break: break-all; margin-bottom: 15px;"> 267 <code style="font-size: 12px;">${data.verifyLink}</code> 268 </div> 269 <button onclick="navigator.clipboard.writeText('${data.verifyLink}').then(() => alert('Link copied to clipboard!'))" 270 style="background: #4CAF50; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer;"> 271 Copy Link 272 </button> 273 </div> 274 `; 275 } 276 277 // Don't redirect - let user see the message 278 } else { 279 // No verification required (shouldn't happen normally) 280 this.success = 'Account created successfully! Redirecting to login...'; 281 282 // Store token if provided 283 if (data.token) { 284 localStorage.setItem('authToken', data.token); 285 if (data.refreshToken) { 286 localStorage.setItem('refreshToken', data.refreshToken); 287 } 288 } 289 290 // Redirect after a short delay 291 setTimeout(() => { 292 window.location.href = '/'; 293 }, 2000); 294 } 295 } else { 296 this.error = data.error || data.message || 'Registration failed. Please try again.'; 297 } 298 } catch (err) { 299 console.error('Registration error:', err); 300 this.error = 'Network error. Please check your connection and try again.'; 301 } finally { 302 this.loading = false;
303 } 304 } 305 } 306 } 307 </script>
307 308</body> 309</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.