Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Authentication - Voice Lab</title> | |
| <link rel="stylesheet" href="css/style.css"> | |
| <style> | |
| body { margin: 0; overflow: hidden; } | |
| .split-layout { | |
| display: flex; | |
| min-height: 100vh; | |
| width: 100vw; | |
| } | |
| .visual-panel { | |
| flex: 1; | |
| position: relative; | |
| display: none; | |
| overflow: hidden; | |
| background: linear-gradient(135deg, #07070f, #1a103c); | |
| } | |
| @media (min-width: 768px) { | |
| .visual-panel { display: flex; align-items: center; justify-content: center; } | |
| } | |
| .mesh-gradient { | |
| position: absolute; | |
| top: -50%; left: -50%; | |
| width: 200%; height: 200%; | |
| background: radial-gradient(circle at 50% 50%, rgba(124, 58, 237, 0.4) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 20%, rgba(37, 99, 235, 0.4) 0%, transparent 50%), | |
| radial-gradient(circle at 20% 80%, rgba(244, 63, 94, 0.3) 0%, transparent 50%); | |
| animation: rotateMesh 20s infinite linear; | |
| z-index: 1; | |
| } | |
| @keyframes rotateMesh { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .visual-content { | |
| position: relative; | |
| z-index: 2; | |
| text-align: center; | |
| padding: 3rem; | |
| background: rgba(0,0,0,0.3); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255,255,255,0.05); | |
| border-radius: 30px; | |
| max-width: 450px; | |
| } | |
| .form-panel { | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 2rem; | |
| background: var(--bg-dark); | |
| position: relative; | |
| z-index: 10; | |
| } | |
| .auth-card { | |
| width: 100%; | |
| max-width: 420px; | |
| } | |
| .auth-form { | |
| display: none; | |
| flex-direction: column; | |
| gap: 1.2rem; | |
| } | |
| .auth-form.active { | |
| display: flex; | |
| animation: slideInRight 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) forwards; | |
| } | |
| @keyframes slideInRight { | |
| from { opacity: 0; transform: translateX(30px); } | |
| to { opacity: 1; transform: translateX(0); } | |
| } | |
| .auth-header { margin-bottom: 2rem; } | |
| .auth-header h2 { font-size: 2.2rem; margin-bottom: 0.5rem; letter-spacing: -0.5px; } | |
| .form-group { display: flex; flex-direction: column; gap: 0.5rem; } | |
| .form-group label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; } | |
| .form-group input { | |
| padding: 1rem 1.2rem; | |
| background: rgba(255,255,255,0.03); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| color: white; | |
| font-family: inherit; | |
| font-size: 1rem; | |
| transition: all 0.3s; | |
| } | |
| .form-group input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| background: rgba(124, 58, 237, 0.05); | |
| box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.1); | |
| } | |
| .auth-switch { | |
| text-align: center; | |
| margin-top: 1.5rem; | |
| font-size: 0.95rem; | |
| color: var(--text-muted); | |
| } | |
| .auth-switch a { | |
| color: var(--primary-hover); | |
| text-decoration: none; | |
| font-weight: 600; | |
| transition: color 0.3s; | |
| } | |
| .auth-switch a:hover { color: var(--text-main); } | |
| .back-home { | |
| position: absolute; | |
| top: 2rem; | |
| right: 2rem; | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| transition: color 0.3s; | |
| } | |
| .back-home:hover { color: white; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="split-layout"> | |
| <!-- Left Visual Panel --> | |
| <div class="visual-panel"> | |
| <div class="mesh-gradient"></div> | |
| <div class="visual-content"> | |
| <div class="spinner-orb" style="width: 100px; height: 100px; margin-bottom: 2rem;"></div> | |
| <h1 style="font-size: 2.5rem; margin-bottom: 1rem;">Voice Lab <span class="text-gradient">AI</span></h1> | |
| <p style="color: var(--text-muted); line-height: 1.6; font-size: 1.1rem;"> | |
| Step into the future of synthetic speech. Generate human-like voices and clone audio with zero-shot precision. | |
| </p> | |
| </div> | |
| <canvas id="particles-canvas" style="position: absolute; top:0; left:0; width:100%; height:100%; z-index:0;"></canvas> | |
| </div> | |
| <!-- Right Form Panel --> | |
| <div class="form-panel"> | |
| <a href="index.html" class="back-home"> | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg> | |
| Back to Home | |
| </a> | |
| <div class="auth-card"> | |
| <!-- Login Form --> | |
| <form id="login-form" class="auth-form active"> | |
| <div class="auth-header"> | |
| <h2>Welcome <span class="text-gradient">Back</span></h2> | |
| <p style="color: var(--text-dim)">Enter your credentials to access your dashboard.</p> | |
| </div> | |
| <div class="form-group"> | |
| <label>Username</label> | |
| <input type="text" required placeholder="Enter your username"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Password</label> | |
| <input type="password" required placeholder="Enter your password"> | |
| </div> | |
| <button type="submit" class="btn btn-glow" style="margin-top: 1rem; width: 100%; padding: 1rem; font-size: 1.05rem;"> | |
| Sign In | |
| <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> | |
| </button> | |
| <div class="auth-switch"> | |
| Don't have an account? <a href="#" id="switch-to-signup">Create one</a> | |
| </div> | |
| </form> | |
| <!-- Signup Form --> | |
| <form id="signup-form" class="auth-form"> | |
| <div class="auth-header"> | |
| <h2>Create <span class="text-gradient">Account</span></h2> | |
| <p style="color: var(--text-dim)">Join the next generation of voice synthesis.</p> | |
| </div> | |
| <div class="form-group"> | |
| <label>Username</label> | |
| <input type="text" required placeholder="Choose a username"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Password</label> | |
| <input type="password" required placeholder="Create a password"> | |
| </div> | |
| <button type="submit" class="btn btn-glow" style="margin-top: 1rem; width: 100%; padding: 1rem; font-size: 1.05rem;"> | |
| Register Now | |
| </button> | |
| <div class="auth-switch"> | |
| Already have an account? <a href="#" id="switch-to-login">Sign In instead</a> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- We load particles script specifically for the left panel --> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const canvas = document.getElementById('particles-canvas'); | |
| if(!canvas) return; | |
| const ctx = canvas.getContext('2d'); | |
| let width = canvas.width = canvas.offsetWidth; | |
| let height = canvas.height = canvas.offsetHeight; | |
| let particles = []; | |
| window.addEventListener('resize', () => { | |
| width = canvas.width = canvas.offsetWidth; | |
| height = canvas.height = canvas.offsetHeight; | |
| }); | |
| class Particle { | |
| constructor() { | |
| this.x = Math.random() * width; this.y = Math.random() * height; | |
| this.vx = (Math.random() - 0.5) * 0.8; this.vy = (Math.random() - 0.5) * 0.8; | |
| this.size = Math.random() * 2 + 0.5; | |
| } | |
| update() { | |
| this.x += this.vx; this.y += this.vy; | |
| if(this.x < 0 || this.x > width) this.vx *= -1; | |
| if(this.y < 0 || this.y > height) this.vy *= -1; | |
| } | |
| draw() { | |
| ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); | |
| ctx.fillStyle = 'rgba(255, 255, 255, 0.4)'; ctx.fill(); | |
| } | |
| } | |
| for(let i=0; i<40; i++) particles.push(new Particle()); | |
| function animate() { | |
| ctx.clearRect(0, 0, width, height); | |
| particles.forEach(p => { p.update(); p.draw(); }); | |
| requestAnimationFrame(animate); | |
| } | |
| animate(); | |
| }); | |
| </script> | |
| <script src="js/toast.js"></script> | |
| <script src="js/auth.js"></script> | |
| </body> | |
| </html> | |