<style data-wp-block-html="css">
.home header,
.home .site-header,
.home nav,
.home footer { display: none !important; }
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>¡Ups! Me he mudado · Manuel Alba Villarejo</title>
<meta name="description" content="Este era mi portfolio antiguo. Mi web actual está en manualbaweb.com">
<meta name="robots" content="noindex, follow">
<link rel="canonical" href="https://manualbaweb.com">
<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>">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@600;700&family=Figtree:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #E4E7EB;
--bg-deep: #C9CED6;
--navy: #1E2B4A;
--navy-edge: #121B30;
--white: #FBFBF8;
--pen: #FFE45C;
--red: #E2483D;
--muted: #8A93A3;
--thick: 3px;
box-sizing: border-box;
padding-top: env(safe-area-inset-top, 0px);
padding-bottom: env(safe-area-inset-bottom, 0px);
}
*, *::before, *::after { box-sizing: inherit; }
html, body { height: 100%; margin: 0; }
body {
display: grid; place-items: center;
overflow: hidden;
font-family: "Figtree", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
color: var(--navy);
background: radial-gradient(ellipse at 50% 35%, #F3F5F7 0%, var(--bg) 45%, var(--bg-deep) 100%);
-webkit-tap-highlight-color: transparent;
}
.scene {
position: relative;
display: grid; justify-items: center; gap: 2.5rem;
perspective: 1100px;
width: 100%;
padding: 1.5rem;
}
/* ---------- tarjeta ---------- */
.card {
--w: min(86vw, 440px);
position: relative;
width: var(--w);
aspect-ratio: 85 / 55;
transform-style: preserve-3d;
cursor: grab;
touch-action: none;
outline: none;
will-change: transform;
}
.card:active { cursor: grabbing; }
.card:focus-visible .face { box-shadow: 0 0 0 3px var(--pen); }
.face, .edge {
position: absolute; inset: 0;
border-radius: 14px;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
.edge { background: var(--navy-edge); backface-visibility: visible; }
.face { overflow: hidden; }
.front {
transform: translateZ(calc(var(--thick) / 2));
background:
radial-gradient(circle at 110% -10%, rgba(255,255,255,.08) 0 40%, transparent 41%),
var(--navy);
color: var(--white);
padding: 7% 8%;
display: flex; flex-direction: column; justify-content: space-between;
}
.back {
transform: rotateY(180deg) translateZ(calc(var(--thick) / 2));
background: var(--white);
padding: 6% 8%;
display: flex; flex-direction: column; justify-content: center;
pointer-events: none;
}
.card.flipped .back { pointer-events: auto; }
/* brillo que se desliza con el giro */
.shine {
position: absolute; inset: 0; pointer-events: none;
background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.22) 45%, transparent 60%);
background-size: 250% 100%;
background-position: var(--shine, 50%) 0;
mix-blend-mode: screen;
}
.back .shine { mix-blend-mode: normal; background-image: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.7) 45%, transparent 60%); }
.mono {
width: 2.4rem; height: 2.4rem; border-radius: 50%;
border: 1.5px solid rgba(251,251,248,.55);
display: grid; place-items: center;
font-weight: 700; font-size: .8rem; letter-spacing: .02em;
}
.name { font-size: clamp(1.25rem, 4.6vw, 1.7rem); font-weight: 700; line-height: 1.1; margin: 0; }
.role { font-size: clamp(.8rem, 2.6vw, .95rem); color: rgba(251,251,248,.65); margin: .35rem 0 0; }
.url-row { position: relative; font-size: clamp(.8rem, 2.6vw, .95rem); }
.old-url { position: relative; display: inline-block; color: rgba(251,251,248,.8); }
.old-url::after { /* tachón */
content: ""; position: absolute; left: -4%; top: 52%;
width: 108%; height: 2px; background: var(--red);
transform: rotate(-3deg) scaleX(0); transform-origin: left;
border-radius: 2px;
animation: strike .45s ease-out 1.9s forwards;
}
@keyframes strike { to { transform: rotate(-3deg) scaleX(1); } }
.scribble {
position: absolute; left: 34%; bottom: 55%;
font-family: "Caveat", "Segoe Print", "Bradley Hand", cursive;
font-weight: 700; color: var(--pen);
font-size: clamp(1.25rem, 5vw, 1.8rem);
line-height: 1; white-space: nowrap;
transform: rotate(-7deg);
clip-path: inset(0 100% 0 0);
animation: write 1.1s steps(18) 2.35s forwards;
}
@keyframes write { to { clip-path: inset(0 0 0 0); } }
.ups {
font-family: "Caveat", "Segoe Print", "Bradley Hand", cursive;
font-weight: 700; font-size: clamp(1.8rem, 7vw, 2.5rem);
line-height: 1; margin: 0 0 .4rem;
}
.msg { font-size: clamp(.78rem, 2.5vw, .95rem); line-height: 1.45; margin: 0 0 .9rem; color: #3A4561; max-width: 34ch; }
.actions { display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; }
.cta {
display: inline-block;
padding: .6rem 1.05rem; border-radius: 999px;
background: var(--navy); color: var(--white);
font-weight: 700; font-size: clamp(.8rem, 2.5vw, .92rem);
text-decoration: none;
transition: transform .15s ease, background .15s ease;
}
.cta:hover { transform: scale(1.05); background: #2B3C66; }
.cta:focus-visible { outline: 3px solid var(--pen); outline-offset: 2px; }
.count { font-size: .78rem; color: var(--muted); }
.count button {
font: inherit; color: inherit; background: none; border: 0; padding: 0;
text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
/* sombra en el suelo */
.floor {
position: absolute; left: 50%; top: calc(50% + min(86vw, 440px) * 0.32);
width: min(70vw, 360px); height: 34px; margin-left: calc(min(70vw, 360px) / -2);
background: radial-gradient(ellipse at center, rgba(20, 28, 48, .35) 0%, transparent 70%);
filter: blur(6px);
transform: scaleX(var(--fs, 1)); opacity: var(--fo, 1);
pointer-events: none;
}
.hint {
font-size: .9rem; color: var(--muted); margin: 0;
opacity: 0; animation: fade .6s ease 3.4s forwards;
}
@keyframes fade { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
.old-url::after, .scribble, .hint { animation-duration: 0s; animation-delay: 0s; }
}
</style>
<div class="scene">
<div class="floor" id="floor"></div>
<div class="card" id="card" tabindex="0" role="button" aria-label="Tarjeta de visita. Pulsa para darle la vuelta.">
<!-- canto de la tarjeta -->
<div class="edge" style="transform: translateZ(-1px)"></div>
<div class="edge" style="transform: translateZ(0)"></div>
<div class="edge" style="transform: translateZ(1px)"></div>
<div class="face front">
<div class="mono">MAV</div>
<div>
<p class="name">Manuel Alba Villarejo</p>
<p class="role">Desarrollo web y diseño gráfico</p>
</div>
<div class="url-row">
<span class="old-url" id="oldUrl">portfolio-antiguo.com</span>
<span class="scribble" aria-hidden="true">¡me mudé! manualbaweb.com</span>
</div>
<div class="shine"></div>
</div>
<div class="face back">
<p class="ups">¡Ups! Tarjeta caducada.</p>
<p class="msg">Si vienes de mi currículum, no te has equivocado: el CV salió antes que la mudanza. Mi trabajo actual está en la web nueva.</p>
<div class="actions">
<a class="cta" id="cta" href="https://manualbaweb.com">Ver mi web actual</a>
<span class="count" id="count" aria-live="polite">Te llevo en <span id="sec">25</span> s. <button id="stay" type="button">Esperar</button></span>
</div>
<div class="shine"></div>
</div>
</div>
<p class="hint" id="hint">Toca la tarjeta o arrástrala para girarla</p>
</div>
<script>
(function () {
var TARGET = "https://manualbaweb.com";
var card = document.getElementById("card");
var floor = document.getElementById("floor");
var reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
// muestra el dominio real donde está alojada esta página
var host = location.hostname.replace(/^www\./, "");
if (host && host !== "localhost") document.getElementById("oldUrl").textContent = host;
// estado de giro (muelle)
var rot = reduce ? 0 : -540, vel = 0, target = 0;
var tiltX = 0, tiltY = 0, tTiltX = 0, tTiltY = 0;
var zoom = 0, leaving = false;
var dragging = false, startX = 0, startRot = 0, lastX = 0, lastT = 0, dragVel = 0, moved = 0;
function faceShown() { return Math.round(target / 180) % 2 !== 0; }
function updateFlipped() { card.classList.toggle("flipped", faceShown()); }
// inclinación hacia el puntero
addEventListener("pointermove", function (e) {
if (dragging) return;
tTiltY = ((e.clientX / innerWidth) - .5) * 18;
tTiltX = -((e.clientY / innerHeight) - .5) * 18;
});
document.addEventListener("pointerleave", function () { tTiltX = tTiltY = 0; });
// arrastrar para girar
card.addEventListener("pointerdown", function (e) {
if (e.target.closest("a, button")) return;
dragging = true; moved = 0;
startX = lastX = e.clientX; startRot = rot; lastT = performance.now();
card.setPointerCapture(e.pointerId);
});
card.addEventListener("pointermove", function (e) {
if (!dragging) return;
var now = performance.now();
moved = Math.max(moved, Math.abs(e.clientX - startX));
rot = startRot + (e.clientX - startX) * .6;
dragVel = (e.clientX - lastX) / Math.max(1, now - lastT) * 16 * .6;
lastX = e.clientX; lastT = now; vel = 0;
});
card.addEventListener("pointerup", function () {
if (!dragging) return;
dragging = false;
if (moved < 6) { target += 180; } // toque = voltear
else { target = Math.round((rot + dragVel * 8) / 180) * 180; vel = dragVel; }
updateFlipped();
});
card.addEventListener("keydown", function (e) {
if (e.target !== card) return;
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); target += 180; updateFlipped(); }
});
// bucle de animación
var t0 = performance.now();
function loop(now) {
var t = (now - t0) / 1000;
if (!dragging) {
vel += (target - rot) * (leaving ? .05 : .06);
vel *= leaving ? .9 : .82;
rot += vel;
}
tiltX += (tTiltX - tiltX) * .08;
tiltY += (tTiltY - tiltY) * .08;
var float = reduce ? 0 : Math.sin(t * 1.4) * 8;
var idle = reduce ? 0 : Math.sin(t * .9) * 3;
if (leaving) zoom += (1 - zoom) * .06;
card.style.transform =
"translateY(" + (float - zoom * 40) + "px) " +
"translateZ(" + (zoom * 900) + "px) " +
"rotateX(" + (tiltX + 8 + idle) + "deg) " +
"rotateY(" + (rot + tiltY) + "deg)";
card.style.opacity = leaving ? Math.max(0, 1 - zoom * 1.4) : 1;
// brillo según ángulo
var a = ((rot + tiltY) % 360 + 360) % 360;
card.style.setProperty("--shine", (100 - (a % 180) / 180 * 100) + "%");
floor.style.setProperty("--fs", (1 - float / 40).toFixed(3));
floor.style.setProperty("--fo", (1 - zoom).toFixed(3));
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
// salida: giro rápido hacia la cámara
function leave(e) {
if (e) e.preventDefault();
if (leaving) return;
leaving = true; clearInterval(timer);
if (reduce) { location.href = TARGET; return; }
target += 720;
setTimeout(function () { location.href = TARGET; }, 900);
}
document.getElementById("cta").addEventListener("click", leave);
// cuenta atrás
var s = 25, sec = document.getElementById("sec");
var timer = setInterval(function () { s--; sec.textContent = s; if (s <= 0) leave(); }, 1000);
document.getElementById("stay").addEventListener("click", function (e) {
e.stopPropagation(); clearInterval(timer);
document.getElementById("count").textContent = "Sin prisa.";
});
// si en 6 s nadie la gira, se da la vuelta sola
setTimeout(function () { if (!faceShown() && !leaving) { target += 180; updateFlipped(); } }, 6000);
})();
</script>