WEBLEB
Inicio
Editora de código
Iniciar sesión
Pro
Español
English
Français
Español
acceso
12
smhhkh1389
Abrir en el editor
Publica tu código
0
Recomendado
27 August 2024
Acceso
1 June 2025
ACCESO
HTML
Copy
NEXUS — Secure Access
/head>
Welcome Back
Sign in to your Nexus account to continue
Email address
Password
Remember me
Forgot password?
Sign In
or
Welcome back!
Redirecting to your dashboard...
CSS
Copy
<style> /* ═══════════════════════════════════════════════ RESET & BASE ═══════════════════════════════════════════════ */ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } :root { --cyan: #00e5ff; --cyan-dim: #00e5ff44; --violet: #8b5cf6; --violet-dim: #8b5cf644; --turquoise: #2dd4bf; --blue: #3b82f6; --blue-glow: #3b82f640; --white: #f0f4ff; --white-dim: #f0f4ff22; --error: #f43f5e; --error-dim: #f43f5e33; --success: #10b981; --success-dim: #10b98133; --glass-bg: rgba(8, 12, 28, 0.55); --glass-border: rgba(255, 255, 255, 0.07); --glass-highlight: rgba(255, 255, 255, 0.04); --input-bg: rgba(255, 255, 255, 0.04); --input-border: rgba(255, 255, 255, 0.08); --input-focus: rgba(0, 229, 255, 0.25); --radius: 24px; --radius-sm: 14px; --radius-xs: 10px; --transition: 0.35s cubic-bezier(0.4, 0, 0.2, 1); --transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1); --transition-spring: 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); } html, body { width: 100%; height: 100%; overflow: hidden; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: #050510; color: var(--white); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* ═══════════════════════════════════════════════ ANIMATED BACKGROUND ═══════════════════════════════════════════════ */ .bg-canvas { position: fixed; inset: 0; z-index: 0; overflow: hidden; } /* Deep base gradient */ .bg-canvas::before { content: ''; position: absolute; inset: -20%; background: radial-gradient(ellipse 80% 60% at 20% 80%, rgba(59, 130, 246, 0.12) 0%, transparent 60%), radial-gradient(ellipse 60% 80% at 80% 20%, rgba(139, 92, 246, 0.10) 0%, transparent 60%), radial-gradient(ellipse 90% 50% at 50% 50%, rgba(0, 229, 255, 0.04) 0%, transparent 70%), linear-gradient(180deg, #030308 0%, #0a0a1a 40%, #080818 100%); animation: bgShift 25s ease-in-out infinite alternate; } @keyframes bgShift { 0% { transform: translate(0, 0) scale(1); } 33% { transform: translate(-2%, 1%) scale(1.02); } 66% { transform: translate(1%, -1%) scale(1.01); } 100% { transform: translate(-1%, 2%) scale(1.03); } } /* Floating blobs */ .blob { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0; animation: blobFloat linear infinite; will-change: transform, opacity; pointer-events: none; } .blob-1 { width: 500px; height: 500px; background: radial-gradient(circle, rgba(0, 229, 255, 0.18), transparent 70%); top: -10%; left: -5%; animation-duration: 22s; animation-delay: 0s; } .blob-2 { width: 600px; height: 600px; background: radial-gradient(circle, rgba(139, 92, 246, 0.15), transparent 70%); bottom: -15%; right: -10%; animation-duration: 28s; animation-delay: -5s; } .blob-3 { width: 400px; height: 400px; background: radial-gradient(circle, rgba(45, 212, 191, 0.12), transparent 70%); top: 40%; left: 60%; animation-duration: 20s; animation-delay: -10s; } .blob-4 { width: 350px; height: 350px; background: radial-gradient(circle, rgba(59, 130, 246, 0.14), transparent 70%); top: 60%; left: 10%; animation-duration: 26s; animation-delay: -3s; } .blob-5 { width: 450px; height: 450px; background: radial-gradient(circle, rgba(0, 229, 255, 0.08), transparent 70%); top: 10%; right: 20%; animation-duration: 30s; animation-delay: -8s; } @keyframes blobFloat { 0% { transform: translate(0, 0) scale(1); opacity: 0.6; } 25% { transform: translate(60px, -80px) scale(1.1); opacity: 0.8; } 50% { transform: translate(-40px, -120px) scale(0.95); opacity: 0.65; } 75% { transform: translate(80px, -40px) scale(1.05); opacity: 0.75; } 100% { transform: translate(0, 0) scale(1); opacity: 0.6; } } /* Grid mesh overlay */ .mesh-grid { position: absolute; inset: 0; background-image: linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px); background-size: 60px 60px; mask-image: radial-gradient(ellipse 60% 50% at 50% 50%, black 20%, transparent 70%); -webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 50%, black 20%, transparent 70%); animation: meshPulse 8s ease-in-out infinite alternate; } @keyframes meshPulse { 0% { opacity: 0.4; } 100% { opacity: 0.8; } } /* Light streaks */ .light-streak { position: absolute; width: 200%; height: 1px; background: linear-gradient(90deg, transparent 0%, rgba(0, 229, 255, 0.15) 30%, rgba(139, 92, 246, 0.2) 50%, rgba(0, 229, 255, 0.15) 70%, transparent 100%); transform-origin: center; animation: streakMove linear infinite; pointer-events: none; } .streak-1 { top: 25%; left: -50%; transform: rotate(-15deg); animation-duration: 12s; } .streak-2 { top: 70%; left: -50%; transform: rotate(10deg); animation-duration: 16s; animation-delay: -4s; opacity: 0.5; } .streak-3 { top: 50%; left: -50%; transform: rotate(-5deg); animation-duration: 20s; animation-delay: -8s; opacity: 0.3; } @keyframes streakMove { 0% { transform: translateX(-30%) rotate(-15deg); opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { transform: translateX(30%) rotate(-15deg); opacity: 0; } } /* Particles canvas */ #particles-canvas { position: absolute; inset: 0; pointer-events: none; } /* Mouse spotlight */ .cursor-glow { position: fixed; width: 600px; height: 600px; border-radius: 50%; background: radial-gradient(circle, rgba(0, 229, 255, 0.06) 0%, rgba(139, 92, 246, 0.03) 30%, transparent 70%); pointer-events: none; z-index: 1; transform: translate(-50%, -50%); transition: opacity 0.3s ease; will-change: left, top; } /* Noise overlay for texture */ .noise-overlay { position: fixed; inset: 0; z-index: 2; opacity: 0.025; pointer-events: none; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='1'/%3E%3C/svg%3E"); background-repeat: repeat; background-size: 256px 256px; } /* ═══════════════════════════════════════════════ MAIN CONTAINER ═══════════════════════════════════════════════ */ .main-container { position: relative; z-index: 10; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; padding: 24px; } /* ═══════════════════════════════════════════════ GLASS LOGIN CARD ═══════════════════════════════════════════════ */ .login-card { position: relative; width: 100%; max-width: 460px; padding: 48px 40px 40px; background: var(--glass-bg); backdrop-filter: blur(40px) saturate(1.4); -webkit-backdrop-filter: blur(40px) saturate(1.4); border-radius: var(--radius); border: 1px solid var(--glass-border); box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.03) inset, 0 1px 0 0 rgba(255, 255, 255, 0.05) inset, 0 25px 60px -12px rgba(0, 0, 0, 0.5), 0 0 100px -20px rgba(0, 229, 255, 0.08), 0 0 80px -30px rgba(139, 92, 246, 0.06); transform: translateY(0); transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow var(--transition); animation: cardEntrance 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; } @keyframes cardEntrance { 0% { opacity: 0; transform: translateY(40px) scale(0.96); filter: blur(8px); } 100% { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); } } .login-card:hover { box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04) inset, 0 1px 0 0 rgba(255, 255, 255, 0.06) inset, 0 30px 70px -12px rgba(0, 0, 0, 0.55), 0 0 120px -20px rgba(0, 229, 255, 0.1), 0 0 100px -30px rgba(139, 92, 246, 0.08); } /* Card top glow accent */ .login-card::before { content: ''; position: absolute; top: -1px; left: 40px; right: 40px; height: 1px; background: linear-gradient(90deg, transparent, rgba(0, 229, 255, 0.5), rgba(139, 92, 246, 0.5), transparent); border-radius: 1px; } /* Inner glow */ .login-card::after { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 120px; background: linear-gradient(180deg, rgba(0, 229, 255, 0.03) 0%, transparent 100%); border-radius: var(--radius) var(--radius) 0 0; pointer-events: none; } /* ═══════════════════════════════════════════════ CARD HEADER ═══════════════════════════════════════════════ */ .card-header { position: relative; z-index: 1; text-align: center; margin-bottom: 36px; } .brand-icon { width: 52px; height: 52px; margin: 0 auto 20px; position: relative; display: flex; align-items: center; justify-content: center; } .brand-icon svg { width: 100%; height: 100%; filter: drop-shadow(0 0 20px rgba(0, 229, 255, 0.3)); } .brand-icon::after { content: ''; position: absolute; inset: -8px; border-radius: 50%; background: radial-gradient(circle, rgba(0, 229, 255, 0.1), transparent 70%); animation: iconPulse 3s ease-in-out infinite; } @keyframes iconPulse { 0%, 100% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.15); opacity: 0.8; } } .card-title { font-size: 26px; font-weight: 700; letter-spacing: -0.5px; background: linear-gradient(135deg, #ffffff 0%, #b8c5e8 50%, #ffffff 100%); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 8px; line-height: 1.2; } .card-subtitle { font-size: 13.5px; font-weight: 400; color: rgba(176, 190, 220, 0.6); letter-spacing: 0.2px; line-height: 1.5; } /* ═══════════════════════════════════════════════ FORM ELEMENTS ═══════════════════════════════════════════════ */ .form-body { position: relative; z-index: 1; } /* Input group */ .input-group { position: relative; margin-bottom: 22px; } .input-wrapper { position: relative; display: flex; align-items: center; } .input-icon { position: absolute; left: 16px; width: 18px; height: 18px; color: rgba(176, 190, 220, 0.35); transition: color var(--transition), transform var(--transition); pointer-events: none; z-index: 2; } .input-field { width: 100%; height: 54px; padding: 16px 48px 0 48px; background: var(--input-bg); border: 1px solid var(--input-border); border-radius: var(--radius-sm); color: var(--white); font-family: 'Inter', sans-serif; font-size: 14.5px; font-weight: 400; outline: none; transition: all var(--transition); caret-color: var(--cyan); letter-spacing: 0.1px; } .input-field::placeholder { color: transparent; } .floating-label { position: absolute; left: 48px; top: 50%; transform: translateY(-50%); font-size: 14px; font-weight: 400; color: rgba(176, 190, 220, 0.4); pointer-events: none; transition: all var(--transition); transform-origin: left center; letter-spacing: 0.2px; } /* Floating label active states */ .input-field:focus ~ .floating-label, .input-field.has-value ~ .floating-label { top: 14px; transform: translateY(0) scale(0.78); color: var(--cyan); font-weight: 500; letter-spacing: 0.5px; } .input-field:focus { background: rgba(0, 229, 255, 0.04); border-color: rgba(0, 229, 255, 0.25); box-shadow: 0 0 0 3px rgba(0, 229, 255, 0.08), 0 0 30px -10px rgba(0, 229, 255, 0.12); } .input-field:focus ~ .input-icon { color: var(--cyan); transform: scale(1.1); } /* Validation states */ .input-group.error .input-field { border-color: rgba(244, 63, 94, 0.4); background: rgba(244, 63, 94, 0.04); animation: inputShake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97); } .input-group.error .input-field:focus { box-shadow: 0 0 0 3px var(--error-dim), 0 0 30px -10px rgba(244, 63, 94, 0.15); } .input-group.error .floating-label, .input-group.error .input-icon { color: var(--error) !important; } .input-group.valid .input-field { border-color: rgba(16, 185, 129, 0.3); } .input-group.valid .input-field:focus { box-shadow: 0 0 0 3px var(--success-dim), 0 0 30px -10px rgba(16, 185, 129, 0.12); background: rgba(16, 185, 129, 0.03); } .input-group.valid .floating-label, .input-group.valid .input-icon { color: var(--success) !important; } @keyframes inputShake { 0%, 100% { transform: translateX(0); } 15% { transform: translateX(-6px); } 30% { transform: translateX(5px); } 45% { transform: translateX(-4px); } 60% { transform: translateX(3px); } 75% { transform: translateX(-2px); } 90% { transform: translateX(1px); } } /* Error message */ .error-message { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12px; font-weight: 450; color: var(--error); opacity: 0; transform: translateY(-4px); transition: all var(--transition-fast); overflow: hidden; max-height: 0; } .error-message.visible { opacity: 1; transform: translateY(0); max-height: 30px; } .error-message svg { width: 13px; height: 13px; flex-shrink: 0; } /* Password toggle */ .password-toggle { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; background: none; border: none; color: rgba(176, 190, 220, 0.35); cursor: pointer; border-radius: 10px; transition: all var(--transition-fast); z-index: 2; } .password-toggle:hover { color: rgba(176, 190, 220, 0.7); background: rgba(255, 255, 255, 0.05); } .password-toggle svg { width: 18px; height: 18px; transition: transform var(--transition-spring); } .password-toggle:active svg { transform: scale(0.85); } /* ═══════════════════════════════════════════════ PASSWORD STRENGTH ═══════════════════════════════════════════════ */ .password-strength { display: flex; gap: 4px; margin-top: 10px; opacity: 0; transform: translateY(-4px); transition: all var(--transition); } .password-strength.visible { opacity: 1; transform: translateY(0); } .strength-bar { flex: 1; height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.06); overflow: hidden; transition: all var(--transition); } .strength-bar .fill { width: 0%; height: 100%; border-radius: 2px; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); } .strength-bar.active .fill { width: 100%; } .strength-weak .fill { background: var(--error); } .strength-fair .fill { background: #f59e0b; } .strength-good .fill { background: var(--cyan); } .strength-strong .fill { background: var(--success); } .strength-text { font-size: 11px; font-weight: 500; margin-top: 6px; letter-spacing: 0.5px; transition: color var(--transition); } /* ═══════════════════════════════════════════════ META ROW (Remember / Forgot) ═══════════════════════════════════════════════ */ .meta-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 28px; margin-top: 4px; } /* Custom checkbox */ .checkbox-wrapper { display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; } .checkbox-wrapper input { display: none; } .custom-checkbox { width: 18px; height: 18px; border: 1.5px solid rgba(255, 255, 255, 0.12); border-radius: 6px; display: flex; align-items: center; justify-content: center; transition: all var(--transition); background: rgba(255, 255, 255, 0.03); flex-shrink: 0; } .custom-checkbox svg { width: 11px; height: 11px; opacity: 0; transform: scale(0.5); transition: all var(--transition-spring); color: white; } .checkbox-wrapper input:checked ~ .custom-checkbox { background: linear-gradient(135deg, var(--cyan), var(--violet)); border-color: transparent; box-shadow: 0 0 12px rgba(0, 229, 255, 0.25); } .checkbox-wrapper input:checked ~ .custom-checkbox svg { opacity: 1; transform: scale(1); } .checkbox-label { font-size: 13px; font-weight: 400; color: rgba(176, 190, 220, 0.55); transition: color var(--transition); } .checkbox-wrapper:hover .checkbox-label { color: rgba(176, 190, 220, 0.8); } /* Forgot link */ .forgot-link { font-size: 13px; font-weight: 500; color: var(--cyan); text-decoration: none; opacity: 0.7; transition: all var(--transition); position: relative; } .forgot-link::after { content: ''; position: absolute; bottom: -2px; left: 0; width: 0%; height: 1px; background: var(--cyan); transition: width var(--transition); } .forgot-link:hover { opacity: 1; } .forgot-link:hover::after { width: 100%; } /* ═══════════════════════════════════════════════ LOGIN BUTTON ═══════════════════════════════════════════════ */ .login-btn { position: relative; width: 100%; height: 54px; border: none; border-radius: var(--radius-sm); background: linear-gradient(135deg, var(--cyan), var(--blue), var(--violet)); background-size: 200% 200%; color: white; font-family: 'Inter', sans-serif; font-size: 15px; font-weight: 600; letter-spacing: 0.5px; cursor: pointer; overflow: hidden; transition: all var(--transition); box-shadow: 0 4px 20px -4px rgba(0, 229, 255, 0.3), 0 0 40px -10px rgba(139, 92, 246, 0.2); animation: btnGradient 4s ease-in-out infinite; } @keyframes btnGradient { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } } .login-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 30px -4px rgba(0, 229, 255, 0.4), 0 0 60px -10px rgba(139, 92, 246, 0.3); } .login-btn:active { transform: translateY(0) scale(0.985); } /* Button shine effect */ .login-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.15), transparent); transition: left 0.6s ease; } .login-btn:hover::before { left: 100%; } /* Button ripple */ .ripple { position: absolute; border-radius: 50%; background: rgba(255, 255, 255, 0.3); transform: scale(0); animation: rippleEffect 0.6s ease-out forwards; pointer-events: none; } @keyframes rippleEffect { to { transform: scale(4); opacity: 0; } } .btn-content { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 8px; transition: all var(--transition); } .btn-content .arrow { transition: transform var(--transition); } .login-btn:hover .btn-content .arrow { transform: translateX(3px); } /* Loading state */ .login-btn.loading .btn-content { opacity: 0; transform: translateY(10px); } .btn-spinner { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity var(--transition); } .login-btn.loading .btn-spinner { opacity: 1; } .spinner { width: 22px; height: 22px; border: 2.5px solid rgba(255, 255, 255, 0.25); border-top-color: white; border-radius: 50%; animation: spin 0.7s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* Success state */ .login-btn.success { background: var(--success) !important; box-shadow: 0 4px 20px -4px rgba(16, 185, 129, 0.4) !important; animation: none; } .btn-success-icon { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity var(--transition); } .login-btn.success .btn-success-icon { opacity: 1; } .login-btn.success .btn-spinner, .login-btn.success .btn-content { opacity: 0; } .success-check { width: 28px; height: 28px; color: white; } .success-check path { stroke-dasharray: 30; stroke-dashoffset: 30; animation: drawCheck 0.5s 0.1s ease forwards; } @keyframes drawCheck { to { stroke-dashoffset: 0; } } /* ═══════════════════════════════════════════════ DIVIDER ═══════════════════════════════════════════════ */ .divider { display: flex; align-items: center; gap: 16px; margin: 24px 0; } .divider-line { flex: 1; height: 1px; background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.08), transparent); } .divider-text { font-size: 12px; font-weight: 400; color: rgba(176, 190, 220, 0.3); letter-spacing: 1px; text-transform: uppercase; } /* ═══════════════════════════════════════════════ SOCIAL BUTTONS (optional visual) ═══════════════════════════════════════════════ */ .social-row { display: flex; gap: 12px; margin-bottom: 24px; } .social-btn { flex: 1; height: 46px; display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.07); border-radius: var(--radius-xs); cursor: pointer; transition: all var(--transition); color: rgba(176, 190, 220, 0.5); } .social-btn:hover { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.12); color: rgba(176, 190, 220, 0.9); transform: translateY(-1px); } .social-btn:active { transform: translateY(0) scale(0.97); } .social-btn svg { width: 20px; height: 20px; } /* ═══════════════════════════════════════════════ FOOTER ═══════════════════════════════════════════════ */ .card-footer { text-align: center; margin-top: 0; } .card-footer p { font-size: 13.5px; color: rgba(176, 190, 220, 0.4); } .register-link { color: var(--cyan); text-decoration: none; font-weight: 600; transition: all var(--transition); position: relative; } .register-link:hover { color: #5eead4; text-shadow: 0 0 20px rgba(0, 229, 255, 0.3); } /* ═══════════════════════════════════════════════ SUCCESS OVERLAY ═══════════════════════════════════════════════ */ .success-overlay { position: absolute; inset: 0; background: rgba(8, 12, 28, 0.92); backdrop-filter: blur(20px); border-radius: var(--radius); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 100; opacity: 0; pointer-events: none; transition: opacity 0.5s ease; } .success-overlay.visible { opacity: 1; pointer-events: auto; } .success-ring { width: 80px; height: 80px; border-radius: 50%; border: 2px solid var(--success); display: flex; align-items: center; justify-content: center; margin-bottom: 20px; box-shadow: 0 0 40px rgba(16, 185, 129, 0.2); animation: ringPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; transform: scale(0); } @keyframes ringPop { to { transform: scale(1); } } .success-ring svg { width: 36px; height: 36px; color: var(--success); } .success-title { font-size: 20px; font-weight: 700; color: var(--white); margin-bottom: 8px; animation: fadeUp 0.5s 0.2s ease forwards; opacity: 0; transform: translateY(10px); } .success-subtitle { font-size: 13.5px; color: rgba(176, 190, 220, 0.5); animation: fadeUp 0.5s 0.35s ease forwards; opacity: 0; transform: translateY(10px); } @keyframes fadeUp { to { opacity: 1; transform: translateY(0); } } /* ═══════════════════════════════════════════════ RESPONSIVE ═══════════════════════════════════════════════ */ @media (max-width: 520px) { .login-card { padding: 36px 24px 32px; border-radius: 20px; max-width: 100%; } .card-title { font-size: 22px; } .card-subtitle { font-size: 13px; } .input-field { height: 50px; font-size: 14px; } .login-btn { height: 50px; font-size: 14px; } .social-btn { height: 42px; } .blob { filter: blur(60px); } .blob-1 { width: 300px; height: 300px; } .blob-2 { width: 350px; height: 350px; } .blob-3, .blob-4, .blob-5 { display: none; } } @media (max-height: 700px) { .login-card { padding: 30px 32px 28px; } .card-header { margin-bottom: 24px; } .input-group { margin-bottom: 16px; } .meta-row { margin-bottom: 20px; } .divider { margin: 18px 0; } .brand-icon { width: 40px; height: 40px; margin-bottom: 14px; } } /* ═══════════════════════════════════════════════ ACCESSIBILITY: FOCUS VISIBLE ═══════════════════════════════════════════════ */ .input-field:focus-visible, .login-btn:focus-visible, .social-btn:focus-visible, .forgot-link:focus-visible, .register-link:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; } /* Selection color */ ::selection { background: rgba(0, 229, 255, 0.2); color: white; } </style>
JS
Copy
<script> /* ═══════════════════════════════════════════════ PARTICLES SYSTEM ═══════════════════════════════════════════════ */ const ParticleSystem = (() => { const canvas = document.getElementById('particles-canvas'); const ctx = canvas.getContext('2d'); let particles = []; let animationId; let width, height; function resize() { width = canvas.width = window.innerWidth; height = canvas.height = window.innerHeight; } class Particle { constructor() { this.reset(); } reset() { this.x = Math.random() * width; this.y = Math.random() * height; this.size = Math.random() * 1.8 + 0.3; this.speedX = (Math.random() - 0.5) * 0.3; this.speedY = (Math.random() - 0.5) * 0.3; this.opacity = Math.random() * 0.4 + 0.1; this.opacitySpeed = Math.random() * 0.003 + 0.001; this.opacityDir = 1; const colors = [ [0, 229, 255], [139, 92, 246], [59, 130, 246], [45, 212, 191], [200, 210, 240] ]; this.color = colors[Math.floor(Math.random() * colors.length)]; } update() { this.x += this.speedX; this.y += this.speedY; this.opacity += this.opacitySpeed * this.opacityDir; if (this.opacity >= 0.5) this.opacityDir = -1; if (this.opacity <= 0.05) this.opacityDir = 1; if (this.x < -10 || this.x > width + 10 || this.y < -10 || this.y > height + 10) { this.reset(); } } draw() { const [r, g, b] = this.color; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${this.opacity})`; ctx.fill(); if (this.size > 1.2) { ctx.beginPath(); ctx.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2); ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${this.opacity * 0.1})`; ctx.fill(); } } } function init() { resize(); const count = Math.min(Math.floor((width * height) / 12000), 120); particles = Array.from({ length: count }, () => new Particle()); } function animate() { ctx.clearRect(0, 0, width, height); particles.forEach(p => { p.update(); p.draw(); }); animationId = requestAnimationFrame(animate); } function start() { init(); animate(); window.addEventListener('resize', () => { resize(); const count = Math.min(Math.floor((width * height) / 12000), 120); while (particles.length < count) particles.push(new Particle()); while (particles.length > count) particles.pop(); }); } return { start }; })(); /* ═══════════════════════════════════════════════ CURSOR SPOTLIGHT & PARALLAX ═══════════════════════════════════════════════ */ const CursorEffects = (() => { const glow = document.getElementById('cursor-glow'); const card = document.getElementById('login-card'); let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; let currentX = mouseX; let currentY = mouseY; function lerp(a, b, t) { return a + (b - a) * t; } function handleMove(e) { mouseX = e.clientX; mouseY = e.clientY; } function animate() { currentX = lerp(currentX, mouseX, 0.08); currentY = lerp(currentY, mouseY, 0.08); glow.style.left = currentX + 'px'; glow.style.top = currentY + 'px'; // Card parallax const rect = card.getBoundingClientRect(); const cardCenterX = rect.left + rect.width / 2; const cardCenterY = rect.top + rect.height / 2; const deltaX = (mouseX - cardCenterX) / window.innerWidth; const deltaY = (mouseY - cardCenterY) / window.innerHeight; card.style.transform = `perspective(1200px) rotateY(${deltaX * 2}deg) rotateX(${-deltaY * 2}deg)`; requestAnimationFrame(animate); } function init() { document.addEventListener('mousemove', handleMove); document.addEventListener('touchmove', (e) => { const t = e.touches[0]; mouseX = t.clientX; mouseY = t.clientY; }); animate(); } return { init }; })(); /* ═══════════════════════════════════════════════ FORM VALIDATION & INTERACTIONS ═══════════════════════════════════════════════ */ const LoginForm = (() => { const form = document.getElementById('login-form'); const emailInput = document.getElementById('email'); const passwordInput = document.getElementById('password'); const emailGroup = document.getElementById('email-group'); const passwordGroup = document.getElementById('password-group'); const emailError = document.getElementById('email-error'); const passwordError = document.getElementById('password-error'); const loginBtn = document.getElementById('login-btn'); const toggleBtn = document.getElementById('toggle-password'); const eyeOpen = document.getElementById('eye-open'); const eyeClosed = document.getElementById('eye-closed'); const strengthBars = document.querySelectorAll('.strength-bar'); const strengthContainer = document.getElementById('password-strength'); const strengthText = document.getElementById('strength-text'); const successOverlay = document.getElementById('success-overlay'); let passwordVisible = false; let isSubmitting = false; // Email validation function validateEmail(value) { if (!value.trim()) return 'Please enter your email address'; const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!regex.test(value)) return 'Please enter a valid email address'; return ''; } // Password validation function validatePassword(value) { if (!value) return 'Please enter your password'; if (value.length < 6) return 'Password must be at least 6 characters'; return ''; } // Password strength function getStrength(value) { if (!value) return 0; let score = 0; if (value.length >= 6) score++; if (value.length >= 10) score++; if (/[A-Z]/.test(value) && /[a-z]/.test(value)) score++; if (/[0-9]/.test(value)) score++; if (/[^A-Za-z0-9]/.test(value)) score++; return Math.min(score, 4); } function updateStrength(value) { const strength = getStrength(value); if (value.length > 0) { strengthContainer.classList.add('visible'); } else { strengthContainer.classList.remove('visible'); strengthText.textContent = ''; return; } const labels = ['', 'Weak', 'Fair', 'Good', 'Strong']; const classes = ['', 'strength-weak', 'strength-fair', 'strength-good', 'strength-strong']; const colors = ['', 'var(--error)', '#f59e0b', 'var(--cyan)', 'var(--success)']; strengthBars.forEach((bar, i) => { bar.className = 'strength-bar'; if (i < strength) { bar.classList.add('active', classes[strength]); } }); strengthText.textContent = labels[strength] || ''; strengthText.style.color = colors[strength] || ''; } // Show/hide error function showError(group, errorEl, message) { group.classList.remove('valid'); group.classList.add('error'); errorEl.querySelector('span').textContent = message; errorEl.classList.add('visible'); } function clearError(group, errorEl) { group.classList.remove('error'); errorEl.classList.remove('visible'); } function showValid(group) { group.classList.remove('error'); group.classList.add('valid'); } // Toggle password visibility function togglePassword() { passwordVisible = !passwordVisible; passwordInput.type = passwordVisible ? 'text' : 'password'; eyeOpen.style.display = passwordVisible ? 'none' : 'block'; eyeClosed.style.display = passwordVisible ? 'block' : 'none'; } // Ripple effect function createRipple(e, btn) { const rect = btn.getBoundingClientRect(); const ripple = document.createElement('span'); ripple.classList.add('ripple'); const size = Math.max(rect.width, rect.height); ripple.style.width = ripple.style.height = size + 'px'; ripple.style.left = (e.clientX - rect.left - size / 2) + 'px'; ripple.style.top = (e.clientY - rect.top - size / 2) + 'px'; btn.appendChild(ripple); ripple.addEventListener('animationend', () => ripple.remove()); } // Has-value class for floating label function updateHasValue(input) { if (input.value.trim()) { input.classList.add('has-value'); } else { input.classList.remove('has-value'); } } // Real-time validation function handleEmailInput() { updateHasValue(emailInput); const error = validateEmail(emailInput.value); if (emailGroup.classList.contains('error')) { if (!error) { clearError(emailGroup, emailError); showValid(emailGroup); } else { showError(emailGroup, emailError, error); } } else if (emailInput.value.trim() && !error) { showValid(emailGroup); } else { emailGroup.classList.remove('valid'); } } function handlePasswordInput() { updateHasValue(passwordInput); updateStrength(passwordInput.value); const error = validatePassword(passwordInput.value); if (passwordGroup.classList.contains('error')) { if (!error) { clearError(passwordGroup, passwordError); showValid(passwordGroup); } else { showError(passwordGroup, passwordError, error); } } else if (passwordInput.value && !error) { showValid(passwordGroup); } else { passwordGroup.classList.remove('valid'); } } // Email blur validation function handleEmailBlur() { if (!emailInput.value.trim()) return; const error = validateEmail(emailInput.value); if (error) { showError(emailGroup, emailError, error); } else { clearError(emailGroup, emailError); showValid(emailGroup); } } // Submit async function handleSubmit(e) { e.preventDefault(); if (isSubmitting) return; const emailErr = validateEmail(emailInput.value); const passErr = validatePassword(passwordInput.value); let hasError = false; if (emailErr) { showError(emailGroup, emailError, emailErr); hasError = true; } else { clearError(emailGroup, emailError); showValid(emailGroup); } if (passErr) { showError(passwordGroup, passwordError, passErr); hasError = true; } else { clearError(passwordGroup, passwordError); showValid(passwordGroup); } if (hasError) return; // Start loading isSubmitting = true; loginBtn.classList.add('loading'); loginBtn.disabled = true; // Simulate API call await new Promise(resolve => setTimeout(resolve, 2000)); // Success loginBtn.classList.remove('loading'); loginBtn.classList.add('success'); await new Promise(resolve => setTimeout(resolve, 800)); // Show overlay successOverlay.classList.add('visible'); // Reset after delay setTimeout(() => { loginBtn.classList.remove('success'); loginBtn.disabled = false; isSubmitting = false; successOverlay.classList.remove('visible'); form.reset(); emailInput.classList.remove('has-value'); passwordInput.classList.remove('has-value'); emailGroup.classList.remove('valid'); passwordGroup.classList.remove('valid'); strengthContainer.classList.remove('visible'); strengthText.textContent = ''; strengthBars.forEach(b => b.className = 'strength-bar'); }, 3500); } function init() { emailInput.addEventListener('input', handleEmailInput); passwordInput.addEventListener('input', handlePasswordInput); emailInput.addEventListener('blur', handleEmailBlur); toggleBtn.addEventListener('click', togglePassword); loginBtn.addEventListener('click', (e) => createRipple(e, loginBtn)); form.addEventListener('submit', handleSubmit); // Initial has-value check (for autofill) setTimeout(() => { updateHasValue(emailInput); updateHasValue(passwordInput); }, 100); } return { init }; })(); /* ═══════════════════════════════════════════════ INITIALIZATION ═══════════════════════════════════════════════ */ document.addEventListener('DOMContentLoaded', () => { ParticleSystem.start(); CursorEffects.init(); LoginForm.init(); }); </script>