Files
miles-web/redesign/spider.html
T

1924 lines
79 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MILES WOLF ALLEN</title>
<meta name="description" content="Miles Wolf Allen — Developer & 3D Artist. Games, tools, and websites built with craft and chaos.">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23F7FF00'/%3E%3Ctext x='50' y='72' font-size='60' font-family='Arial Black' font-weight='900' text-anchor='middle' fill='%23000'%3EM%3C/text%3E%3C/svg%3E">
<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=Archivo+Black&family=Press+Start+2P&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<script>document.documentElement.className='js';</script>
<style>
/* ─── RESET & TOKENS ─── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #08080a;
--bg2: #111116;
--bg3: #1c1c23;
--ink: #ffffff;
--muted: #72727c;
--dim: #2e2e38;
--pink: #FF2EA6;
--yellow: #F7FF00;
--cyan: #00F4FF;
--orange: #FF5C00;
--green: #00FF85;
--purple: #8A2BFF;
--red: #FF1F1F;
--rule: rgba(255,255,255,.08);
--serif: 'Archivo Black', Impact, sans-serif;
--mono: 'Space Mono', monospace;
--pixel: 'Press Start 2P', monospace;
}
html { scroll-behavior: auto; overflow-x: hidden; scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }
body {
background: var(--bg);
color: var(--ink);
font-family: var(--mono);
font-size: 14px;
line-height: 1.65;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
cursor: none;
}
::selection { background: var(--yellow); color: #000; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
/* ─── FIXED BG GRID ─── */
.bg-grid {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,.028) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
background-size: 48px 48px;
}
.noise {
position: fixed; inset: 0; z-index: 9000; pointer-events: none; opacity: .045;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.88' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
.vignette {
position: fixed; inset: 0; z-index: 1; pointer-events: none;
background: radial-gradient(ellipse at center, transparent 45%, rgba(0,0,0,.6) 100%);
}
</style>
<style>
/* ─── CUSTOM CURSOR ─── */
.c-dot {
position: fixed; top: 0; left: 0; z-index: 9500; pointer-events: none;
width: 12px; height: 12px; background: var(--yellow);
border: 2px solid #000; box-shadow: 3px 3px 0 #000;
transform: translate(-50%, -50%);
transition: width .2s, height .2s, border-radius .2s, background .2s;
will-change: left, top;
}
.c-ring {
position: fixed; top: 0; left: 0; z-index: 9499; pointer-events: none;
width: 44px; height: 44px;
border: 2px solid rgba(247,255,0,.4);
transform: translate(-50%, -50%);
transition: width .35s ease, height .35s ease, border-color .25s;
will-change: left, top;
}
body.cursor-hover .c-dot {
width: 52px; height: 52px; border-radius: 0;
background: var(--pink); box-shadow: 5px 5px 0 #000; border-width: 3px;
}
body.cursor-hover .c-ring { width: 0; height: 0; border-color: transparent; }
@media (pointer: coarse) { .c-dot, .c-ring { display: none; } body { cursor: auto; } }
/* ─── LOADER ─── */
.loader {
position: fixed; inset: 0; z-index: 8000;
background: var(--yellow);
display: flex; align-items: flex-end; justify-content: flex-start;
padding: clamp(24px,5vw,60px);
pointer-events: none;
overflow: hidden;
}
.loader::before {
content: 'LOADING';
position: absolute; top: clamp(24px,5vw,60px); right: clamp(24px,5vw,60px);
font-family: var(--pixel); font-size: clamp(8px,.7vw,12px); color: #000;
letter-spacing: .15em; opacity: .4;
}
.loader-count {
font-family: var(--serif); font-size: clamp(88px,20vw,220px);
font-weight: 900; color: #000; line-height: 1;
font-variant-numeric: tabular-nums;
position: relative; z-index: 2;
}
.loader-bar {
position: absolute; bottom: 0; left: 0;
height: 6px; width: 0; background: #000;
}
/* ink block that wipes over the yellow */
.loader-wipe {
position: absolute; inset: 0; background: var(--bg);
transform: scaleX(0); transform-origin: left;
z-index: 3;
}
.loader.done { display: none; }
/* ─── FIXED HEADER ─── */
header {
position: fixed; top: 0; left: 0; right: 0; z-index: 700;
padding: clamp(14px,2.2vw,24px) clamp(18px,4vw,52px);
display: flex; align-items: center; justify-content: space-between;
border-bottom: 3px solid #000;
background: rgba(8,8,10,.9);
backdrop-filter: blur(6px);
box-shadow: 0 3px 0 #000;
}
.h-logo {
font-family: var(--serif); font-size: clamp(16px,1.4vw,20px);
display: flex; align-items: center; gap: 9px; letter-spacing: .02em;
}
.h-logo-dot {
width: 10px; height: 10px; background: var(--green);
border: 2px solid #000; border-radius: 50%;
animation: blinkDot 1.6s steps(1) infinite;
}
@keyframes blinkDot { 50% { opacity: .1; } }
nav { display: flex; gap: 4px; }
nav a {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
text-transform: uppercase; letter-spacing: .05em;
padding: 9px 13px; border: 2px solid transparent;
color: rgba(255,255,255,.45);
transition: color .15s, border-color .15s, background .15s, transform .12s;
}
nav a:hover {
color: #000; background: var(--yellow); border-color: #000;
transform: translate(-2px,-2px); box-shadow: 3px 3px 0 #000;
}
.h-status {
display: flex; align-items: center; gap: 7px;
font-family: var(--pixel); font-size: clamp(6px,.52vw,8px);
text-transform: uppercase; letter-spacing: .07em; color: var(--yellow);
}
.h-status-dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--green);
box-shadow: 0 0 0 0 rgba(0,255,133,.4);
animation: statusPulse 2.2s ease infinite;
}
@keyframes statusPulse {
0% { box-shadow: 0 0 0 0 rgba(0,255,133,.4); }
55% { box-shadow: 0 0 0 7px rgba(0,255,133,0); }
100% { box-shadow: 0 0 0 0 rgba(0,255,133,0); }
}
@media (max-width: 700px) { nav { display: none; } }
</style>
<style>
/* ─── HERO ─── */
.hero {
position: relative; min-height: 100svh;
display: flex; flex-direction: column; justify-content: flex-end;
padding: 0 clamp(18px,4vw,52px) clamp(44px,7vh,80px);
overflow: hidden; z-index: 2;
}
/* scan-line flicker overlay */
.hero::after {
content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
background: repeating-linear-gradient(0deg, transparent, transparent 3px, rgba(0,0,0,.07) 3px, rgba(0,0,0,.07) 4px);
animation: scanFlicker 8s linear infinite;
}
@keyframes scanFlicker {
0%,100% { opacity: 1; } 92% { opacity: 1; } 93% { opacity: .6; } 94% { opacity: 1; }
}
/* big decorative rings */
.hero-orb {
position: absolute; top: 50%; right: 2vw;
transform: translateY(-52%);
width: clamp(340px, 52vw, 760px);
height: clamp(340px, 52vw, 760px);
pointer-events: none; z-index: 0;
}
.h-ring {
position: absolute; inset: 0; border-radius: 50%;
transition: transform 1.8s cubic-bezier(.16,1,.3,1), opacity 1.8s ease;
}
.h-ring-1 { border: 2px dashed rgba(255,255,255,.18); animation: spinR 38s linear infinite; }
.h-ring-2 { inset: 9%; border: 2px solid rgba(255,46,166,.5); animation: spinR 24s linear infinite reverse; }
.h-ring-3 { inset: 18%; border: 2px dashed rgba(0,244,255,.45); animation: spinR 52s linear infinite; }
.h-ring-core {
position: absolute; inset: 35%; border-radius: 50%;
background: radial-gradient(circle at 40% 40%, rgba(247,255,0,.25), transparent 70%);
animation: corePulse 3.2s ease-in-out infinite;
}
@keyframes spinR { to { transform: rotate(360deg); } }
@keyframes corePulse { 0%,100% { transform: scale(.9); opacity: .5; } 50% { transform: scale(1.1); opacity: 1; } }
@media (max-width: 800px) { .hero-orb { display: none; } }
/* chips row */
.hero-chips {
position: relative; z-index: 3;
display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 26px;
}
.chip {
font-family: var(--pixel); font-size: clamp(6px,.55vw,8px);
text-transform: uppercase; border: 2px solid #000;
padding: 8px 12px; box-shadow: 3px 3px 0 #000; color: #000;
}
.chip-y { background: var(--yellow); }
.chip-p { background: var(--pink); }
.chip-c { background: var(--cyan); }
/* label above title */
.hero-label {
position: absolute; top: clamp(80px,11vh,120px); left: clamp(18px,4vw,52px);
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
display: flex; align-items: center; gap: 10px; z-index: 3;
opacity: 0; transform: translateY(14px);
transition: opacity .8s ease .2s, transform .8s ease .2s;
}
.hero-label.in { opacity: 1; transform: none; }
.hero-label::before { content: ''; width: 24px; height: 2px; background: var(--yellow); }
/* THE NAME */
.hero-name {
position: relative; z-index: 3;
font-family: var(--serif); font-weight: 900;
font-size: clamp(70px,17vw,240px);
line-height: .86; letter-spacing: -.03em; text-transform: uppercase;
-webkit-text-stroke: 0;
}
.hero-name .word { display: block; overflow: hidden; }
.hero-name .word span {
display: block; transform: translateY(110%);
transition: transform 1.05s cubic-bezier(.16,1,.3,1);
will-change: transform;
}
.hero-name .word:nth-child(1) span { transition-delay: .05s; color: var(--yellow); -webkit-text-stroke: 3px #000; }
.hero-name .word:nth-child(2) span { transition-delay: .16s; color: transparent; -webkit-text-stroke: 2px #fff; }
.hero-name .word:nth-child(3) span { transition-delay: .27s; color: var(--pink); -webkit-text-stroke: 3px #000; }
html.js .hero-name.in .word span { transform: translateY(0); }
/* glitch pseudo */
.hero-name .word:nth-child(1) { position: relative; }
.hero-name .word:nth-child(1) span::before,
.hero-name .word:nth-child(1) span::after {
content: attr(data-text); position: absolute; left: 0; top: 0; pointer-events: none; display: block;
}
.hero-name .word:nth-child(1) span::before { color: var(--cyan); clip-path: polygon(0 30%,100% 30%,100% 50%,0 50%); animation: glA 5s steps(1) infinite 1.5s; }
.hero-name .word:nth-child(1) span::after { color: var(--red); clip-path: polygon(0 65%,100% 65%,100% 80%,0 80%); animation: glB 5s steps(1) infinite 1.5s; }
@keyframes glA {
0%,90%,100% { transform: none; opacity: 0; }
91% { transform: translateX(-4px); opacity: .9; }
92% { transform: translateX(4px); opacity: .9; }
93% { transform: none; opacity: 0; }
}
@keyframes glB {
0%,91%,100% { transform: none; opacity: 0; }
92% { transform: translateX(6px); opacity: .9; }
93% { transform: translateX(-2px); opacity: .9; }
94% { transform: none; opacity: 0; }
}
.hero-bottom {
position: relative; z-index: 3;
display: flex; align-items: flex-end; justify-content: space-between;
gap: 24px; margin-top: 36px; flex-wrap: wrap;
}
.hero-desc {
font-family: var(--mono); font-size: clamp(12px,1.1vw,15px);
color: var(--muted); max-width: 38ch; line-height: 1.8;
border-left: 4px solid var(--pink); padding-left: 16px;
opacity: 0; transform: translateY(22px);
transition: opacity .8s ease .5s, transform .8s ease .5s;
}
.hero-desc.in { opacity: 1; transform: none; }
.hero-desc b { color: #fff; }
.hero-cta {
display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
opacity: 0; transform: translateY(22px);
transition: opacity .8s ease .65s, transform .8s ease .65s;
}
.hero-cta.in { opacity: 1; transform: none; }
/* buttons */
.btn {
font-family: var(--pixel); font-size: clamp(8px,.7vw,10px);
text-transform: uppercase; letter-spacing: .05em;
padding: 14px 22px; border: 3px solid #000;
box-shadow: 5px 5px 0 #000; cursor: pointer;
transition: transform .12s, box-shadow .12s;
display: inline-flex; align-items: center; gap: 8px;
}
.btn:hover { transform: translate(-2px,-2px); box-shadow: 8px 8px 0 #000; }
.btn:active { transform: translate(3px,3px); box-shadow: 1px 1px 0 #000; }
.btn-pink { background: var(--pink); color: #000; }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.35); }
.btn-ghost:hover { background: #fff; color: #000; border-color: #000; box-shadow: 8px 8px 0 var(--pink); }
.hero-scroll {
position: absolute; bottom: clamp(22px,3.5vh,44px); left: 50%; transform: translateX(-50%);
display: flex; flex-direction: column; align-items: center; gap: 8px;
font-family: var(--pixel); font-size: 7px; letter-spacing: .14em; text-transform: uppercase;
color: var(--muted); z-index: 3; opacity: 0;
transition: opacity .8s ease 1s;
}
.hero-scroll.in { opacity: 1; }
.scroll-line {
width: 2px; height: 40px; background: linear-gradient(var(--pink), transparent);
transform-origin: top; animation: dropLine 2.2s cubic-bezier(.16,1,.3,1) infinite 1.4s;
}
@keyframes dropLine {
0% { transform: scaleY(0); opacity: 1; transform-origin: top; }
45% { transform: scaleY(1); opacity: 1; transform-origin: top; }
46% { transform-origin: bottom; }
100% { transform: scaleY(0); opacity: 0; transform-origin: bottom; }
}
</style>
<style>
/* ─── STATEMENT MARQUEE ─── */
.statement {
overflow: hidden; border-top: 4px solid #000; border-bottom: 4px solid #000;
position: relative; z-index: 2;
}
.mq-row { overflow: hidden; }
.mq-row + .mq-row { border-top: 3px solid #000; }
.mq-row-1 { background: var(--yellow); }
.mq-row-2 { background: var(--pink); }
.marquee-track {
display: flex; white-space: nowrap; align-items: center;
animation: mqFwd 20s linear infinite;
}
.marquee-track.rev { animation: mqRev 28s linear infinite; }
.marquee-item {
font-family: var(--serif); font-size: clamp(22px,4.5vw,60px);
font-weight: 900; text-transform: uppercase; letter-spacing: -.01em;
padding: clamp(12px,2vw,22px) clamp(20px,2.5vw,40px);
white-space: nowrap; color: #000;
}
.mq-star { font-size: clamp(16px,2.5vw,32px); color: #000; padding: 0 clamp(10px,1.5vw,22px); flex-shrink: 0; }
@keyframes mqFwd { to { transform: translateX(-50%); } }
@keyframes mqRev { 0% { transform: translateX(-50%); } 100% { transform: translateX(0); } }
/* ─── ABOUT ─── */
.about {
padding: clamp(70px,10vw,130px) clamp(18px,4vw,52px);
display: grid; grid-template-columns: 1fr 1fr;
gap: clamp(28px,4.5vw,70px); align-items: start;
border-bottom: 4px solid #000; position: relative; z-index: 2;
}
.about-eyebrow {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
letter-spacing: .12em; text-transform: uppercase; color: var(--yellow);
margin-bottom: 20px; display: flex; align-items: center; gap: 10px;
}
.about-eyebrow::before { content: '01 //'; opacity: .5; color: var(--muted); }
.about-heading {
font-family: var(--serif); font-size: clamp(44px,8vw,110px);
font-weight: 900; line-height: .88; letter-spacing: -.03em; text-transform: uppercase;
overflow: hidden;
}
.about-heading .line { overflow: hidden; }
.about-heading .line span {
display: block; transform: translateY(105%);
transition: transform 1.05s cubic-bezier(.16,1,.3,1);
}
.about-heading .line:nth-child(1) span { color: var(--yellow); -webkit-text-stroke: 2px #000; }
.about-heading .line:nth-child(2) span { color: transparent; -webkit-text-stroke: 2px #fff; transition-delay: .1s; }
.about-heading .line:nth-child(3) span { transition-delay: .2s; }
.about-heading.in .line span { transform: translateY(0); }
.about-number {
font-family: var(--serif); font-size: clamp(100px,18vw,200px);
font-weight: 900; color: transparent; -webkit-text-stroke: 1.5px rgba(255,255,255,.07);
line-height: .85; user-select: none; pointer-events: none; margin-top: -0.15em;
}
.about-right { padding-top: clamp(32px,5vw,64px); }
.about-photo-wrap { position: relative; margin-bottom: 28px; }
.about-photo {
width: 100%; aspect-ratio: 3/4; object-fit: cover; object-position: center top;
border: 5px solid #000; box-shadow: 12px 12px 0 var(--pink);
transform: rotate(-2deg) scale(.96); opacity: 0;
filter: grayscale(.1) contrast(1.1);
transition: transform 1.3s cubic-bezier(.16,1,.3,1), opacity 1.3s ease;
}
.about-photo.in { transform: rotate(-2deg) scale(1); opacity: 1; }
.about-photo:hover { transform: rotate(0) scale(1.02) !important; }
.about-photo-cap {
position: absolute; bottom: -6px; right: -6px;
font-family: var(--pixel); font-size: 7px; letter-spacing: .08em; text-transform: uppercase;
background: var(--yellow); color: #000; padding: 8px 12px;
border: 3px solid #000; box-shadow: 4px 4px 0 #000;
transform: rotate(2deg);
}
.about-body {
font-size: clamp(13px,1.1vw,15px); color: var(--muted); line-height: 1.9; max-width: 44ch;
opacity: 0; transform: translateY(24px);
transition: opacity .9s ease .2s, transform .9s ease .2s;
}
.about-body.in { opacity: 1; transform: none; }
.about-body b { color: #fff; font-weight: 700; }
.about-tags {
display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px;
opacity: 0; transition: opacity .8s ease .4s;
}
.about-tags.in { opacity: 1; }
.atag {
font-family: var(--pixel); font-size: 7px; letter-spacing: .06em; text-transform: uppercase;
border: 2px solid #000; padding: 8px 12px; box-shadow: 3px 3px 0 #000; color: #000;
}
.atag:nth-child(5n+1) { background: var(--yellow); }
.atag:nth-child(5n+2) { background: var(--pink); }
.atag:nth-child(5n+3) { background: var(--cyan); }
.atag:nth-child(5n+4) { background: var(--green); }
.atag:nth-child(5n) { background: var(--orange); }
@media (max-width: 768px) { .about { grid-template-columns: 1fr; } .about-number { font-size: 90px; } }
/* ─── STATS ─── */
.stats {
display: grid; grid-template-columns: repeat(4,1fr);
border-top: 4px solid #000;
position: relative; z-index: 2;
}
.stat {
padding: clamp(24px,4vw,48px) clamp(16px,2.5vw,32px);
border-right: 4px solid #000; position: relative; overflow: hidden;
transition: transform .15s, box-shadow .15s;
}
.stat:last-child { border-right: none; }
.stat:nth-child(1) { background: var(--pink); }
.stat:nth-child(2) { background: var(--yellow); }
.stat:nth-child(3) { background: var(--cyan); }
.stat:nth-child(4) { background: var(--orange); }
.stat-n {
font-family: var(--serif); font-size: clamp(40px,6.5vw,80px);
font-weight: 900; line-height: 1; display: block; color: #000; letter-spacing: -.04em;
}
.stat-l {
font-family: var(--pixel); font-size: clamp(6px,.55vw,8px);
letter-spacing: .08em; text-transform: uppercase;
color: #000; margin-top: 10px; display: block; opacity: .7;
}
.stat::after {
content: '★'; position: absolute; top: -10px; right: -10px;
width: 30px; height: 30px; background: #fff; border: 2px solid #000; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 13px; box-shadow: 2px 2px 0 #000; color: #000;
display: grid; place-items: center;
}
@media (max-width: 640px) { .stats { grid-template-columns: repeat(2,1fr); } .stat:nth-child(2) { border-right: 4px solid #000; } .stat:nth-child(even) { border-right: none; } }
</style>
<style>
/* ─── WORK / PROJECT ROWS ─── */
.work-header {
padding: clamp(14px,2vw,24px) clamp(18px,4vw,52px);
display: flex; align-items: center; justify-content: space-between;
border-top: 4px solid #000; border-bottom: 4px solid #000;
background: var(--bg2); position: relative; z-index: 2;
box-shadow: 0 4px 0 #000;
}
.work-header h2 {
font-family: var(--pixel); font-size: clamp(8px,.7vw,11px);
letter-spacing: .1em; text-transform: uppercase; color: #fff;
}
.work-count { font-family: var(--pixel); font-size: 8px; color: var(--yellow); letter-spacing: .06em; }
.proj-row {
display: grid; grid-template-columns: 70px 1fr auto auto;
align-items: center; gap: clamp(14px,2vw,36px);
padding: clamp(18px,2.8vw,36px) clamp(18px,4vw,52px);
border-bottom: 3px solid #000; position: relative; overflow: hidden;
cursor: pointer; background: var(--bg);
transition: padding-left .25s;
}
.proj-row::before {
content: ''; position: absolute; inset: 0;
background: var(--yellow); transform: scaleY(0); transform-origin: bottom;
transition: transform .5s cubic-bezier(.16,1,.3,1); z-index: 0;
}
.proj-row:nth-child(even)::before { background: var(--pink); }
.proj-row:nth-child(3n)::before { background: var(--cyan); }
.proj-row:hover { padding-left: calc(clamp(18px,4vw,52px) + 14px); }
.proj-row:hover::before { transform: scaleY(1); }
.proj-row:hover .pr-num,
.proj-row:hover .pr-name,
.proj-row:hover .pr-tags span,
.proj-row:hover .pr-year,
.proj-row:hover .pr-arrow { color: #000; }
.proj-row:hover .pr-tags span { border-color: rgba(0,0,0,.25); }
.proj-row > * { position: relative; z-index: 1; }
.pr-num {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
color: var(--muted); letter-spacing: .06em; transition: color .25s;
}
.pr-name {
font-family: var(--serif); font-size: clamp(22px,4.5vw,60px);
font-weight: 900; line-height: 1; letter-spacing: -.02em; text-transform: uppercase;
transition: color .25s;
}
.pr-right { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
.pr-tags { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.pr-tags span {
font-family: var(--pixel); font-size: 6px; letter-spacing: .05em; text-transform: uppercase;
color: var(--muted); border: 1px solid var(--dim); padding: 5px 8px;
transition: color .25s, border-color .25s;
}
.pr-year {
font-family: var(--pixel); font-size: clamp(6px,.52vw,8px);
color: var(--muted); transition: color .25s;
}
.pr-arrow { font-size: 20px; color: var(--yellow); transition: color .25s, transform .25s; }
.proj-row:hover .pr-arrow { transform: translate(4px,-4px); color: #000; }
@media (max-width: 640px) { .proj-row { grid-template-columns: 44px 1fr; } .pr-right { display: none; } }
/* ─── SKILLS ─── */
.skills {
padding: clamp(70px,10vw,130px) clamp(18px,4vw,52px);
display: grid; grid-template-columns: 280px 1fr; gap: clamp(28px,4.5vw,70px);
align-items: start; border-top: 4px solid #000; position: relative; z-index: 2;
}
.skills-eyebrow {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
letter-spacing: .12em; text-transform: uppercase; color: var(--yellow);
margin-bottom: 20px; display: flex; align-items: center; gap: 10px;
}
.skills-eyebrow::before { content: '02 //'; opacity: .5; color: var(--muted); }
.skills-heading {
font-family: var(--serif); font-size: clamp(36px,5.5vw,72px);
font-weight: 900; line-height: .88; letter-spacing: -.03em; text-transform: uppercase;
overflow: hidden;
}
.skills-heading .line { overflow: hidden; }
.skills-heading .line span {
display: block; transform: translateY(105%);
transition: transform 1.05s cubic-bezier(.16,1,.3,1);
}
.skills-heading .line:nth-child(1) span { color: var(--yellow); }
.skills-heading .line:nth-child(2) span { transition-delay: .1s; color: transparent; -webkit-text-stroke: 2px #fff; }
.skills-heading.in .line span { transform: translateY(0); }
.skills-note { font-size: 12px; color: var(--muted); line-height: 1.75; margin-top: 20px; }
.skill-group { margin-bottom: 32px; }
.skill-group-label {
font-family: var(--pixel); font-size: 8px; letter-spacing: .1em; text-transform: uppercase;
color: var(--yellow); margin-bottom: 14px; padding-bottom: 10px;
border-bottom: 2px solid var(--dim);
display: flex; align-items: center; gap: 8px;
}
.skill-list { display: flex; flex-wrap: wrap; gap: 8px; }
.skill-item {
font-family: var(--mono); font-weight: 700; font-size: 12px; text-transform: uppercase;
padding: 10px 14px; border: 3px solid #000; background: #fff; color: #000;
box-shadow: 4px 4px 0 #000; display: flex; align-items: center; gap: 8px;
transition: transform .12s, box-shadow .12s, background .12s;
}
.skill-item:nth-child(5n) { transform: rotate(-1.5deg); }
.skill-item:nth-child(5n+2) { transform: rotate(1.2deg); }
.skill-item:nth-child(5n+4) { transform: rotate(-.8deg); }
.skill-item:hover { transform: translate(-2px,-3px) rotate(0) !important; box-shadow: 8px 8px 0 var(--pink); }
.skill-item img { width: 16px; height: 16px; }
.skill-item.dim {
background: var(--bg3); color: #fff; border-style: dashed; box-shadow: none; opacity: .65;
transform: none !important;
}
.skill-item.dim:hover { background: var(--orange); color: #000; border-style: solid; box-shadow: 8px 8px 0 #000; opacity: 1; }
@media (max-width: 768px) { .skills { grid-template-columns: 1fr; } }
</style>
<style>
/* ─── DEVLOG HORIZONTAL SCROLL ─── */
.h-scroll-outer {
overflow: hidden; border-top: 4px solid #000;
padding: clamp(48px,7vw,88px) 0;
background: var(--bg2); position: relative; z-index: 2;
}
.h-scroll-inner {
display: flex; gap: clamp(24px,3vw,40px);
will-change: transform;
padding: 0 clamp(18px,4vw,52px);
}
.h-card {
flex: none; width: clamp(260px,28vw,360px);
border: 4px solid #000; padding: 28px;
background: var(--bg); box-shadow: 8px 8px 0 #000;
transition: transform .2s, box-shadow .2s;
position: relative;
}
.h-card:nth-child(odd) { transform: rotate(-1.2deg); }
.h-card:nth-child(even) { transform: rotate(.9deg) translateY(8px); }
.h-card:hover { transform: rotate(0) translate(-3px,-3px) !important; box-shadow: 12px 12px 0 var(--pink); }
/* tape strip */
.h-card::before {
content: ''; position: absolute; top: -8px; right: 22px;
width: 54px; height: 18px; background: var(--yellow); opacity: .85;
transform: rotate(2deg); box-shadow: 0 0 0 2px #000;
}
.h-card-num {
font-family: var(--pixel); font-size: 8px; color: var(--muted); letter-spacing: .08em; margin-bottom: 16px;
}
.h-card-title {
font-family: var(--serif); font-size: clamp(17px,2vw,24px);
font-weight: 900; color: #fff; line-height: 1.1; margin-bottom: 12px;
letter-spacing: -.01em; text-transform: uppercase;
}
.h-card-body { font-size: 12px; color: var(--muted); line-height: 1.8; }
.h-card-tag {
display: inline-flex; align-items: center; gap: 6px; margin-top: 18px;
font-family: var(--pixel); font-size: 7px; letter-spacing: .07em; text-transform: uppercase;
color: var(--yellow); border-bottom: 2px solid var(--yellow); padding-bottom: 2px;
transition: color .2s, border-color .2s;
}
.h-card:hover .h-card-tag { color: var(--pink); border-color: var(--pink); }
/* ─── CONTACT ─── */
.contact {
padding: clamp(70px,10vw,130px) clamp(18px,4vw,52px) clamp(44px,6vw,80px);
border-top: 4px solid #000;
display: grid; grid-template-columns: 1fr 1fr; gap: clamp(36px,5vw,90px);
position: relative; z-index: 2;
}
.contact-eyebrow {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
letter-spacing: .12em; text-transform: uppercase; color: var(--yellow);
margin-bottom: 20px; display: flex; align-items: center; gap: 10px;
}
.contact-eyebrow::before { content: '03 //'; opacity: .5; color: var(--muted); }
.contact-heading {
font-family: var(--serif); font-size: clamp(50px,10vw,130px);
font-weight: 900; line-height: .86; letter-spacing: -.04em; text-transform: uppercase;
overflow: hidden;
}
.contact-heading .line { overflow: hidden; }
.contact-heading .line span {
display: block; transform: translateY(105%);
transition: transform 1.05s cubic-bezier(.16,1,.3,1);
}
.contact-heading .line:nth-child(1) span { color: var(--yellow); -webkit-text-stroke: 2px #000; }
.contact-heading .line:nth-child(2) span { transition-delay: .12s; color: transparent; -webkit-text-stroke: 2px #fff; }
.contact-heading.in .line span { transform: translateY(0); }
.contact-right { padding-top: clamp(20px,3vw,48px); }
.contact-link {
display: flex; align-items: center; justify-content: space-between;
padding: clamp(14px,1.8vw,24px) clamp(12px,1.5vw,20px);
border: 3px solid #000; border-bottom: none; background: var(--bg2);
box-shadow: 4px 4px 0 #000; margin-bottom: 0;
transition: background .18s, padding-left .22s;
position: relative;
}
.contact-link:first-child { border-top: 3px solid #000; }
.contact-link:last-child { border-bottom: 3px solid #000; margin-bottom: 0; }
.contact-link:hover { background: var(--pink); padding-left: calc(clamp(12px,1.5vw,20px) + 14px); }
.contact-link:hover .cl-label,
.contact-link:hover .cl-val,
.contact-link:hover .cl-arrow { color: #000; }
.cl-label { font-family: var(--pixel); font-size: 7px; letter-spacing: .1em; text-transform: uppercase; color: var(--yellow); transition: color .18s; }
.cl-val { font-family: var(--serif); font-size: clamp(14px,1.8vw,20px); font-weight: 700; color: #fff; transition: color .18s; }
.cl-arrow { font-size: 18px; color: var(--pink); transition: color .18s, transform .2s; }
.contact-link:hover .cl-arrow { transform: translate(4px,-4px); }
@media (max-width: 768px) { .contact { grid-template-columns: 1fr; } }
/* ─── FOOTER ─── */
.footer-bottom {
padding: clamp(20px,2.5vw,36px) clamp(18px,4vw,52px);
border-top: 4px solid #000;
display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
background: #000; position: relative; z-index: 2;
}
.footer-word {
font-family: var(--serif); font-size: clamp(60px,16vw,220px);
font-weight: 900; text-transform: uppercase; color: transparent;
-webkit-text-stroke: 2px rgba(255,255,255,.09);
user-select: none; pointer-events: none;
text-align: center; display: block;
border-top: 4px solid #000; padding: clamp(20px,3vw,48px) 0 0;
position: relative; z-index: 2;
}
.footer-copy { font-family: var(--pixel); font-size: 8px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.footer-links { display: flex; gap: 16px; flex-wrap: wrap; }
.footer-links a { font-family: var(--pixel); font-size: 8px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); transition: color .2s; }
.footer-links a:hover { color: var(--yellow); }
/* ─── REVEAL HELPERS ─── */
.reveal-up { opacity: 0; transform: translateY(36px); transition: opacity .9s ease, transform .9s cubic-bezier(.16,1,.3,1); }
.reveal-up.in { opacity: 1; transform: none; }
.reveal-delay-1 { transition-delay: .12s; }
.reveal-delay-2 { transition-delay: .24s; }
/* ─── REDUCED MOTION ─── */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001s !important; transition-duration: .001s !important; }
.hero-name .word span,
.about-heading .line span,
.skills-heading .line span,
.contact-heading .line span { transform: none !important; }
.loader { display: none; }
}
/* ══════════════════════════════════════
DEV ACRONYM — PINNED SCROLL SECTION
══════════════════════════════════════ */
.dev-outer {
position: relative; z-index: 2;
border-top: 4px solid #000;
}
/* sticky container that holds the whole sequence */
.dev-sticky {
position: sticky; top: 0;
height: 100vh; overflow: hidden;
display: flex; align-items: stretch;
}
/* left column — big letter */
.dev-letter-col {
position: relative; flex: 0 0 clamp(120px, 22vw, 320px);
border-right: 4px solid #000;
display: flex; align-items: flex-end;
padding: 0 0 clamp(28px,4vw,52px) clamp(20px,3vw,40px);
overflow: hidden;
}
.dev-letter {
font-family: var(--serif);
font-size: clamp(200px, 36vw, 500px);
line-height: .8; font-weight: 900; letter-spacing: -.04em;
color: transparent;
-webkit-text-stroke: 3px rgba(255,255,255,.12);
position: absolute; bottom: -.1em; left: -.05em;
pointer-events: none; user-select: none;
transition: -webkit-text-stroke-color .4s, color .4s;
}
/* one colour per panel */
.dev-panel[data-letter="D"] .dev-letter { -webkit-text-stroke-color: var(--yellow); }
.dev-panel[data-letter="E"] .dev-letter { -webkit-text-stroke-color: var(--pink); }
.dev-panel[data-letter="V"] .dev-letter { -webkit-text-stroke-color: var(--cyan); }
.dev-active-letter {
font-family: var(--serif);
font-size: clamp(140px,24vw,320px);
line-height: 1; font-weight: 900; letter-spacing: -.04em;
position: relative; z-index: 2;
/* colour set per active panel via JS class */
}
.dev-active-letter.col-d { color: var(--yellow); -webkit-text-stroke: 3px #000; }
.dev-active-letter.col-e { color: var(--pink); -webkit-text-stroke: 3px #000; }
.dev-active-letter.col-v { color: var(--cyan); -webkit-text-stroke: 3px #000; }
/* step progress dots */
.dev-dots {
position: absolute; top: 50%; right: -22px; transform: translateY(-50%);
display: flex; flex-direction: column; gap: 10px; z-index: 10;
}
.dev-dot {
width: 10px; height: 10px; border: 2px solid #000; background: var(--dim);
box-shadow: 2px 2px 0 #000; transition: background .25s, transform .2s;
}
.dev-dot.active { background: var(--yellow); transform: scale(1.4); }
/* right column — content */
.dev-content-col {
flex: 1; position: relative; overflow: hidden;
display: flex; flex-direction: column; justify-content: flex-end;
padding: clamp(28px,4vw,52px) clamp(24px,4vw,60px);
}
/* panels — stacked, revealed via clip-path */
.dev-panel {
position: absolute; inset: 0;
display: flex; flex-direction: column; justify-content: flex-end;
padding: clamp(28px,4vw,52px) clamp(24px,4vw,60px);
clip-path: inset(0 0 100% 0);
transition: clip-path .0s; /* GSAP handles this */
}
.dev-panel[data-letter="D"] { background: var(--bg2); }
.dev-panel[data-letter="E"] { background: var(--bg3); }
.dev-panel[data-letter="V"] { background: #0a0a10; }
/* eyebrow */
.dev-eyebrow {
font-family: var(--pixel); font-size: clamp(7px,.58vw,9px);
text-transform: uppercase; letter-spacing: .12em;
display: flex; align-items: center; gap: 10px; margin-bottom: 20px;
}
.dev-panel[data-letter="D"] .dev-eyebrow { color: var(--yellow); }
.dev-panel[data-letter="E"] .dev-eyebrow { color: var(--pink); }
.dev-panel[data-letter="V"] .dev-eyebrow { color: var(--cyan); }
.dev-eyebrow::before { content: attr(data-n); color: var(--muted); opacity: .5; }
/* word expansion */
.dev-word {
font-family: var(--serif);
font-size: clamp(38px, 7.5vw, 100px);
line-height: .88; letter-spacing: -.03em; text-transform: uppercase;
overflow: hidden; margin-bottom: 28px;
}
.dev-word-inner {
display: block; transform: translateY(105%);
transition: transform 0s; /* driven by GSAP */
}
.dev-panel[data-letter="D"] .dev-word-inner { color: var(--yellow); }
.dev-panel[data-letter="E"] .dev-word-inner { color: var(--pink); }
.dev-panel[data-letter="V"] .dev-word-inner { color: var(--cyan); }
/* body copy */
.dev-body {
font-family: var(--mono); font-size: clamp(12px,1.05vw,15px);
color: var(--muted); line-height: 1.85; max-width: 56ch;
margin-bottom: 28px; opacity: 0;
}
.dev-body b { color: #fff; font-weight: 700; }
/* tags */
.dev-tags { display: flex; gap: 8px; flex-wrap: wrap; opacity: 0; }
.dev-tag {
font-family: var(--pixel); font-size: 7px; text-transform: uppercase;
letter-spacing: .05em; padding: 7px 11px;
border: 2px solid #000; box-shadow: 3px 3px 0 #000; color: #000;
}
.dev-panel[data-letter="D"] .dev-tag { background: var(--yellow); }
.dev-panel[data-letter="E"] .dev-tag { background: var(--pink); }
.dev-panel[data-letter="V"] .dev-tag { background: var(--cyan); }
/* side decorative number */
.dev-side-num {
position: absolute; top: clamp(20px,3vw,40px); right: clamp(20px,3vw,44px);
font-family: var(--serif);
font-size: clamp(80px,14vw,180px); font-weight: 900;
color: transparent;
-webkit-text-stroke: 1.5px rgba(255,255,255,.06);
pointer-events: none; user-select: none; line-height: 1;
}
/* progress bar at very bottom of sticky viewport */
.dev-progress-bar {
position: absolute; bottom: 0; left: 0; height: 4px;
background: linear-gradient(90deg, var(--yellow), var(--pink), var(--cyan));
width: 0; z-index: 20;
box-shadow: 0 0 8px var(--pink);
}
/* non-sticky scroll track — height controls how long the pin lasts */
.dev-scroll-track {
/* height is set by JS to 300vh (3 panels × 100vh) */
}
/* ── responsive ── */
@media (max-width: 640px) {
.dev-letter-col { display: none; }
.dev-dots { right: 12px; top: 50%; transform: translateY(-50%); }
.dev-word { font-size: clamp(32px,9vw,60px); }
}
</style>
<style>
/* ══════════════════════════════════════
SPIDER-VERSE SPRAY-PAINT PASS
halftone dot screens, CMYK misprints, splatter
══════════════════════════════════════ */
/* fixed spray layer: colour splats + fine halftone screen */
.spray {
position: fixed; inset: 0; z-index: 2; pointer-events: none;
mix-blend-mode: screen;
background:
radial-gradient(circle at 84% 10%, rgba(255,46,166,.16) 0, transparent 20%),
radial-gradient(circle at 10% 64%, rgba(0,244,255,.13) 0, transparent 16%),
radial-gradient(circle at 95% 82%, rgba(247,255,0,.12) 0, transparent 14%),
radial-gradient(circle at 40% 18%, rgba(0,255,133,.10) 0, transparent 12%),
radial-gradient(circle at 58% 94%, rgba(255,92,0,.12) 0, transparent 15%),
radial-gradient(rgba(255,255,255,.05) 1px, transparent 1.5px);
background-size: auto, auto, auto, auto, auto, 9px 9px;
}
@media (prefers-reduced-motion: reduce) { .spray { display: none; } }
/* stencil stamp slapped on the hero */
.stamp {
position: absolute; top: clamp(78px,10vh,112px); right: clamp(18px,4vw,52px);
z-index: 3;
font-family: var(--pixel); font-size: clamp(6px,.52vw,8px);
letter-spacing: .1em; text-transform: uppercase; color: #000;
background: var(--yellow);
border: 3px solid #000; box-shadow: 4px 4px 0 #000;
padding: 9px 13px; transform: rotate(2.5deg);
}
@media (max-width: 640px) { .stamp { display: none; } }
/* Ben-Day dot wash behind the hero name */
.hero::before {
content: ''; position: absolute; z-index: 0; pointer-events: none;
left: clamp(18px,4vw,52px); right: 10%; bottom: 20%;
height: clamp(140px,24vw,300px);
background-image:
radial-gradient(rgba(247,255,0,.5) 2.4px, transparent 2.9px),
radial-gradient(rgba(255,46,166,.38) 2.4px, transparent 2.9px);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;
-webkit-mask-image: linear-gradient(180deg, transparent, #000 30%, #000 72%, transparent);
mask-image: linear-gradient(180deg, transparent, #000 30%, #000 72%, transparent);
opacity: .55;
}
/* CMYK misprint offset on the name */
.hero-name .word:nth-child(1) span {
filter: drop-shadow(3px 4px 0 rgba(0,244,255,.85)) drop-shadow(-3px -4px 0 rgba(255,46,166,.6));
}
.hero-name .word:nth-child(2) span {
filter: drop-shadow(3px 4px 0 rgba(255,46,166,.55)) drop-shadow(-3px -4px 0 rgba(0,244,255,.55));
}
.hero-name .word:nth-child(3) span {
filter: drop-shadow(3px 4px 0 rgba(247,255,0,.8)) drop-shadow(-3px -4px 0 rgba(0,244,255,.6));
}
/* off-kilter sticker chips + printed dots */
.chip-y { transform: rotate(-2deg); }
.chip-p { transform: rotate(1.5deg); }
.chip-c { transform: rotate(-1deg); }
.chip {
background-image: radial-gradient(rgba(0,0,0,.15) 1px, transparent 1.4px);
background-size: 8px 8px;
}
/* marquee rows print like comic paper */
.mq-row { position: relative; }
.mq-row::after {
content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
background-image:
radial-gradient(rgba(0,0,0,.25) 1.6px, transparent 2px),
radial-gradient(rgba(255,255,255,.08) 1.6px, transparent 2px);
background-size: 13px 13px, 13px 13px;
background-position: 0 0, 6.5px 6.5px;
}
.mq-row .marquee-track { position: relative; z-index: 2; }
/* heading misprints */
.about-heading .line:nth-child(1) span {
filter: drop-shadow(3px 4px 0 rgba(0,244,255,.8)) drop-shadow(-3px -4px 0 rgba(255,46,166,.55));
}
.about-heading .line:nth-child(2) span {
filter: drop-shadow(3px 4px 0 rgba(255,46,166,.5)) drop-shadow(-3px -4px 0 rgba(0,244,255,.5));
}
.about-heading .line:nth-child(3) span {
filter: drop-shadow(3px 4px 0 rgba(247,255,0,.7)) drop-shadow(-3px -4px 0 rgba(0,244,255,.5));
}
.skills-heading .line:nth-child(1) span {
filter: drop-shadow(3px 4px 0 rgba(0,244,255,.75)) drop-shadow(-3px -4px 0 rgba(255,46,166,.5));
}
.skills-heading .line:nth-child(2) span {
filter: drop-shadow(3px 4px 0 rgba(255,46,166,.5)) drop-shadow(-3px -4px 0 rgba(0,244,255,.5));
}
.contact-heading .line:nth-child(1) span {
filter: drop-shadow(3px 4px 0 rgba(0,244,255,.8)) drop-shadow(-3px -4px 0 rgba(255,46,166,.55));
}
.contact-heading .line:nth-child(2) span {
filter: drop-shadow(3px 4px 0 rgba(255,46,166,.5)) drop-shadow(-3px -4px 0 rgba(0,244,255,.5));
}
/* stat tiles read as printed comic panels */
.stat:nth-child(1),
.stat:nth-child(2),
.stat:nth-child(3),
.stat:nth-child(4) {
background-image:
radial-gradient(rgba(0,0,0,.2) 1.6px, transparent 2px),
radial-gradient(rgba(255,255,255,.1) 1.6px, transparent 2px);
background-size: 15px 15px, 15px 15px;
background-position: 0 0, 7.5px 7.5px;
}
/* work header spray + dots */
.work-header {
background-image:
radial-gradient(circle at 96% 50%, rgba(255,46,166,.18) 0 80px, transparent 130px),
radial-gradient(rgba(255,255,255,.06) 1.3px, transparent 1.7px);
background-size: auto, 14px 14px;
}
/* devlog rail gets a printed wash */
.h-scroll-outer {
background-image:
radial-gradient(circle at 8% 50%, rgba(0,244,255,.10) 0 90px, transparent 140px),
radial-gradient(rgba(255,255,255,.05) 1.3px, transparent 1.7px);
background-size: auto, 14px 14px;
}
/* about photo caption reads as a sticker */
.about-photo-cap {
background-image: radial-gradient(rgba(0,0,0,.15) 1px, transparent 1.4px);
background-size: 8px 8px;
}
/* giant DEV letter gets a sprayed halftone fill */
.dev-letter {
background-image: radial-gradient(rgba(255,255,255,.16) 2px, transparent 2.6px);
background-size: 20px 20px;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
.dev-letter-col {
background-image: radial-gradient(rgba(255,255,255,.06) 1.4px, transparent 1.8px);
background-size: 16px 16px;
}
.dev-active-letter {
filter: drop-shadow(4px 5px 0 rgba(247,255,0,.5)) drop-shadow(-3px -3px 0 rgba(255,46,166,.45));
}
.dev-panel::after {
content: ''; position: absolute; inset: 0; pointer-events: none;
background-image:
radial-gradient(rgba(255,255,255,.05) 1.3px, transparent 1.7px);
background-size: 12px 12px;
}
.dev-panel[data-letter="D"]::after {
background-image:
radial-gradient(circle at 82% 12%, rgba(247,255,0,.16) 0 90px, transparent 130px),
radial-gradient(circle at 12% 88%, rgba(0,244,255,.12) 0 70px, transparent 100px),
radial-gradient(rgba(255,255,255,.05) 1.3px, transparent 1.7px);
background-size: auto, auto, 12px 12px;
}
.dev-panel[data-letter="E"]::after {
background-image:
radial-gradient(circle at 82% 12%, rgba(255,46,166,.16) 0 90px, transparent 130px),
radial-gradient(circle at 12% 88%, rgba(247,255,0,.12) 0 70px, transparent 100px),
radial-gradient(rgba(255,255,255,.05) 1.3px, transparent 1.7px);
background-size: auto, auto, 12px 12px;
}
.dev-panel[data-letter="V"]::after {
background-image:
radial-gradient(circle at 82% 12%, rgba(0,244,255,.16) 0 90px, transparent 130px),
radial-gradient(circle at 12% 88%, rgba(255,46,166,.12) 0 70px, transparent 100px),
radial-gradient(rgba(255,255,255,.05) 1.3px, transparent 1.7px);
background-size: auto, auto, 12px 12px;
}
.dev-panel[data-letter="D"] .dev-word-inner { filter: drop-shadow(3px 4px 0 rgba(0,244,255,.6)); }
.dev-panel[data-letter="E"] .dev-word-inner { filter: drop-shadow(3px 4px 0 rgba(247,255,0,.6)); }
.dev-panel[data-letter="V"] .dev-word-inner { filter: drop-shadow(3px 4px 0 rgba(255,46,166,.6)); }
/* footer word: spray behind + misprint */
.footer-word {
background-image:
radial-gradient(circle at 30% 60%, rgba(255,46,166,.10) 0 90px, transparent 140px),
radial-gradient(circle at 70% 40%, rgba(0,244,255,.10) 0 90px, transparent 140px);
filter: drop-shadow(4px 6px 0 rgba(255,46,166,.35)) drop-shadow(-4px -6px 0 rgba(0,244,255,.3));
}
</style>
</head>
<body>
<!-- fixed layers -->
<div class="bg-grid" aria-hidden="true"></div>
<div class="noise" aria-hidden="true"></div>
<div class="vignette" aria-hidden="true"></div>
<div class="spray" aria-hidden="true"></div>
<!-- cursor -->
<div class="c-dot" id="cDot" aria-hidden="true"></div>
<div class="c-ring" id="cRing" aria-hidden="true"></div>
<!-- loader -->
<div class="loader" id="loader" aria-hidden="true">
<div class="loader-count" id="loaderCount">0</div>
<div class="loader-bar" id="loaderBar"></div>
<div class="loader-wipe" id="loaderWipe"></div>
</div>
<!-- header -->
<header>
<a class="h-logo" href="#hero">
<span class="h-logo-dot"></span>MILES
</a>
<nav>
<a href="#about">About</a>
<a href="#work">Builds</a>
<a href="#skills">Skills</a>
<a href="#contact">Contact</a>
</nav>
<div class="h-status">
<span class="h-status-dot"></span>USUALLY ONLINE
</div>
</header>
<!-- ══════════════ HERO ══════════════ -->
<section class="hero" id="hero">
<div class="hero-orb" aria-hidden="true">
<div class="h-ring h-ring-1"></div>
<div class="h-ring h-ring-2"></div>
<div class="h-ring h-ring-3"></div>
<div class="h-ring-core"></div>
</div>
<div class="hero-label" id="heroLabel">Developer &amp; 3D Artist — Est. 2008</div>
<div class="hero-chips">
<span class="chip chip-y">★ MILES WOLF ALLEN</span>
<span class="chip chip-p">DEVELOPER &amp; 3D ARTIST</span>
<span class="chip chip-c">DISKETTE LABS</span>
</div>
<span class="stamp" aria-hidden="true">SPRAY-PAINTED BY MILES ✦</span>
<h1 class="hero-name" id="heroName" aria-label="Miles Wolf Allen">
<span class="word"><span data-text="MILES">MILES</span></span>
<span class="word"><span data-text="WOLF">WOLF</span></span>
<span class="word"><span data-text="ALLEN">ALLEN</span></span>
</h1>
<div class="hero-bottom">
<p class="hero-desc" id="heroDesc">
I make <b>games</b>, <b>tools</b>, and <b>websites</b> — with craft
and a little chaos. Co-founder of <b>Diskette Labs</b>.
160 repos and counting. Send help.
</p>
<div class="hero-cta" id="heroCta">
<a class="btn btn-pink" href="#work">SEE THE BUILDS ↓</a>
<a class="btn btn-ghost" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener">GITHUB ↗</a>
</div>
</div>
<div class="hero-scroll" id="heroScroll" aria-hidden="true">
<span class="scroll-line"></span>
SCROLL
</div>
</section>
<!-- ══════════════ MARQUEE ══════════════ -->
<div class="statement" aria-hidden="true">
<div class="mq-row mq-row-1">
<div class="marquee-track" id="mq1"></div>
</div>
<div class="mq-row mq-row-2">
<div class="marquee-track rev" id="mq2"></div>
</div>
</div>
<!-- ══════════════ ABOUT ══════════════ -->
<section class="about" id="about">
<div class="about-left">
<div class="about-number" aria-hidden="true">01</div>
<div class="about-eyebrow reveal-up">About</div>
<h2 class="about-heading" id="aboutHeading">
<span class="line"><span>BUILD.</span></span>
<span class="line"><span>BREAK.</span></span>
<span class="line"><span>REPEAT.</span></span>
</h2>
</div>
<div class="about-right">
<div class="about-photo-wrap">
<img class="about-photo" id="aboutPhoto"
src="https://avatars.githubusercontent.com/u/115095719?v=4"
alt="Miles Wolf Allen" width="480" height="640" loading="lazy">
<span class="about-photo-cap">@mileswolfallen2</span>
</div>
<p class="about-body" id="aboutBody">
I'm <b>Miles Wolf Allen</b> — developer &amp; 3D artist, still in school,
co-founder of <b>Diskette Labs</b>, and currently building
<b>Gelectron</b>: a drop-in Electron replacement using native web views
instead of bundling an entire Chromium. Pancakes &gt; waffles.
Cereal is soup. Software should feel good to use.
</p>
<div class="about-tags" id="aboutTags">
<span class="atag">PULL SHARK ×2</span>
<span class="atag">YOLO</span>
<span class="atag">QUICKDRAW</span>
<span class="atag">GD ADDICT</span>
<span class="atag">MIT LICENSED</span>
</div>
</div>
</section>
<!-- ══════════════ STATS ══════════════ -->
<div class="stats" id="stats">
<div class="stat">
<span class="stat-n num" data-count="160">0</span>
<span class="stat-l">REPOS ONLINE</span>
</div>
<div class="stat">
<span class="stat-n num" data-count="91">0</span>
<span class="stat-l">STARS EARNED</span>
</div>
<div class="stat">
<span class="stat-n num" data-count="3839">0</span>
<span class="stat-l">FEDL VERIFIED RUNS</span>
</div>
<div class="stat">
<span class="stat-n num" data-count="1542">0</span>
<span class="stat-l">MOON GAMING COMMITS</span>
</div>
</div>
<!-- ══════════════ DEV ACRONYM ══════════════ -->
<div class="dev-outer" id="devSection">
<!-- non-sticky height track — JS sets height to 300vh -->
<div class="dev-scroll-track" id="devTrack">
<div class="dev-sticky" id="devSticky">
<!-- left: big active letter -->
<div class="dev-letter-col">
<div class="dev-active-letter col-d" id="devActiveLetter">D</div>
<!-- step dots -->
<div class="dev-dots" id="devDots">
<div class="dev-dot active" data-i="0"></div>
<div class="dev-dot" data-i="1"></div>
<div class="dev-dot" data-i="2"></div>
</div>
</div>
<!-- right: panels -->
<div class="dev-content-col" id="devContentCol">
<!-- progress bar -->
<div class="dev-progress-bar" id="devProgressBar"></div>
<!-- ─ D ─ -->
<div class="dev-panel" data-letter="D" id="devPanelD">
<div class="dev-side-num" aria-hidden="true">01</div>
<div class="dev-eyebrow" data-n="01 // ">Dedicated</div>
<div class="dev-word"><span class="dev-word-inner">DEDI&shy;CATED</span></div>
<p class="dev-body">
Since age 10 I've been building things online — not because anyone asked,
but because I couldn't stop. <b>160 GitHub repos</b>, a GD demon list with
<b>3,800+ verified runs</b>, a music app, a rhythm game, and a co-founded studio —
all before finishing high school. Dedication isn't a trait, it's a default setting.
</p>
<div class="dev-tags">
<span class="dev-tag">160 repos</span>
<span class="dev-tag">Est. 2008</span>
<span class="dev-tag">Pull Shark ×2</span>
</div>
</div>
<!-- ─ E ─ -->
<div class="dev-panel" data-letter="E" id="devPanelE">
<div class="dev-side-num" aria-hidden="true">02</div>
<div class="dev-eyebrow" data-n="02 // ">Experimental</div>
<div class="dev-word"><span class="dev-word-inner">EXPERI&shy;MENTAL</span></div>
<p class="dev-body">
I don't ship the first idea — I ship the <b>weird</b> one. Gelectron exists because
I thought "what if Electron but without bundling an entire browser?" Nova AI exists
because I wanted local AI that doesn't phone home. OmniEmu exists because I got
tired of juggling 15 emulator windows. Every project starts with
<b>"that's a dumb idea, let's try it."</b>
</p>
<div class="dev-tags">
<span class="dev-tag">Gelectron</span>
<span class="dev-tag">Nova AI</span>
<span class="dev-tag">OmniEmu</span>
</div>
</div>
<!-- ─ V ─ -->
<div class="dev-panel" data-letter="V" id="devPanelV">
<div class="dev-side-num" aria-hidden="true">03</div>
<div class="dev-eyebrow" data-n="03 // ">Versatile</div>
<div class="dev-word"><span class="dev-word-inner">VERSA&shy;TILE</span></div>
<p class="dev-body">
One day I'm writing <b>Rust</b> for a native web-view renderer. The next I'm
rigging a 3D model in <b>Blender</b>, scripting a GD demon list backend in
<b>Node.js</b>, or building a macOS app in <b>Swift</b>. I work across the
full stack, both dimensions, and every timezone my screen stays lit in.
Jack of all trades — master of shipping.
</p>
<div class="dev-tags">
<span class="dev-tag">Rust</span>
<span class="dev-tag">Swift</span>
<span class="dev-tag">Blender</span>
<span class="dev-tag">Full-stack</span>
</div>
</div>
</div><!-- /dev-content-col -->
</div><!-- /dev-sticky -->
</div><!-- /dev-scroll-track -->
</div><!-- /dev-outer -->
<!-- ══════════════ WORK ══════════════ -->
<section id="work">
<div class="work-header reveal-up">
<h2>Selected Work</h2>
<span class="work-count">2022 — 2026 // 09 PROJECTS</span>
</div>
<a class="proj-row" href="../projects/omniemu.html">
<span class="pr-num">01</span>
<span class="pr-name">OmniEmu 2.0</span>
<div class="pr-right"><div class="pr-tags"><span>TypeScript</span><span>MIT</span><span>Active</span></div><span class="pr-year">20242026</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/gelectron.html">
<span class="pr-num">02</span>
<span class="pr-name">Gelectron</span>
<div class="pr-right"><div class="pr-tags"><span>Rust</span><span>Node.js</span><span>Early</span></div><span class="pr-year">20252026</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/fedl.html">
<span class="pr-num">03</span>
<span class="pr-name">FEDL</span>
<div class="pr-right"><div class="pr-tags"><span>Community</span><span>Live</span></div><span class="pr-year">20242026</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/command-move.html">
<span class="pr-num">04</span>
<span class="pr-name">CommandMove</span>
<div class="pr-right"><div class="pr-tags"><span>Swift</span><span>macOS</span></div><span class="pr-year">2023</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/noto.html">
<span class="pr-num">05</span>
<span class="pr-name">Noto</span>
<div class="pr-right"><div class="pr-tags"><span>Flutter</span><span>AI</span></div><span class="pr-year">20252026</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/nova.html">
<span class="pr-num">06</span>
<span class="pr-name">Nova AI</span>
<div class="pr-right"><div class="pr-tags"><span>Python</span><span>Local AI</span></div><span class="pr-year">20242025</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/music-app.html">
<span class="pr-num">07</span>
<span class="pr-name">Music App</span>
<div class="pr-right"><div class="pr-tags"><span>JavaScript</span><span>Web</span></div><span class="pr-year">20232024</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/rhythm-game.html">
<span class="pr-num">08</span>
<span class="pr-name">Rhythm Game</span>
<div class="pr-right"><div class="pr-tags"><span>HTML</span><span>Game</span></div><span class="pr-year">20222023</span></div>
<span class="pr-arrow"></span>
</a>
<a class="proj-row" href="../projects/moon-gaming.html">
<span class="pr-num">09</span>
<span class="pr-name">Moon Gaming</span>
<div class="pr-right"><div class="pr-tags"><span>HTML</span><span>1542 commits</span></div><span class="pr-year">20222026</span></div>
<span class="pr-arrow"></span>
</a>
</section>
<!-- ══════════════ SKILLS ══════════════ -->
<section class="skills" id="skills">
<div class="skills-left">
<div class="skills-eyebrow reveal-up">Loadout</div>
<h2 class="skills-heading" id="skillsHeading">
<span class="line"><span>TOOLS</span></span>
<span class="line"><span>&amp; TECH</span></span>
</h2>
<p class="skills-note reveal-up reveal-delay-1">
What I reach for daily — and what I'm currently beating my head against.
</p>
</div>
<div class="skills-right">
<div class="skill-group reveal-up">
<div class="skill-group-label">★ READY TO SHIP</div>
<div class="skill-list" id="pillsReady">
<span class="skill-item" data-si="javascript">JavaScript</span>
<span class="skill-item" data-si="nodedotjs">Node.js</span>
<span class="skill-item" data-si="html5">HTML</span>
<span class="skill-item" data-si="css3">CSS</span>
<span class="skill-item" data-si="python">Python</span>
<span class="skill-item" data-si="git">Git</span>
<span class="skill-item" data-si="blender">Blender</span>
<span class="skill-item" data-si="unity">Unity</span>
<span class="skill-item" data-si="unrealengine">Unreal</span>
<span class="skill-item" data-si="cloudflare">Cloudflare</span>
<span class="skill-item" data-si="linux">Linux</span>
<span class="skill-item" data-si="gnubash">Bash</span>
<span class="skill-item" data-si="electron">Electron</span>
<span class="skill-item" data-si="github">GitHub</span>
</div>
</div>
<div class="skill-group reveal-up reveal-delay-2">
<div class="skill-group-label">★ CURRENTLY UNLOCKING</div>
<div class="skill-list">
<span class="skill-item dim" data-si="rust">Rust</span>
<span class="skill-item dim" data-si="php">PHP</span>
<span class="skill-item dim" data-si="dotnet">.NET</span>
<span class="skill-item dim" data-si="nixos">Nix</span>
</div>
</div>
</div>
</section>
<!-- ══════════════ DEVLOG CARDS (horizontal scroll) ══════════════ -->
<div class="h-scroll-outer" id="devlog">
<div class="h-scroll-inner" id="hScrollInner">
<div class="h-card">
<div class="h-card-num">2026 // 001</div>
<div class="h-card-title">FOCUSING ON DISKETTE LABS</div>
<p class="h-card-body">Co-founder of a product design collective. Where my focus is now — still open to collabs.</p>
<a class="h-card-tag" href="../blog/diskette-devlog.html">READ ENTRY →</a>
</div>
<div class="h-card">
<div class="h-card-num">2026 // 002</div>
<div class="h-card-title">WHY I BUILT GELECTRON</div>
<p class="h-card-body">Electron bundles Chromium with every app. 150300 MB per install. Native web views fix that.</p>
<a class="h-card-tag" href="../blog/gelectron-devlog.html">READ ENTRY →</a>
</div>
<div class="h-card">
<div class="h-card-num">2026 // 003</div>
<div class="h-card-title">WHY I BUILT OMNIEMU</div>
<p class="h-card-body">I got tired of opening 15 different emulator apps, so I built one that manages them all.</p>
<a class="h-card-tag" href="../blog/omniemu-devlog.html">READ ENTRY →</a>
</div>
<div class="h-card">
<div class="h-card-num">2026 // 004</div>
<div class="h-card-title">COMMANDMOVE: CUT &amp; PASTE</div>
<p class="h-card-body">macOS doesn't have Cmd+X for files. Global event taps, AppleScript, and Accessibility perms.</p>
<a class="h-card-tag" href="../blog/command-move-devlog.html">READ ENTRY →</a>
</div>
<div class="h-card">
<div class="h-card-num">2025 // 005</div>
<div class="h-card-title">RUNNING THE FEDL</div>
<p class="h-card-body">3,800+ verified runs, ranked levels, and a GD community that never sleeps.</p>
<a class="h-card-tag" href="../blog/fedl-devlog.html">READ ENTRY →</a>
</div>
<div class="h-card">
<div class="h-card-num">2025 // 006</div>
<div class="h-card-title">BUILDING NOVA AI</div>
<p class="h-card-body">Local AI that doesn't phone home. Desktop GUI, web interface, CLI — no cloud, no subscriptions.</p>
<a class="h-card-tag" href="../blog/nova-devlog.html">READ ENTRY →</a>
</div>
</div>
</div>
<!-- ══════════════ CONTACT ══════════════ -->
<section class="contact" id="contact">
<div class="contact-left">
<div class="contact-eyebrow reveal-up">Contact</div>
<h2 class="contact-heading" id="contactHeading">
<span class="line"><span>LET'S</span></span>
<span class="line"><span>TALK.</span></span>
</h2>
</div>
<div class="contact-right">
<a class="contact-link" href="mailto:[email protected]">
<div><div class="cl-label">EMAIL</div><div class="cl-val">[email protected]</div></div>
<span class="cl-arrow"></span>
</a>
<a class="contact-link" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener">
<div><div class="cl-label">GITHUB</div><div class="cl-val">mileswolfallen2</div></div>
<span class="cl-arrow"></span>
</a>
<a class="contact-link" href="https://diskettelabs.com/" target="_blank" rel="noopener">
<div><div class="cl-label">STUDIO</div><div class="cl-val">Diskette Labs</div></div>
<span class="cl-arrow"></span>
</a>
<a class="contact-link" href="https://replit.com/@MilesAllen1" target="_blank" rel="noopener">
<div><div class="cl-label">REPLIT</div><div class="cl-val">@MilesAllen1</div></div>
<span class="cl-arrow"></span>
</a>
<a class="contact-link" href="https://codepen.io/mileswolfallen2-the-typescripter" target="_blank" rel="noopener">
<div><div class="cl-label">CODEPEN</div><div class="cl-val">mileswolfallen2</div></div>
<span class="cl-arrow"></span>
</a>
</div>
</section>
<!-- ══════════════ FOOTER ══════════════ -->
<div class="footer-word" aria-hidden="true">MILES</div>
<div class="footer-bottom">
<span class="footer-copy">© 2026 MILES WOLF ALLEN — BUILT BY HAND</span>
<div class="footer-links">
<a href="../uses.html">USES</a>
<a href="../now.html">NOW</a>
<a href="../colophon.html">COLOPHON</a>
<a href="#hero">↑ TOP</a>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script>
(function () {
'use strict';
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var fine = window.matchMedia && window.matchMedia('(pointer: fine)').matches;
/* ── LOADER ── */
var loaderEl = document.getElementById('loader');
var loaderCount = document.getElementById('loaderCount');
var loaderBar = document.getElementById('loaderBar');
var loaderWipe = document.getElementById('loaderWipe');
var startTime = performance.now();
var loadDur = reduce ? 0 : 1500;
function runLoader() {
var elapsed = performance.now() - startTime;
var pct = Math.min(elapsed / loadDur, 1);
var eased = pct < .5 ? 2 * pct * pct : -1 + (4 - 2 * pct) * pct;
loaderCount.textContent = Math.floor(eased * 100);
loaderBar.style.width = (eased * 100) + '%';
if (pct < 1) {
requestAnimationFrame(runLoader);
} else {
loaderCount.textContent = '100';
loaderBar.style.width = '100%';
setTimeout(dismissLoader, 100);
}
}
function dismissLoader() {
if (!loaderWipe) return;
/* ink wipe sweeps right over the yellow panel, then whole loader goes up */
loaderWipe.style.transition = 'transform .6s cubic-bezier(.76,0,.24,1)';
loaderWipe.style.transform = 'scaleX(1)';
setTimeout(function () {
if (!loaderEl) return;
loaderEl.style.transition = 'transform .7s cubic-bezier(.76,0,.24,1)';
loaderEl.style.transform = 'translateY(-100%)';
setTimeout(function () {
loaderEl.classList.add('done');
triggerHeroIn();
}, 720);
}, 620);
}
function triggerHeroIn() {
var heroLabel = document.getElementById('heroLabel');
var heroName = document.getElementById('heroName');
var heroDesc = document.getElementById('heroDesc');
var heroCta = document.getElementById('heroCta');
var heroScroll = document.getElementById('heroScroll');
setTimeout(function () { if (heroLabel) heroLabel.classList.add('in'); }, 80);
setTimeout(function () { if (heroName) heroName.classList.add('in'); }, 160);
setTimeout(function () { if (heroDesc) heroDesc.classList.add('in'); }, 320);
setTimeout(function () { if (heroCta) heroCta.classList.add('in'); }, 440);
setTimeout(function () { if (heroScroll) heroScroll.classList.add('in'); }, 620);
}
if (reduce) {
if (loaderEl) loaderEl.classList.add('done');
triggerHeroIn();
} else {
requestAnimationFrame(runLoader);
}
/* ── MARQUEE CONTENT ── */
var mq1Words = ['BUILDING EMULATORS','★','SEND HELP','★','160 REPOS','★','STILL DEBUGGING','★','GD ADDICT','★','USUALLY ONLINE','★','FULL MAXIMALISM','★','CEREAL IS SOUP','★'];
var mq2Words = ['RATED ★★★★★','EST. 2008','PULL SHARK ×2','OVERCLOCKED','DISKETTE LABS','COFFEE: UNKNOWN','BUGS: INFINITE','YOLO'];
function buildMarquee(id, words) {
var el = document.getElementById(id);
if (!el) return;
var half = '';
words.forEach(function (w) {
half += '<span class="marquee-item">' + w + '</span>';
});
el.innerHTML = half + half;
}
buildMarquee('mq1', mq1Words);
buildMarquee('mq2', mq2Words);
/* ── SKILL ICONS ── */
document.querySelectorAll('[data-si]').forEach(function (el) {
var img = document.createElement('img');
img.alt = ''; img.loading = 'lazy';
img.src = 'https://cdn.simpleicons.org/' + el.getAttribute('data-si') + '/000000';
el.insertBefore(img, el.firstChild);
});
/* ── CURSOR ── */
var dot = document.getElementById('cDot');
var ring = document.getElementById('cRing');
if (fine && dot && ring) {
var mx=-200, my=-200, dx=-200, dy=-200, rx=-200, ry=-200;
document.addEventListener('mousemove', function (e) { mx = e.clientX; my = e.clientY; }, { passive: true });
(function loop() {
dx += (mx - dx) * 0.5; dy += (my - dy) * 0.5;
rx += (mx - rx) * 0.1; ry += (my - ry) * 0.1;
dot.style.left = dx + 'px'; dot.style.top = dy + 'px';
ring.style.left = rx + 'px'; ring.style.top = ry + 'px';
requestAnimationFrame(loop);
})();
var hoverEls = document.querySelectorAll('a, button, .proj-row, .skill-item, .contact-link, .h-card, .btn');
hoverEls.forEach(function (el) {
el.addEventListener('mouseenter', function () { document.body.classList.add('cursor-hover'); });
el.addEventListener('mouseleave', function () { document.body.classList.remove('cursor-hover'); });
});
}
/* ── INTERSECTION REVEALS ── */
if ('IntersectionObserver' in window && !reduce) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); }
});
}, { threshold: 0.13, rootMargin: '0px 0px -4% 0px' });
document.querySelectorAll(
'.reveal-up, .about-heading, .about-photo, .about-body, .about-tags, .skills-heading, .contact-heading'
).forEach(function (el) { io.observe(el); });
} else {
document.querySelectorAll(
'.reveal-up, .about-heading, .about-photo, .about-body, .about-tags, .skills-heading, .contact-heading'
).forEach(function (el) { el.classList.add('in'); });
}
/* ── COUNT-UP ── */
function countUp(el) {
var target = parseInt(el.getAttribute('data-count'), 10) || 0;
if (reduce) { el.textContent = target.toLocaleString('en-US'); return; }
var t0 = null;
function step(ts) {
if (t0 === null) t0 = ts;
var p = Math.min(1, (ts - t0) / 1500);
var ease = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * ease).toLocaleString('en-US');
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
if ('IntersectionObserver' in window) {
var cio = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { countUp(e.target); cio.unobserve(e.target); }
});
}, { threshold: 0.4 });
document.querySelectorAll('.num[data-count]').forEach(function (el) { cio.observe(el); });
}
/* ── GSAP CHOREOGRAPHY ── */
if (window.gsap && window.ScrollTrigger && !reduce) {
gsap.registerPlugin(ScrollTrigger);
/* hero name parallax drift */
gsap.to('#heroName', {
yPercent: -16, ease: 'none',
scrollTrigger: { trigger: '.hero', start: 'top top', end: 'bottom top', scrub: 1.2 }
});
/* hero rings slow spin + scale */
gsap.to('.hero-orb', {
scale: 1.22, opacity: .2, rotation: 15, ease: 'none',
scrollTrigger: { trigger: '.hero', start: 'top top', end: 'bottom top', scrub: 1.4 }
});
/* project rows — stagger slide in from left */
gsap.utils.toArray('.proj-row').forEach(function (row, i) {
gsap.fromTo(row,
{ opacity: 0, x: -40 },
{ opacity: 1, x: 0, duration: .65, ease: 'power3.out', delay: i * .045,
scrollTrigger: { trigger: row, start: 'top 95%', toggleActions: 'play none none none' }
});
});
/* stats — pop in with spring */
gsap.utils.toArray('.stat').forEach(function (s, i) {
gsap.fromTo(s,
{ opacity: 0, y: 40, rotation: (i % 2 === 0 ? -2 : 2) },
{ opacity: 1, y: 0, rotation: 0, duration: .75, ease: 'back.out(1.6)', delay: i * .09,
scrollTrigger: { trigger: '.stats', start: 'top 88%' }
});
});
/* skill pills — fan in */
gsap.utils.toArray('.skill-item').forEach(function (item, i) {
gsap.fromTo(item,
{ opacity: 0, scale: .7, rotation: (Math.random() * 10 - 5) },
{ opacity: item.classList.contains('dim') ? .65 : 1,
scale: 1, rotation: 0,
duration: .45, ease: 'back.out(1.8)', delay: i * .025,
scrollTrigger: { trigger: '#skills', start: 'top 86%' }
});
});
/* horizontal devlog scroll — pinned scrub */
var hInner = document.getElementById('hScrollInner');
if (hInner) {
var totalW = 0;
hInner.querySelectorAll('.h-card').forEach(function (c) {
totalW += c.offsetWidth + 40; /* gap approx */
});
var overscroll = totalW - hInner.parentElement.offsetWidth + 104;
if (overscroll > 0) {
gsap.to(hInner, {
x: -overscroll, ease: 'none',
scrollTrigger: {
trigger: '.h-scroll-outer', pin: true,
start: 'top top', end: '+=' + (overscroll * 1.25),
scrub: 1.3
}
});
}
}
/* contact heading lines */
gsap.utils.toArray('.contact-heading .line span').forEach(function (el, i) {
gsap.fromTo(el, { y: '105%' }, {
y: '0%', duration: .95, ease: 'power3.out', delay: i * .14,
scrollTrigger: { trigger: '#contact', start: 'top 86%' }
});
});
/* about photo tilt scrub */
gsap.to('#aboutPhoto', {
rotation: 0, ease: 'none',
scrollTrigger: { trigger: '#about', start: 'top 80%', end: 'bottom 30%', scrub: 1.5 }
});
ScrollTrigger.refresh();
}
/* ── SMOOTH WHEEL INERTIA ── */
if (!reduce) {
var scrollY = window.scrollY, targetY = scrollY, ticking = false;
window.addEventListener('wheel', function (e) {
e.preventDefault();
targetY = Math.max(0, Math.min(document.body.scrollHeight - window.innerHeight, targetY + e.deltaY * 1.05));
if (!ticking) {
ticking = true;
(function step() {
scrollY += (targetY - scrollY) * .1;
window.scrollTo(0, scrollY);
if (Math.abs(targetY - scrollY) > 0.4) requestAnimationFrame(step);
else { scrollY = targetY; window.scrollTo(0, scrollY); ticking = false; }
})();
}
}, { passive: false });
window.addEventListener('touchstart', function () { targetY = scrollY = window.scrollY; }, { passive: true });
window.addEventListener('touchmove', function () { targetY = scrollY = window.scrollY; }, { passive: true });
}
/* ── KONAMI ── */
var seq = [38,38,40,40,37,39,37,39,66,65], pos = 0;
document.addEventListener('keydown', function (e) {
pos = e.keyCode === seq[pos] ? pos + 1 : 0;
if (pos === seq.length) {
pos = 0;
var colors = ['#FF2EA6','#F7FF00','#00F4FF','#FF5C00','#00FF85'];
for (var i = 0; i < 44; i++) {
var s = document.createElement('div');
s.style.cssText = 'position:fixed;left:50%;top:50%;z-index:9999;pointer-events:none;width:'+(7+Math.random()*16)+'px;height:'+(7+Math.random()*16)+'px;border:2px solid #000;background:'+colors[i%colors.length]+';box-shadow:3px 3px 0 #000;';
document.body.appendChild(s);
var a = Math.random()*Math.PI*2, d = 80+Math.random()*400;
s.animate([
{ transform:'translate(-50%,-50%) rotate(0deg)', opacity:1 },
{ transform:'translate(calc(-50% + '+(Math.cos(a)*d)+'px),calc(-50% + '+(Math.sin(a)*d)+'px)) rotate('+(Math.random()*720-360)+'deg)', opacity:0 }
], { duration:900+Math.random()*500, easing:'cubic-bezier(.2,.8,.2,1)' }).onfinish = (function(el){ return function(){ el.remove(); }; })(s);
}
}
});
})();
</script>
<script>
/* ══════════════════════════════════════
DEV ACRONYM — SCROLL CHOREOGRAPHY
══════════════════════════════════════ */
(function () {
'use strict';
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var panels = ['D','E','V'];
var colors = { D: 'col-d', E: 'col-e', V: 'col-v' };
var letters = { D: 'D', E: 'E', V: 'V' };
var trackEl = document.getElementById('devTrack');
var stickyEl = document.getElementById('devSticky');
var activeLtr = document.getElementById('devActiveLetter');
var progressEl = document.getElementById('devProgressBar');
var dots = document.querySelectorAll('.dev-dot');
if (!trackEl || !stickyEl) return;
/* height of the scroll track = 3 panels × 100vh + 1 extra for breathing room */
var panelCount = panels.length;
trackEl.style.height = (panelCount * 100) + 'vh';
/* ── no-GSAP fallback (also used for reduced-motion) ── */
function simpleFallback() {
/* just reveal all panels statically */
panels.forEach(function (l) {
var p = document.getElementById('devPanel' + l);
if (!p) return;
p.style.clipPath = 'inset(0 0 0% 0)';
var body = p.querySelector('.dev-body');
var tags = p.querySelector('.dev-tags');
var word = p.querySelector('.dev-word-inner');
if (body) body.style.opacity = '1';
if (tags) tags.style.opacity = '1';
if (word) word.style.transform = 'translateY(0)';
});
/* show first panel on top */
var first = document.getElementById('devPanelD');
if (first) first.style.zIndex = '5';
}
if (reduce || !window.gsap || !window.ScrollTrigger) {
simpleFallback();
return;
}
gsap.registerPlugin(ScrollTrigger);
/* ── helper: animate a panel IN ── */
function animateIn(letter) {
var p = document.getElementById('devPanel' + letter);
if (!p) return;
var word = p.querySelector('.dev-word-inner');
var body = p.querySelector('.dev-body');
var tags = p.querySelector('.dev-tags');
/* reveal clip */
gsap.to(p, { clipPath: 'inset(0 0 0% 0)', duration: .72, ease: 'power3.inOut' });
/* word slides up */
if (word) gsap.to(word, { y: '0%', duration: .7, ease: 'power3.out', delay: .18 });
/* body fades */
if (body) gsap.to(body, { opacity: 1, y: 0, duration: .6, ease: 'power2.out', delay: .32 });
/* tags pop */
if (tags) gsap.to(tags, { opacity: 1, duration: .45, ease: 'power2.out', delay: .46 });
/* left letter */
if (activeLtr) {
gsap.to(activeLtr, { y: -12, opacity: 0, duration: .2, ease: 'power2.in',
onComplete: function () {
activeLtr.textContent = letter;
activeLtr.className = 'dev-active-letter ' + colors[letter];
gsap.fromTo(activeLtr,
{ y: 24, opacity: 0 },
{ y: 0, opacity: 1, duration: .4, ease: 'back.out(1.4)' }
);
}
});
}
/* dots */
dots.forEach(function (d, i) {
d.classList.toggle('active', i === panels.indexOf(letter));
});
}
/* ── helper: animate a panel OUT (going forward) ── */
function animateOut(letter) {
var p = document.getElementById('devPanel' + letter);
if (!p) return;
var body = p.querySelector('.dev-body');
var tags = p.querySelector('.dev-tags');
var word = p.querySelector('.dev-word-inner');
gsap.to(p, { clipPath: 'inset(0 0 100% 0)', duration: .55, ease: 'power3.in' });
if (body) gsap.to(body, { opacity: 0, duration: .25, ease: 'power2.in' });
if (tags) gsap.to(tags, { opacity: 0, duration: .2, ease: 'power2.in' });
if (word) gsap.to(word, { y: '-30%', duration: .35, ease: 'power2.in' });
}
/* ── set initial state ── */
panels.forEach(function (l, i) {
var p = document.getElementById('devPanel' + l);
var body = p ? p.querySelector('.dev-body') : null;
var tags = p ? p.querySelector('.dev-tags') : null;
var word = p ? p.querySelector('.dev-word-inner') : null;
if (p) gsap.set(p, { clipPath: 'inset(0 0 100% 0)', zIndex: i + 1 });
if (body) gsap.set(body, { opacity: 0, y: 12 });
if (tags) gsap.set(tags, { opacity: 0 });
if (word) gsap.set(word, { y: '105%' });
});
/* ── main ScrollTrigger: pin + scrub ── */
var currentPanel = -1;
ScrollTrigger.create({
trigger: trackEl,
start: 'top top',
end: 'bottom bottom',
pin: stickyEl,
pinSpacing: false,
onUpdate: function (self) {
/* progress bar */
if (progressEl) progressEl.style.width = (self.progress * 100) + '%';
/* which panel? */
var idx = Math.min(
panelCount - 1,
Math.floor(self.progress * panelCount)
);
if (idx !== currentPanel) {
/* out old */
if (currentPanel >= 0) animateOut(panels[currentPanel]);
currentPanel = idx;
/* in new */
animateIn(panels[currentPanel]);
}
},
onEnter: function () {
/* kick off the first panel */
if (currentPanel < 0) {
currentPanel = 0;
animateIn(panels[0]);
}
},
onLeaveBack: function () {
/* reset if scrolled above section */
if (currentPanel >= 0) animateOut(panels[currentPanel]);
currentPanel = -1;
if (activeLtr) {
activeLtr.textContent = 'D';
activeLtr.className = 'dev-active-letter col-d';
}
dots.forEach(function (d, i) { d.classList.toggle('active', i === 0); });
if (progressEl) progressEl.style.width = '0%';
}
});
ScrollTrigger.refresh();
})();
</script>
</body>
</html>