Files
moraworld/login.html
T
Lizandro GuarnizoandClaude Sonnet 4.6 492dc5436c fix: auditoría UI/UX landing page y consistencia entre subpáginas
- Agrega menú móvil funcional con panel deslizable y animación hamburger
- Corrige hero stat "150 N / Day St" confuso por "$3.50/lb (flete)"
- Arregla bug CSS `flex-wrap: gap` inválido en footer-bottom
- Corrige links "Carga Pesada" en nav y footer para apuntar a #cotizacion
- Agrega acción al botón de pre-alerta con validación
- Limpia address del footer (eliminado placeholder "Suite EC-" vacío)
- Agrega favicon, OG tags y theme-color a las 4 páginas
- Consolida event listeners para evitar conflictos móvil/WhatsApp

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 11:35:15 -05:00

297 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Iniciar sesión — Moraworld Imports</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📦</text></svg>" />
<meta name="theme-color" content="#0057FF" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"/>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
--primary:#0057FF;--primary-d:#003FC7;--accent:#FF6B00;
--dark:#0D1117;--gray-900:#111827;--gray-700:#374151;
--gray-500:#6B7280;--gray-200:#E5E7EB;--gray-100:#F3F4F6;
--white:#fff;--green:#10B981;--red:#EF4444;
}
body{font-family:'Inter',sans-serif;background:var(--gray-100);min-height:100vh;display:flex;flex-direction:column}
a{text-decoration:none;color:inherit}
/* NAV */
nav{background:rgba(255,255,255,.95);backdrop-filter:blur(12px);border-bottom:1px solid var(--gray-200);padding:0 24px;height:64px;display:flex;align-items:center;justify-content:space-between}
.nav-logo{font-size:1.2rem;font-weight:800;color:var(--primary)}
.nav-logo span{color:var(--accent)}
.nav-back{font-size:.85rem;color:var(--gray-500);display:flex;align-items:center;gap:6px;transition:color .2s}
.nav-back:hover{color:var(--primary)}
/* LAYOUT */
.auth-wrapper{flex:1;display:grid;grid-template-columns:1fr 1fr;min-height:calc(100vh - 64px)}
/* LEFT PANEL */
.auth-left{
background:linear-gradient(135deg,#0D1117 0%,#0D2150 100%);
display:flex;flex-direction:column;justify-content:center;align-items:flex-start;
padding:60px 56px;position:relative;overflow:hidden;
}
.auth-left::before{
content:'';position:absolute;inset:0;
background:url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none'%3E%3Cg fill='%230057FF' fill-opacity='0.06'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
.auth-left-content{position:relative;z-index:1;max-width:420px}
.al-badge{display:inline-flex;align-items:center;gap:8px;background:rgba(0,87,255,.15);border:1px solid rgba(0,87,255,.3);color:#7AADFF;font-size:.8rem;font-weight:600;padding:6px 14px;border-radius:999px;margin-bottom:32px}
.al-badge span{width:8px;height:8px;border-radius:50%;background:#7AADFF;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.auth-left h2{font-size:2.2rem;font-weight:800;color:#fff;line-height:1.2;margin-bottom:16px}
.auth-left h2 em{color:var(--accent);font-style:normal}
.auth-left p{color:#9CA3AF;font-size:1rem;line-height:1.7;margin-bottom:40px}
.feature-list{display:flex;flex-direction:column;gap:16px}
.feat-item{display:flex;align-items:flex-start;gap:14px}
.feat-icon{width:40px;height:40px;border-radius:10px;background:rgba(0,87,255,.2);display:flex;align-items:center;justify-content:center;font-size:1rem;flex-shrink:0}
.feat-text strong{display:block;color:#fff;font-size:.9rem;margin-bottom:2px}
.feat-text span{color:#6B7280;font-size:.82rem}
/* RIGHT PANEL */
.auth-right{display:flex;align-items:center;justify-content:center;padding:40px 24px;background:var(--white)}
.auth-box{width:100%;max-width:420px}
.auth-box h1{font-size:1.8rem;font-weight:800;color:var(--gray-900);margin-bottom:8px}
.auth-box .subtitle{color:var(--gray-500);font-size:.93rem;margin-bottom:36px}
.auth-box .subtitle a{color:var(--primary);font-weight:600}
.form-group{margin-bottom:20px}
.form-group label{display:block;font-size:.85rem;font-weight:600;color:var(--gray-700);margin-bottom:6px}
.form-group input{
width:100%;padding:13px 16px;border:2px solid var(--gray-200);
border-radius:8px;font-size:.95rem;font-family:inherit;
transition:border-color .2s;outline:none;background:var(--white);
}
.form-group input:focus{border-color:var(--primary)}
.form-group input.error{border-color:var(--red)}
.input-wrap{position:relative}
.input-wrap input{padding-right:44px}
.toggle-pass{position:absolute;right:14px;top:50%;transform:translateY(-50%);cursor:pointer;font-size:1rem;color:var(--gray-500);background:none;border:none;padding:0}
.form-options{display:flex;justify-content:space-between;align-items:center;margin-bottom:28px}
.remember{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--gray-700);cursor:pointer}
.remember input[type=checkbox]{accent-color:var(--primary);width:16px;height:16px}
.forgot-link{font-size:.85rem;color:var(--primary);font-weight:600}
.forgot-link:hover{text-decoration:underline}
.btn-submit{
width:100%;padding:14px;background:var(--primary);color:#fff;
border:none;border-radius:8px;font-size:1rem;font-weight:700;
cursor:pointer;font-family:inherit;transition:all .2s;
}
.btn-submit:hover{background:var(--primary-d);transform:translateY(-1px);box-shadow:0 6px 20px rgba(0,87,255,.3)}
.divider{display:flex;align-items:center;gap:12px;margin:24px 0;color:var(--gray-500);font-size:.82rem}
.divider::before,.divider::after{content:'';flex:1;height:1px;background:var(--gray-200)}
.mfa-notice{
background:#EFF6FF;border:1px solid #BFDBFE;border-radius:8px;
padding:12px 16px;display:flex;gap:10px;align-items:flex-start;
font-size:.82rem;color:#1D4ED8;margin-bottom:24px;
}
.mfa-notice strong{display:block;margin-bottom:2px}
.mfa-notice span{opacity:.75}
/* MFA step */
.mfa-step{display:none}
.mfa-step.show{display:block}
.login-step{display:block}
.login-step.hide{display:none}
.otp-wrap{display:flex;gap:10px;margin-bottom:24px}
.otp-wrap input{
flex:1;text-align:center;font-size:1.6rem;font-weight:700;
padding:14px 4px;border:2px solid var(--gray-200);border-radius:8px;
font-family:inherit;outline:none;transition:border-color .2s;
}
.otp-wrap input:focus{border-color:var(--primary)}
.back-link{display:inline-flex;align-items:center;gap:6px;color:var(--gray-500);font-size:.85rem;cursor:pointer;margin-bottom:24px;background:none;border:none;font-family:inherit}
.back-link:hover{color:var(--primary)}
/* ALERT */
.alert{padding:12px 16px;border-radius:8px;font-size:.85rem;margin-bottom:20px;display:none}
.alert.show{display:flex;gap:10px;align-items:center}
.alert-error{background:#FEE2E2;color:#991B1B;border:1px solid #FCA5A5}
@media(max-width:768px){
.auth-wrapper{grid-template-columns:1fr}
.auth-left{display:none}
}
</style>
</head>
<body>
<nav>
<a href="index.html" class="nav-logo">Moraworld<span>.</span>Imports</a>
<a href="index.html" class="nav-back">← Volver al inicio</a>
</nav>
<div class="auth-wrapper">
<!-- ── LEFT ── -->
<div class="auth-left">
<div class="auth-left-content">
<div class="al-badge">
<span></span> Sistema de Casillero Internacional
</div>
<h2>Tu puerta al<br/><em>mercado americano</em></h2>
<p>Gestiona tus compras en EE.UU., haz seguimiento en tiempo real y recibe en Ecuador con declaración SENAE incluida.</p>
<div class="feature-list">
<div class="feat-item">
<div class="feat-icon">📦</div>
<div class="feat-text">
<strong>Suite personalizada en NJ</strong>
<span>150 N Day St, City of Orange, NJ 07050</span>
</div>
</div>
<div class="feat-item">
<div class="feat-icon">🔍</div>
<div class="feat-text">
<strong>Tracking en tiempo real</strong>
<span>11 estados de seguimiento con notificaciones automáticas</span>
</div>
</div>
<div class="feat-item">
<div class="feat-icon">🛃</div>
<div class="feat-text">
<strong>Aduana incluida</strong>
<span>Declaración simplificada SENAE gestionada automáticamente</span>
</div>
</div>
<div class="feat-item">
<div class="feat-icon">🔒</div>
<div class="feat-text">
<strong>Seguridad ISO/IEC 27001</strong>
<span>MFA, cifrado AES-256 y protección de datos LOPDP</span>
</div>
</div>
</div>
</div>
</div>
<!-- ── RIGHT ── -->
<div class="auth-right">
<div class="auth-box">
<!-- ALERT -->
<div class="alert alert-error" id="alert-error">
<span>⚠️</span>
<span id="alert-msg">Correo o contraseña incorrectos.</span>
</div>
<!-- STEP 1: LOGIN -->
<div class="login-step" id="step-login">
<h1>Bienvenido de vuelta</h1>
<p class="subtitle">¿No tienes cuenta? <a href="registro.html">Crea la tuya gratis →</a></p>
<div class="mfa-notice">
<span>🔐</span>
<div>
<strong>Autenticación segura</strong>
<span>Este portal utiliza verificación en dos pasos (MFA) para proteger tu cuenta.</span>
</div>
</div>
<div class="form-group">
<label for="email">Correo electrónico</label>
<input type="email" id="email" placeholder="correo@ejemplo.com" autocomplete="email"/>
</div>
<div class="form-group">
<label for="password">Contraseña</label>
<div class="input-wrap">
<input type="password" id="password" placeholder="Tu contraseña" autocomplete="current-password"/>
<button class="toggle-pass" onclick="togglePassword('password', this)" type="button">👁️</button>
</div>
</div>
<div class="form-options">
<label class="remember">
<input type="checkbox" id="remember"/> Recordarme
</label>
<a href="#" class="forgot-link">¿Olvidaste tu contraseña?</a>
</div>
<button class="btn-submit" onclick="handleLogin()">Iniciar sesión →</button>
</div>
<!-- STEP 2: MFA -->
<div class="mfa-step" id="step-mfa">
<button class="back-link" onclick="backToLogin()">← Volver</button>
<h1>Verificación MFA</h1>
<p class="subtitle" style="margin-bottom:28px;">Ingresa el código de 6 dígitos de tu aplicación de autenticación (Google Authenticator, Authy, etc.)</p>
<div class="otp-wrap">
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 0)"/>
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 1)"/>
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 2)"/>
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 3)"/>
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 4)"/>
<input type="text" maxlength="1" class="otp-digit" oninput="otpMove(this, 5)"/>
</div>
<button class="btn-submit" onclick="verifyMFA()">Verificar código →</button>
<p style="text-align:center;margin-top:16px;font-size:.82rem;color:var(--gray-500);">¿No tienes acceso? <a href="#" style="color:var(--primary);font-weight:600;">Recuperar cuenta</a></p>
</div>
</div>
</div>
</div>
<script>
function togglePassword(id, btn){
const inp = document.getElementById(id);
if(inp.type === 'password'){inp.type='text'; btn.textContent='🙈';}
else{inp.type='password'; btn.textContent='👁️';}
}
function handleLogin(){
const email = document.getElementById('email').value.trim();
const pass = document.getElementById('password').value;
if(!email || !pass){
showAlert('Por favor ingresa tu correo y contraseña.');
return;
}
hideAlert();
// Demo: mostrar paso MFA
document.getElementById('step-login').classList.add('hide');
document.getElementById('step-mfa').classList.add('show');
document.querySelector('.otp-digit').focus();
}
function backToLogin(){
document.getElementById('step-login').classList.remove('hide');
document.getElementById('step-mfa').classList.remove('show');
hideAlert();
}
function otpMove(inp, idx){
const digits = document.querySelectorAll('.otp-digit');
inp.value = inp.value.replace(/\D/g,'');
if(inp.value && idx < 5) digits[idx+1].focus();
// Auto-verify when all filled
const code = Array.from(digits).map(d=>d.value).join('');
if(code.length === 6) verifyMFA();
}
function verifyMFA(){
const digits = document.querySelectorAll('.otp-digit');
const code = Array.from(digits).map(d=>d.value).join('');
if(code.length < 6){ showAlert('Ingresa los 6 dígitos del código.'); return; }
hideAlert();
// Demo: redirigir al portal del cliente
window.location.href = 'portal-cliente.html';
}
function showAlert(msg){
const el = document.getElementById('alert-error');
document.getElementById('alert-msg').textContent = msg;
el.classList.add('show');
}
function hideAlert(){
document.getElementById('alert-error').classList.remove('show');
}
</script>
</body>
</html>