<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Samuel - Portfolio</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;1,400&family=Inter:wght@300;400&display=swap');
:root {
--mouse-x: 50%;
--mouse-y: 50%;
--px: 0;
--py: 0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body, html {
width: 100%;
height: 100vh;
overflow: hidden;
background-color: #fdfdfd;
font-family: 'Inter', sans-serif;
color: #111;
}
/* --- Background Layers --- */
.bg-layer {
position: absolute;
top: -5%;
left: -5%;
width: 110%;
height: 110%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transition: transform 0.3s ease-out;
transform: translate(calc(var(--px) * -15px), calc(var(--py) * -15px));
}
/* Base Image */
.layer-one {
/* REPLACE THIS WITH YOUR FIRST IMAGE URL OR LOCAL FILE PATH */
background-image: url('image-one.jpg');
z-index: 1;
}
/* Reveal Image */
.layer-two {
/* REPLACE THIS WITH YOUR SECOND IMAGE URL OR LOCAL FILE PATH */
background-image: url('image-two.jpg');
z-index: 2;
-webkit-mask-image: radial-gradient(circle 180px at var(--mouse-x) var(--mouse-y), black 60%, transparent 100%);
mask-image: radial-gradient(circle 180px at var(--mouse-x) var(--mouse-y), black 60%, transparent 100%);
}
/* --- Subtle Animated Grid --- */
.grid-overlay {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background-image:
linear-gradient(to right, rgba(0,0,0,0.04) 1px, transparent 1px),
linear-gradient(to bottom, rgba(0,0,0,0.04) 1px, transparent 1px);
background-size: 60px 60px;
z-index: 3;
pointer-events: none;
transform: translate(calc(var(--px) * 10px), calc(var(--py) * 10px));
}
/* --- UI Elements --- */
.ui-container {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
z-index: 10;
pointer-events: none;
padding: 40px;
display: flex;
justify-content: space-between;
}
.ui-element {
pointer-events: auto;
}
/* Top Left: Name */
.brand-name {
font-family: 'Playfair Display', serif;
font-size: 3.5rem;
line-height: 1.1;
font-weight: 600;
letter-spacing: -1px;
text-transform: uppercase;
}
/* Top Right: Nav */
.nav-link {
text-decoration: none;
color: #111;
font-weight: 500;
font-size: 1.1rem;
position: relative;
padding-bottom: 4px;
}
.nav-link::after {
content: '';
position: absolute;
bottom: 0; left: 0;
width: 100%; height: 2px;
background-color: #111;
transform: scaleX(0);
transform-origin: right;
transition: transform 0.3s ease;
}
.nav-link:hover::after {
transform: scaleX(1);
transform-origin: left;
}
/* Bottom Right: Socials */
.socials {
position: absolute;
bottom: 40px;
right: 40px;
display: flex;
gap: 20px;
}
.social-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px; height: 44px;
background: #111;
border-radius: 50%;
color: #fff;
text-decoration: none;
transition: transform 0.3s ease, background 0.3s ease;
}
.social-icon:hover {
transform: translateY(-4px);
background: #333;
}
.social-icon svg {
width: 20px; height: 20px;
fill: currentColor;
}
/* --- Cursor Echoes --- */
.echo {
position: absolute;
width: 360px; height: 360px;
background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 60%);
border-radius: 50%;
transform: translate(-50%, -50%) scale(1);
pointer-events: none;
z-index: 4;
animation: dissolve 0.5s ease-out forwards;
}
@keyframes dissolve {
0% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
}
</style>
</head>
<body>
<div class="bg-layer layer-one"></div>
<div class="bg-layer layer-two"></div>
<div class="grid-overlay"></div>
<div class="ui-container">
<div class="ui-element header-left">
<h1 class="brand-name">Samuel<br>Lastname</h1>
</div>
<div class="ui-element header-right">
<a href="#" class="nav-link">F1 Records</a>
</div>
<div class="ui-element socials">
<a href="#" class="social-icon" aria-label="Instagram">
<svg viewBox="0 0 24 24">
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/>
</svg>
</a>
<a href="#" class="social-icon" aria-label="X">
<svg viewBox="0 0 24 24">
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.005 4.15H5.059z"/>
</svg>
</a>
</div>
</div>
<script>
const root = document.documentElement;
let targetX = window.innerWidth / 2;
let targetY = window.innerHeight / 2;
let currentX = targetX;
let currentY = targetY;
let lastEchoX = currentX;
let lastEchoY = currentY;
document.addEventListener('mousemove', (e) => {
targetX = e.clientX;
targetY = e.clientY;
});
function animate() {
currentX += (targetX - currentX) * 0.12;
currentY += (targetY - currentY) * 0.12;
root.style.setProperty('--mouse-x', `${currentX}px`);
root.style.setProperty('--mouse-y', `${currentY}px`);
let px = (currentX / window.innerWidth - 0.5) * 2;
let py = (currentY / window.innerHeight - 0.5) * 2;
root.style.setProperty('--px', px);
root.style.setProperty('--py', py);
let dx = currentX - lastEchoX;
let dy = currentY - lastEchoY;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 30) {
spawnEcho(currentX, currentY);
lastEchoX = currentX;
lastEchoY = currentY;
}
requestAnimationFrame(animate);
}
function spawnEcho(x, y) {
const echo = document.createElement('div');
echo.className = 'echo';
echo.style.left = x + 'px';
echo.style.top = y + 'px';
document.body.appendChild(echo);
setTimeout(() => {
echo.remove();
}, 500);
}
animate();
</script>
</body>
</html>