mirror of
https://github.com/mileswolfallen2/miles-web.git
synced 2026-09-08 11:23:17 +00:00
1924 lines
79 KiB
HTML
1924 lines
79 KiB
HTML
<!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 & 3D Artist — Est. 2008</div>
|
||
|
||
<div class="hero-chips">
|
||
<span class="chip chip-y">★ MILES WOLF ALLEN</span>
|
||
<span class="chip chip-p">DEVELOPER & 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 & 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 > 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­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­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­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">2024–2026</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">2025–2026</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">2024–2026</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">2025–2026</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">2024–2025</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">2023–2024</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">2022–2023</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">2022–2026</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>& 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. 150–300 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 & 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>
|