Espacio Cliente
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Seguimiento de pedido</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #000000;
--bg-raised: #0a0a0a;
--card: #111111;
--card-border: #1e1e1e;
--card-hover: #161616;
--accent: #c8956c;
--accent-dim: rgba(200,149,108,0.12);
--accent-glow: rgba(200,149,108,0.25);
--green: #34d399;
--green-dim: rgba(52,211,153,0.10);
--green-glow: rgba(52,211,153,0.20);
--orange: #f59e0b;
--orange-dim: rgba(245,158,11,0.10);
--orange-glow: rgba(245,158,11,0.20);
--red: #ef4444;
--red-dim: rgba(239,68,68,0.10);
--red-glow: rgba(239,68,68,0.20);
--white: #f0f0f0;
--text: #e8e8e8;
--text-muted: #707070;
--text-dim: #4a4a4a;
--border: #1e1e1e;
--border-light: #2a2a2a;
--input-bg: #0a0a0a;
--shadow: 0 4px 32px rgba(0,0,0,0.5);
--shadow-lg: 0 12px 48px rgba(0,0,0,0.7);
}
body {
font-family: 'DM Sans', sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
overflow-x: hidden;
}
/* ── HEADER ── */
.header {
background: var(--bg);
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 600px;
height: 300px;
background: radial-gradient(ellipse, rgba(200,149,108,0.08) 0%, transparent 70%);
pointer-events: none;
}
.header::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, var(--accent-glow), transparent);
}
.header-inner {
max-width: 720px;
margin: 0 auto;
padding: 44px 24px 40px;
text-align: center;
position: relative;
z-index: 1;
}
.header-badge {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--accent-dim);
border: 1px solid rgba(200,149,108,0.18);
border-radius: 100px;
padding: 6px 18px;
margin-bottom: 22px;
}
.header-badge svg { flex-shrink: 0; }
.header-badge span {
font-size: 11px;
font-weight: 700;
color: var(--accent);
letter-spacing: 2px;
text-transform: uppercase;
}
.header h1 {
font-family: 'Playfair Display', serif;
font-size: 34px;
font-weight: 700;
color: var(--white);
letter-spacing: -0.5px;
margin-bottom: 10px;
}
.header p {
color: var(--text-muted);
font-size: 15px;
font-weight: 400;
}
/* ── MAIN ── */
.main {
max-width: 720px;
margin: 0 auto;
padding: 40px 24px 80px;
}
/* ── FORM CARD ── */
.form-card {
background: var(--card);
border-radius: 16px;
padding: 36px;
box-shadow: var(--shadow);
border: 1px solid var(--card-border);
}
.form-title {
font-size: 18px;
font-weight: 700;
color: var(--white);
margin-bottom: 4px;
}
.form-subtitle {
font-size: 14px;
color: var(--text-muted);
margin-bottom: 28px;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
/* ── TABLET ── */
@media (max-width: 768px) {
.header-inner { padding: 32px 20px 32px; }
.main { padding: 28px 16px 60px; }
.form-card { padding: 28px 22px; }
.result-card { padding: 28px 22px; }
}
/* ── SMARTPHONE ── */
@media (max-width: 480px) {
.header-inner { padding: 28px 16px 28px; }
.header h1 { font-size: 24px; }
.header p { font-size: 13px; }
.header-badge { padding: 5px 14px; margin-bottom: 16px; }
.header-badge span { font-size: 10px; letter-spacing: 1.5px; }
.main { padding: 20px 12px 48px; }
.form-card { padding: 22px 18px; border-radius: 14px; }
.form-title { font-size: 16px; }
.form-subtitle { font-size: 13px; margin-bottom: 20px; }
.form-grid { grid-template-columns: 1fr; gap: 14px; }
.field input {
padding: 14px 12px;
font-size: 16px;
border-radius: 8px;
}
.btn-submit {
padding: 16px;
font-size: 15px;
border-radius: 8px;
margin-top: 20px;
min-height: 50px;
}
.result-card { padding: 22px 18px; border-radius: 14px; margin-top: 16px; }
.result-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
margin-bottom: 24px;
padding-bottom: 18px;
}
.result-header-left h2 { font-size: 18px; }
.result-header-left p { font-size: 12px; }
.tracking-number {
font-size: 12px;
padding: 6px 12px;
word-break: break-all;
}
.timeline { padding-left: 32px; }
.step { padding-bottom: 26px; }
.step-dot {
width: 24px;
height: 24px;
left: -32px;
}
.step-dot svg { width: 12px; height: 12px; }
.step-content h3 { font-size: 14px; }
.step-content .step-date { font-size: 12px; }
.step-content .step-desc { font-size: 12px; }
.status-banner {
padding: 12px 14px;
font-size: 13px;
gap: 10px;
margin-top: 22px;
border-radius: 8px;
line-height: 1.4;
}
.status-banner svg { width: 18px; height: 18px; min-width: 18px; }
.error-msg { font-size: 13px; padding: 10px 14px; }
}
/* ── PANTALLAS MUY PEQUEÑAS ── */
@media (max-width: 360px) {
.header h1 { font-size: 21px; }
.form-card { padding: 18px 14px; }
.result-card { padding: 18px 14px; }
.timeline { padding-left: 28px; }
.step-dot { left: -28px; width: 22px; height: 22px; }
.status-banner { flex-direction: column; text-align: center; }
}
.field label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--text);
margin-bottom: 6px;
}
.field input {
width: 100%;
padding: 12px 14px;
font-family: 'DM Sans', sans-serif;
font-size: 15px;
border: 1.5px solid var(--border-light);
border-radius: 10px;
background: var(--input-bg);
color: var(--white);
transition: border-color .2s, box-shadow .2s;
outline: none;
}
.field input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim);
}
.field input::placeholder { color: var(--text-dim); }
.field input[type="date"]::-webkit-calendar-picker-indicator {
filter: invert(0.7);
cursor: pointer;
}
.btn-submit {
margin-top: 24px;
width: 100%;
padding: 14px;
font-family: 'DM Sans', sans-serif;
font-size: 15px;
font-weight: 700;
color: #000;
background: var(--accent);
border: none;
border-radius: 10px;
cursor: pointer;
transition: background .2s, transform .1s, box-shadow .2s;
letter-spacing: 0.3px;
}
.btn-submit:hover {
background: #d9a67d;
box-shadow: 0 4px 20px var(--accent-glow);
}
.btn-submit:active { transform: scale(0.985); }
/* ── RESULTADO ── */
.result-card {
background: var(--card);
border-radius: 16px;
padding: 36px;
box-shadow: var(--shadow-lg);
border: 1px solid var(--card-border);
margin-top: 24px;
animation: slideUp .5s cubic-bezier(.22,1,.36,1);
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.result-header {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid var(--border-light);
}
.result-header-left h2 {
font-size: 20px;
font-weight: 700;
color: var(--white);
margin-bottom: 2px;
}
.result-header-left p {
font-size: 13px;
color: var(--text-muted);
}
.tracking-number {
font-family: 'DM Sans', monospace;
font-size: 13px;
font-weight: 600;
background: var(--bg);
color: var(--accent);
padding: 8px 14px;
border-radius: 8px;
border: 1px solid var(--card-border);
letter-spacing: 0.5px;
}
/* ── LÍNEA DE TIEMPO ── */
.timeline { position: relative; padding-left: 36px; }
.timeline::before {
content: '';
position: absolute;
left: 13px;
top: 8px;
bottom: 8px;
width: 2px;
background: var(--border-light);
border-radius: 2px;
}
.step {
position: relative;
padding-bottom: 32px;
animation: fadeStep .4s cubic-bezier(.22,1,.36,1) both;
}
.step:last-child { padding-bottom: 0; }
@keyframes fadeStep {
from { opacity: 0; transform: translateX(-8px); }
to { opacity: 1; transform: translateX(0); }
}
.step:nth-child(1) { animation-delay: .1s; }
.step:nth-child(2) { animation-delay: .2s; }
.step:nth-child(3) { animation-delay: .3s; }
.step:nth-child(4) { animation-delay: .4s; }
.step-dot {
position: absolute;
left: -36px;
top: 2px;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.step-dot.done {
background: var(--green);
box-shadow: 0 0 12px var(--green-glow);
}
.step-dot.active {
background: var(--orange);
box-shadow: 0 0 12px var(--orange-glow);
animation: pulseOrange 2.5s ease-in-out infinite;
}
.step-dot.active-red {
background: var(--red);
box-shadow: 0 0 12px var(--red-glow);
animation: pulseRed 2.5s ease-in-out infinite;
}
@keyframes pulseOrange {
0%, 100% { box-shadow: 0 0 12px var(--orange-glow); }
50% { box-shadow: 0 0 24px rgba(245,158,11,0.35); }
}
@keyframes pulseRed {
0%, 100% { box-shadow: 0 0 12px var(--red-glow); }
50% { box-shadow: 0 0 24px rgba(239,68,68,0.35); }
}
.step-dot.pending {
background: var(--card);
border: 2px solid var(--border-light);
}
.step-dot svg { width: 14px; height: 14px; }
.step-content h3 {
font-size: 15px;
font-weight: 700;
margin-bottom: 2px;
}
.step-content .step-date {
font-size: 13px;
color: var(--text-muted);
margin-bottom: 4px;
}
.step-content .step-desc {
font-size: 13px;
color: var(--text-dim);
line-height: 1.5;
}
.step.is-done .step-content h3 { color: var(--green); }
.step.is-active .step-content h3 { color: var(--orange); }
.step.is-active-red .step-content h3 { color: var(--red); }
.step.is-pending .step-content h3 { color: var(--text-dim); }
.status-banner {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 18px;
border-radius: 10px;
margin-top: 28px;
font-size: 14px;
font-weight: 600;
border: 1px solid;
}
.status-banner.green {
background: var(--green-dim);
color: var(--green);
border-color: rgba(52,211,153,0.15);
}
.status-banner.orange {
background: var(--orange-dim);
color: var(--orange);
border-color: rgba(245,158,11,0.15);
}
.status-banner.red {
background: var(--red-dim);
color: var(--red);
border-color: rgba(239,68,68,0.15);
}
.status-banner svg { flex-shrink: 0; }
.error-msg {
margin-top: 12px;
padding: 12px 16px;
background: var(--red-dim);
color: var(--red);
font-size: 14px;
font-weight: 500;
border-radius: 10px;
border: 1px solid rgba(239,68,68,0.15);
animation: slideUp .3s ease;
}
/* ── FOOTER ── */
.footer {
text-align: center;
padding: 24px;
padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
color: var(--text-dim);
font-size: 12px;
border-top: 1px solid var(--border);
}
</style>
</head>
<body>
<div class="header">
<div class="header-inner">
<div class="header-badge">
<svg width="16" height="16" fill="none" viewBox="0 0 16 16"><path d="M8 1l2.1 4.3 4.7.7-3.4 3.3.8 4.7L8 11.8 3.8 14l.8-4.7L1.2 6l4.7-.7L8 1z" fill="#c8956c"/></svg>
<span>Espacio Cliente</span>
</div>
<h1>Seguimiento de pedido</h1>
<p>Sigue el envío de tu paquete en tiempo real</p>
</div>
</div>
<div class="main">
<div class="form-card">
<div class="form-title">Buscar mi pedido</div>
<div class="form-subtitle">Introduce los datos de tu compra</div>
<div class="form-grid">
<div class="field">
<label for="email">Correo electrónico</label>
<input type="email" id="email" placeholder="tu@email.com">
</div>
<div class="field">
<label for="orderDate">Fecha del pedido</label>
<input type="date" id="orderDate">
</div>
</div>
<button class="btn-submit" onclick="trackOrder()">Buscar mi pedido</button>
<div id="errorBox"></div>
</div>
<div id="resultArea"></div>
</div>
<div class="footer">
© 2026 — Servicio de seguimiento de envíos
</div>
<script>
function getBusinessDays(startDate, endDate) {
let count = 0;
const d = new Date(startDate);
d.setDate(d.getDate() + 1);
while (d <= endDate) {
const day = d.getDay();
if (day !== 0 && day !== 6) count++;
d.setDate(d.getDate() + 1);
}
return count;
}
function addBusinessDays(date, days) {
const result = new Date(date);
let added = 0;
while (added < days) {
result.setDate(result.getDate() + 1);
const day = result.getDay();
if (day !== 0 && day !== 6) added++;
}
return result;
}
function formatDate(date) {
return date.toLocaleDateString('es-ES', { day: 'numeric', month: 'long', year: 'numeric' });
}
function formatDateTime(date) {
const h = String(7 + Math.floor(Math.random() * 12)).padStart(2, '0');
const m = String(Math.floor(Math.random() * 60)).padStart(2, '0');
return date.toLocaleDateString('es-ES', { day: 'numeric', month: 'long', year: 'numeric' }) + ' a las ' + h + ':' + m;
}
function generateTrackingNumber() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = 'ES';
for (let i = 0; i < 11; i++) result += chars.charAt(Math.floor(Math.random() * chars.length));
return result;
}
function trackOrder() {
const email = document.getElementById('email').value.trim();
const dateVal = document.getElementById('orderDate').value;
const errorBox = document.getElementById('errorBox');
const resultArea = document.getElementById('resultArea');
errorBox.innerHTML = '';
resultArea.innerHTML = '';
if (!email || !dateVal) {
errorBox.innerHTML = '<div class="error-msg">Por favor, rellena todos los campos.</div>';
return;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
errorBox.innerHTML = '<div class="error-msg">Por favor, introduce un correo electrónico válido.</div>';
return;
}
const orderDate = new Date(dateVal);
const today = new Date();
today.setHours(0,0,0,0);
orderDate.setHours(0,0,0,0);
if (orderDate > today) {
errorBox.innerHTML = '<div class="error-msg">La fecha del pedido no puede ser futura.</div>';
return;
}
const calendarDays = Math.floor((today - orderDate) / (1000 * 60 * 60 * 24));
const businessDays = getBusinessDays(orderDate, today);
const trackNum = generateTrackingNumber();
const dateStep1 = orderDate;
const dateStep2 = addBusinessDays(orderDate, 2);
const dateStep3 = addBusinessDays(orderDate, 3);
const dateStep4 = addBusinessDays(orderDate, 7);
let currentStage = 0;
if (businessDays >= 7) currentStage = 3;
else if (businessDays >= 3) currentStage = 2;
else if (calendarDays >= 2) currentStage = 1;
const checkSVG = '<svg viewBox="0 0 14 14" fill="none"><path d="M2.5 7.5l3 3 6-6" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
const dotSVG = '<svg viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="3" fill="#000"/></svg>';
const emptySVG = '';
const steps = [
{
title: 'Pedido confirmado',
desc: 'Tu pedido ha sido registrado y validado.',
date: formatDate(dateStep1),
},
{
title: 'En espera de recogida',
desc: 'Tu paquete está preparado y espera la recogida por parte del transportista.',
date: currentStage >= 1 ? formatDateTime(dateStep2) : 'Pendiente',
},
{
title: 'Recogido por el transportista',
desc: 'El paquete ha sido recogido y está en camino.',
date: currentStage >= 2 ? formatDateTime(dateStep3) : 'Pendiente',
},
{
title: 'Destinatario ausente, paquete entregado a un vecino',
desc: 'El repartidor se presentó pero el destinatario no estaba. El paquete se entregó a un vecino.',
date: currentStage >= 3 ? formatDateTime(dateStep4) : 'Pendiente',
},
];
let stepsHTML = '';
steps.forEach((step, i) => {
let dotClass, stepClass, icon;
if (i < currentStage) {
dotClass = 'done';
stepClass = 'is-done';
icon = checkSVG;
} else if (i === currentStage) {
if (i === 3) {
dotClass = 'active-red';
stepClass = 'is-active-red';
} else {
dotClass = 'active';
stepClass = 'is-active';
}
icon = dotSVG;
} else {
return;
}
stepsHTML += `
<div class="step ${stepClass}">
<div class="step-dot ${dotClass}">${icon}</div>
<div class="step-content">
<h3>${step.title}</h3>
<div class="step-date">${step.date}</div>
<div class="step-desc">${step.desc}</div>
</div>
</div>
`;
});
let bannerHTML = '';
if (currentStage === 3) {
bannerHTML = `
<div class="status-banner red">
<svg width="20" height="20" fill="none" viewBox="0 0 20 20"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="1.5"/><path d="M10 6v4m0 4h.01" stroke="#ef4444" stroke-width="1.5" stroke-linecap="round"/></svg>
Acción necesaria — Contacta con tu vecino para recoger tu paquete.
</div>
`;
} else if (currentStage === 2) {
bannerHTML = `
<div class="status-banner orange">
<svg width="20" height="20" fill="none" viewBox="0 0 20 20"><circle cx="10" cy="10" r="9" stroke="#f59e0b" stroke-width="1.5"/><path d="M10 6v5l3 2" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
En tránsito — Entrega estimada en unos días.
</div>
`;
} else if (currentStage === 1) {
bannerHTML = `
<div class="status-banner orange">
<svg width="20" height="20" fill="none" viewBox="0 0 20 20"><circle cx="10" cy="10" r="9" stroke="#f59e0b" stroke-width="1.5"/><path d="M10 6v5l3 2" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
En preparación — Tu paquete será recogido pronto.
</div>
`;
} else {
bannerHTML = `
<div class="status-banner green">
<svg width="20" height="20" fill="none" viewBox="0 0 20 20"><circle cx="10" cy="10" r="9" stroke="#34d399" stroke-width="1.5"/><path d="M6 10l3 3 5-5" stroke="#34d399" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
Pedido registrado — Tu paquete será enviado próximamente.
</div>
`;
}
resultArea.innerHTML = `
<div class="result-card">
<div class="result-header">
<div class="result-header-left">
<h2>Detalle del seguimiento</h2>
<p>Pedido del ${formatDate(orderDate)}</p>
</div>
<div class="tracking-number">${trackNum}</div>
</div>
<div class="timeline">
${stepsHTML}
</div>
${bannerHTML}
</div>
`;
}
</script>
</body>
</html>