Files
miles-web/redesign/try-three.html
T

992 lines
50 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 // TRY THREE</title>
<meta name="description" content="Miles Wolf Allen — developer, 3D artist, and the guy trying to replace Electron.">
<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:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<script>document.documentElement.className='js';</script>
<style>
:root {
--bg: #08080a;
--bg2: #111114;
--bg3: #1a1a1f;
--ink: #ffffff;
--muted: #7a7a85;
--dim: #3a3a45;
--pink: #FF2EA6;
--yellow: #F7FF00;
--cyan: #00F4FF;
--orange: #FF5C00;
--purple: #8A2BFF;
--green: #00FF85;
--red: #FF1F1F;
--d: 'Archivo Black', Impact, sans-serif;
--m: 'Space Mono', monospace;
--p: 'Press Start 2P', monospace;
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; overflow-x: hidden; }
body {
background: var(--bg);
color: var(--ink);
font-family: var(--m);
font-size: 14px;
line-height: 1.65;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
::selection { background: var(--yellow); color: #000; }
a { color: inherit; text-decoration: none; }
/* scrollbar */
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: #000; }
::-webkit-scrollbar-thumb { background: var(--pink); border: 3px solid #000; }
/* progress */
#progress {
position: fixed; top: 0; left: 0; height: 4px; width: 0;
background: linear-gradient(90deg, var(--pink), var(--yellow), var(--cyan));
z-index: 700; box-shadow: 0 0 10px var(--pink);
transition: width .1s linear;
}
/* fixed bg layers */
.bg-grid {
position: fixed; inset: 0; z-index: -3; pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,.03) 1px, transparent 1px);
background-size: 52px 52px;
}
.noise {
position: fixed; inset: 0; z-index: -2; pointer-events: none; opacity: .05;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' 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 50%, rgba(0,0,0,.65) 100%);
}
/* cursor */
.cursor { position: fixed; z-index: 900; pointer-events: none; border-radius: 50%; top: 0; left: 0; }
.cursor-dot { width: 10px; height: 10px; background: #fff; transform: translate(-50%,-50%); mix-blend-mode: difference; transition: width .15s, height .15s; }
.cursor-halo { width: 300px; height: 300px; background: radial-gradient(circle, rgba(255,46,166,.14), transparent 65%); transform: translate(-50%,-50%); z-index: 800; }
.cursor-dot.big { width: 40px; height: 40px; border: 2px solid #fff; background: transparent; }
@media (pointer: coarse) { .cursor { display: none; } }
/* splash */
.splash {
position: fixed; inset: 0; z-index: 1000; background: var(--bg);
display: flex; align-items: center; justify-content: center;
pointer-events: none;
animation: splashOut .5s ease forwards 1.1s;
}
.splash-letters { display: flex; }
.splash-letters span {
font-family: var(--d); font-size: clamp(72px, 20vw, 240px); display: inline-block;
opacity: 0; animation: splashPop .55s cubic-bezier(.18,1.4,.3,1) forwards;
}
.splash-letters span:nth-child(1) { animation-delay: .05s; color: var(--yellow); -webkit-text-stroke: 3px #000; }
.splash-letters span:nth-child(2) { animation-delay: .14s; }
.splash-letters span:nth-child(3) { animation-delay: .23s; color: var(--pink); -webkit-text-stroke: 3px #000; }
.splash-letters span:nth-child(4) { animation-delay: .32s; }
.splash-letters span:nth-child(5) { animation-delay: .41s; color: var(--cyan); -webkit-text-stroke: 3px #000; }
.splash-tag {
position: absolute; bottom: 14%; left: 50%; transform: translateX(-50%) rotate(-2deg);
font-family: var(--p); font-size: 8px; background: var(--yellow); color: #000;
padding: 10px 18px; border: 3px solid #000; box-shadow: 5px 5px 0 var(--pink);
}
@keyframes splashPop { 0% { opacity: 0; transform: translateY(100%) rotate(5deg); } 100% { opacity: 1; transform: none; } }
@keyframes splashOut { 0% { opacity: 1; } 100% { opacity: 0; visibility: hidden; } }
/* nav */
.nav {
position: fixed; top: 12px; left: 12px; right: 12px; z-index: 500;
display: flex; align-items: center; justify-content: space-between; gap: 12px;
background: rgba(8,8,10,.82); border: 3px solid #000;
box-shadow: 5px 5px 0 #000; padding: 10px 16px;
backdrop-filter: blur(8px);
}
.nav-logo { font-family: var(--d); font-size: 18px; display: flex; align-items: center; gap: 8px; }
.nav-logo .live { width: 10px; height: 10px; background: var(--green); border: 2px solid #000; border-radius: 50%; animation: blink 1.4s steps(1) infinite; }
.nav-links { display: flex; gap: 4px; flex-wrap: wrap; }
.nav-links a {
font-family: var(--p); font-size: 7px; color: #fff; padding: 8px 11px;
border: 2px solid rgba(255,255,255,.25); text-transform: uppercase;
transition: all .14s;
}
.nav-links a:hover { background: var(--pink); color: #000; border-color: #000; transform: translate(-2px,-2px); box-shadow: 3px 3px 0 #000; }
.nav-right { display: flex; align-items: center; gap: 8px; font-family: var(--p); font-size: 7px; color: var(--yellow); }
.nav-right .pulse { width: 8px; height: 8px; background: var(--yellow); border-radius: 50%; animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: .15; } }
@media (max-width: 700px) { .nav-links { display: none; } }
/* buttons */
.btn {
display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
font-family: var(--p); font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
padding: 15px 22px; border: 3px solid #000; box-shadow: 5px 5px 0 #000;
transition: transform .12s, box-shadow .12s; user-select: none;
}
.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-yellow { background: var(--yellow); color: #000; }
.btn-ghost { background: transparent; color: #fff; border-color: rgba(255,255,255,.4); }
.btn-ghost:hover { background: #fff; color: #000; border-color: #000; box-shadow: 8px 8px 0 var(--pink); }
/* chips */
.chip {
display: inline-flex; align-items: center; gap: 7px;
font-family: var(--p); font-size: 7px; text-transform: uppercase;
border: 2px solid #000; padding: 7px 11px; box-shadow: 3px 3px 0 #000;
}
.chip-y { background: var(--yellow); color: #000; }
.chip-p { background: var(--pink); color: #000; }
.chip-c { background: var(--cyan); color: #000; }
.chip-g { background: var(--green); color: #000; }
.chip-o { background: var(--orange); color: #000; }
/* marquee */
.mq { overflow: hidden; white-space: nowrap; border-top: 3px solid #000; border-bottom: 3px solid #000; position: relative; z-index: 3; }
.mq-track { display: inline-flex; white-space: nowrap; }
.mq-track.fwd { animation: mqFwd 24s linear infinite; }
.mq-track.rev { animation: mqRev 28s linear infinite; }
.mq span { font-family: var(--p); font-size: 11px; padding: 13px 0; margin: 0 20px; flex-shrink: 0; color: #000; }
.mq-y { background: var(--yellow); }
.mq-p { background: var(--pink); }
.mq-c { background: var(--cyan); }
.mq-o { background: var(--orange); }
@keyframes mqFwd { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
@keyframes mqRev { 0% { transform: translateX(-50%); } 100% { transform: translateX(0); } }
/* section shell */
.sec { position: relative; padding: 100px 5vw 80px; }
.sec-ghost {
position: absolute; top: 20px; right: -1vw; z-index: 0; pointer-events: none; user-select: none;
font-family: var(--d); font-size: clamp(110px, 22vw, 260px); line-height: .8;
color: transparent; -webkit-text-stroke: 1.5px rgba(255,255,255,.07);
}
.sec-header { position: relative; z-index: 2; margin-bottom: 52px; }
.sec-eyebrow {
display: inline-block; font-family: var(--p); font-size: 8px; color: #000;
background: var(--yellow); border: 3px solid #000; padding: 7px 12px;
box-shadow: 3px 3px 0 #000; transform: rotate(-1.5deg); margin-bottom: 18px;
}
.sec-title {
font-family: var(--d); font-size: clamp(44px, 9.5vw, 120px);
line-height: .9; letter-spacing: -.02em; text-transform: uppercase;
display: flex; align-items: baseline; flex-wrap: wrap; gap: 0 .2em;
}
.sec-title .out { color: transparent; -webkit-text-stroke: 2px #fff; }
.sec-title .acc-pink { color: var(--pink); }
.sec-title .acc-yellow { color: var(--yellow); }
.sec-title .dot { color: var(--yellow); }
.sec-sub { font-family: var(--m); color: var(--muted); font-size: 13px; margin-top: 12px; max-width: 500px; }
/* reveal system */
html.js [data-in] { opacity: 0; transition: opacity .7s ease, transform .75s cubic-bezier(.18,1.3,.3,1); will-change: transform, opacity; }
html.js [data-in="up"] { transform: translateY(55px) rotate(-1deg); }
html.js [data-in="left"] { transform: translateX(-70px) rotate(-1.5deg); }
html.js [data-in="right"] { transform: translateX(70px) rotate(1.5deg); }
html.js [data-in="pop"] { transform: scale(.55) rotate(-8deg); }
html.js [data-in="flip"] { transform: translateY(40px) rotateX(-55deg); transform-style: preserve-3d; }
html.js [data-in].in { opacity: 1; transform: none; }
/* ── HERO ── */
.hero {
position: relative; min-height: 100vh;
display: flex; flex-direction: column; justify-content: flex-end;
padding: 130px 5vw 60px; overflow: hidden;
}
.hero-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 28px; position: relative; z-index: 2; }
.hero-title {
font-family: var(--d); line-height: .86; letter-spacing: -.03em;
font-size: clamp(76px, 22vw, 290px); text-transform: uppercase;
position: relative; z-index: 2;
}
.hero-title .row { display: block; }
.hero-title .row-2 { margin-left: .9em; }
.hero-title em { font-style: normal; color: transparent; -webkit-text-stroke: 2.5px #fff; }
.hero-title .star-dot { color: var(--yellow); }
/* hero orbital rings */
.hero-orb {
position: absolute; top: 50%; right: 3vw;
transform: translateY(-52%);
width: min(44vw, 540px); height: min(44vw, 540px);
z-index: 0; pointer-events: none;
}
.ring { position: absolute; inset: 0; border-radius: 50%; }
.ring-1 { border: 3px dashed rgba(255,255,255,.25); animation: spin 32s linear infinite; }
.ring-2 { inset: 10%; border: 3px solid rgba(255,46,166,.45); animation: spin 22s linear infinite reverse; }
.ring-3 { inset: 20%; border: 3px dashed rgba(0,244,255,.45); animation: spin 48s linear infinite; }
.ring-core {
position: absolute; inset: 38%; border-radius: 50%;
background: radial-gradient(circle, rgba(247,255,0,.22), transparent 70%);
animation: pulse 3s ease-in-out infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { 0%,100% { opacity: .6; transform: scale(.95); } 50% { opacity: 1; transform: scale(1.05); } }
@media (max-width: 700px) { .hero-orb { display: none; } }
.hero-sub {
font-family: var(--m); font-size: clamp(13px, 1.5vw, 18px);
color: var(--muted); max-width: 540px; line-height: 1.75;
margin: 28px 0 32px; padding-left: 16px; border-left: 5px solid var(--pink);
position: relative; z-index: 2;
}
.hero-sub b { color: #fff; }
.hero-cta { display: flex; gap: 12px; flex-wrap: wrap; position: relative; z-index: 2; }
/* floating labels */
.fl {
position: absolute; z-index: 3;
font-family: var(--p); font-size: 8px; text-transform: uppercase;
border: 3px solid #000; box-shadow: 5px 5px 0 #000; padding: 9px 13px;
line-height: 1.5; text-align: center;
}
.fl-1 { top: 23%; left: 5%; background: var(--yellow); color: #000; transform: rotate(-8deg); }
.fl-2 { top: 35%; right: 5%; background: #fff; color: #000; transform: rotate(6deg); border-radius: 50%; width: 90px; height: 90px; display: flex; align-items: center; justify-content: center; }
.fl-3 { bottom: 22%; left: 46%; background: var(--pink); color: #000; transform: rotate(-4deg); }
@media (max-width: 900px) { .fl { display: none; } }
.hero-scroll {
position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
font-family: var(--p); font-size: 7px; color: var(--muted);
display: flex; flex-direction: column; align-items: center; gap: 7px; z-index: 2;
}
.hero-scroll .arr { font-size: 16px; animation: bob 1.6s ease-in-out infinite; }
@keyframes bob { 50% { transform: translateY(5px); } }
/* ── STATS ── */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; position: relative; z-index: 2; }
.stat-card {
border: 4px solid #000; padding: 32px 16px; text-align: center;
box-shadow: 8px 8px 0 #000; position: relative; overflow: hidden;
transition: transform .15s, box-shadow .15s;
}
.stat-card:hover { transform: translate(-3px,-3px); box-shadow: 12px 12px 0 #000; }
.stat-card .n { font-family: var(--d); font-size: clamp(38px, 6vw, 80px); line-height: 1; display: block; color: #000; }
.stat-card .l { font-family: var(--p); font-size: 7px; text-transform: uppercase; margin-top: 10px; display: block; color: #000; letter-spacing: .06em; }
.stat-card .tick { position: absolute; top: -10px; right: -10px; width: 32px; height: 32px; background: #fff; border: 3px solid #000; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; box-shadow: 2px 2px 0 #000; }
.s1 { background: var(--pink); transform: rotate(-1.8deg); }
.s2 { background: var(--yellow); transform: rotate(1.4deg) translateY(8px); }
.s3 { background: var(--cyan); transform: rotate(-1deg) translateY(-5px); }
.s4 { background: var(--orange); transform: rotate(2deg) translateY(5px); }
@media (max-width: 860px) { .stats-grid { grid-template-columns: repeat(2,1fr); gap: 22px 12px; } .s2,.s3,.s4 { transform: none; } }
/* ── ABOUT ── */
.about-layout { display: grid; grid-template-columns: 260px 1fr; gap: 36px; position: relative; z-index: 2; }
.avatar-wrap { position: relative; align-self: start; }
.avatar-img {
width: 100%; display: block; border: 5px solid #000;
box-shadow: 10px 10px 0 var(--pink); transform: rotate(-3deg);
filter: grayscale(.15) contrast(1.1); transition: transform .22s;
}
.avatar-img:hover { transform: rotate(0) scale(1.02); }
.avatar-sticker {
position: absolute; bottom: -18px; left: -14px;
background: var(--yellow); border: 3px solid #000; padding: 9px 13px;
font-family: var(--p); font-size: 7px; transform: rotate(-5deg);
box-shadow: 4px 4px 0 #000; text-transform: uppercase; color: #000;
}
.bio-card {
background: var(--bg2); border: 4px solid #000; box-shadow: 10px 10px 0 #000;
padding: 32px; position: relative;
}
.bio-card::before {
content: ''; position: absolute; top: -12px; right: 28px;
width: 66px; height: 22px; background: #FFC93C; opacity: .85;
transform: rotate(2deg); box-shadow: 0 0 0 2px #000;
}
.bio-card h3 { font-family: var(--d); font-size: clamp(22px,2.8vw,36px); margin-bottom: 14px; }
.bio-card p { color: var(--muted); font-size: 13px; margin-bottom: 16px; line-height: 1.8; }
.bio-badges { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.badge {
font-family: var(--p); font-size: 7px; padding: 9px 13px;
border: 2px solid #000; box-shadow: 3px 3px 0 #000; text-transform: uppercase; color: #000;
}
.b-pk { background: var(--pink); } .b-y { background: var(--yellow); }
.b-g { background: var(--green); } .b-o { background: var(--orange); }
.bio-actions { display: flex; gap: 10px; flex-wrap: wrap; border-top: 3px dashed rgba(255,255,255,.15); padding-top: 18px; }
@media (max-width: 840px) { .about-layout { grid-template-columns: 1fr; } .avatar-img { max-width: 220px; } }
/* ── SKILLS ── */
.skills-body { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 36px; }
.skill-group-label {
font-family: var(--p); font-size: 9px; text-transform: uppercase; color: var(--yellow);
display: flex; align-items: center; gap: 10px; margin-bottom: 14px;
}
.skill-group-label::after { content: ''; flex: 1; height: 2px; background: rgba(255,255,255,.1); }
.pills { display: flex; flex-wrap: wrap; gap: 10px; }
.pill {
font-family: var(--m); font-weight: 700; font-size: 13px; text-transform: uppercase;
padding: 11px 16px; background: #fff; color: #000; border: 3px solid #000;
box-shadow: 4px 4px 0 #000; display: inline-flex; align-items: center; gap: 9px;
transition: transform .12s, box-shadow .12s, background .12s;
}
.pill:nth-child(5n) { transform: rotate(-2deg); }
.pill:nth-child(5n+2) { transform: rotate(1.5deg); }
.pill:nth-child(5n+4) { transform: rotate(-1deg); }
.pill:hover { transform: translate(-2px,-3px) rotate(0) scale(1.04); box-shadow: 8px 8px 0 var(--pink); }
.pill img { width: 18px; height: 18px; flex-shrink: 0; }
.pill.soon { background: var(--bg3); color: #fff; border-style: dashed; box-shadow: none; opacity: .7; }
.pill.soon:hover { background: var(--orange); color: #000; border-style: solid; box-shadow: 8px 8px 0 #000; opacity: 1; }
/* ── PROJECTS ── */
.proj-list { display: flex; flex-direction: column; gap: 22px; position: relative; z-index: 2; }
.proj {
background: var(--bg2); border: 4px solid #000; box-shadow: 10px 10px 0 #000;
padding: 28px 30px; position: relative; transition: transform .16s, box-shadow .16s;
display: block; overflow: hidden;
}
.proj:nth-child(odd) { transform: rotate(-1deg); }
.proj:nth-child(even) { transform: rotate(.8deg); }
.proj:hover { transform: rotate(0) translate(-3px,-3px) !important; box-shadow: 16px 16px 0 var(--pink); }
.proj-num {
position: absolute; top: 12px; right: 18px; font-family: var(--d);
font-size: clamp(50px, 8vw, 100px); line-height: 1;
color: transparent; -webkit-text-stroke: 1.5px rgba(255,255,255,.1);
user-select: none; pointer-events: none;
}
.proj-tags { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; flex-wrap: wrap; }
.tag {
font-family: var(--p); font-size: 7px; padding: 7px 11px;
border: 2px solid #000; box-shadow: 3px 3px 0 #000; color: #000; text-transform: uppercase;
}
.proj-title {
font-family: var(--d); font-size: clamp(24px, 4vw, 50px);
line-height: 1; text-transform: uppercase; display: flex; align-items: baseline; gap: 12px;
}
.proj-arrow { font-family: var(--p); font-size: 14px; color: var(--yellow); transition: transform .15s; }
.proj:hover .proj-arrow { transform: translate(4px,-4px); }
.proj-desc { color: var(--muted); margin-top: 12px; font-size: 13px; max-width: 620px; transition: color .15s; }
.proj:hover .proj-desc { color: #fff; }
/* ── SCOREBOARD ── */
.score-blocks { display: flex; flex-direction: column; gap: 36px; position: relative; z-index: 2; }
.score-block {}
.score-block-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.score-block-head .lvl-badge { font-family: var(--p); font-size: 7px; background: var(--cyan); color: #000; border: 3px solid #000; padding: 6px 11px; box-shadow: 3px 3px 0 #000; }
.score-block-head h3 { font-family: var(--d); font-size: clamp(20px, 2.8vw, 32px); text-transform: uppercase; }
.score-cells { display: grid; grid-template-columns: repeat(4,1fr); gap: 14px; }
.scell { border: 4px solid #000; padding: 22px 16px; text-align: center; box-shadow: 6px 6px 0 #000; background: var(--bg2); }
.scell .v { font-family: var(--d); font-size: clamp(24px, 3.8vw, 44px); display: block; line-height: 1; }
.scell .k { font-family: var(--p); font-size: 6px; margin-top: 8px; display: block; color: var(--muted); text-transform: uppercase; }
.scell.hi { } .scell.hi.c1 { background: var(--pink); color: #000; } .scell.hi.c1 .k { color: #000; }
.scell.hi.c2 { background: var(--yellow); color: #000; } .scell.hi.c2 .k { color: #000; }
.scell.hi.c3 { background: var(--cyan); color: #000; } .scell.hi.c3 .k { color: #000; }
.scell.hi.c4 { background: var(--orange); color: #000; } .scell.hi.c4 .k { color: #000; }
@media (max-width: 840px) { .score-cells { grid-template-columns: repeat(2,1fr); } }
/* ── DEVLOG ── */
.blog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; position: relative; z-index: 2; }
.blog-card {
border: 4px solid #000; background: var(--bg2); box-shadow: 8px 8px 0 #000;
padding: 24px; display: block; position: relative; transition: transform .14s, box-shadow .14s;
}
.blog-card:nth-child(3n+1) { transform: rotate(-1deg); }
.blog-card:nth-child(3n+2) { transform: rotate(.7deg) translateY(5px); }
.blog-card:nth-child(3n) { transform: rotate(-.5deg); }
.blog-card:hover { transform: translate(-3px,-3px) rotate(0) !important; box-shadow: 14px 14px 0 var(--cyan); }
.blog-date { font-family: var(--p); font-size: 7px; color: var(--pink); display: block; margin-bottom: 10px; }
.blog-title { font-family: var(--d); font-size: 17px; line-height: 1.2; text-transform: uppercase; margin-bottom: 9px; }
.blog-excerpt { font-size: 12px; color: var(--muted); margin-bottom: 14px; line-height: 1.7; }
.blog-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.btag { font-family: var(--p); font-size: 6px; background: #fff; color: #000; border: 2px solid #000; padding: 5px 8px; box-shadow: 2px 2px 0 #000; }
/* ── CONTACT ── */
.contact-rows { position: relative; z-index: 2; display: flex; flex-direction: column; border-top: 4px solid #000; }
.crow {
display: flex; align-items: center; justify-content: space-between; gap: 18px;
padding: 24px 18px; border: 4px solid #000; border-top: none; position: relative;
background: var(--bg2); transition: background .12s, padding-left .15s;
box-shadow: 6px 6px 0 #000; margin-bottom: 6px;
}
.crow:last-child { margin-bottom: 0; }
.crow .k { font-family: var(--p); font-size: 9px; color: var(--yellow); text-transform: uppercase; }
.crow .v { font-family: var(--d); font-size: clamp(16px, 2.8vw, 28px); text-transform: lowercase; display: flex; align-items: center; gap: 9px; }
.crow .arr { font-size: 20px; color: var(--pink); transition: transform .15s; }
.crow:hover { background: var(--pink); color: #000; padding-left: 28px; }
.crow:hover .k, .crow:hover .arr { color: #000; }
.crow:hover .arr { transform: translateX(5px); }
/* ── FOOTER ── */
.footer { position: relative; padding: 80px 5vw 36px; text-align: center; overflow: hidden; }
.footer-word {
font-family: var(--d); font-size: clamp(64px, 18vw, 260px); line-height: .88;
color: transparent; -webkit-text-stroke: 2px rgba(255,255,255,.1); text-transform: uppercase;
user-select: none; pointer-events: none;
}
.footer-meta {
font-family: var(--p); font-size: 8px; color: var(--muted);
margin-top: 24px; display: flex; justify-content: center; gap: 16px; flex-wrap: wrap;
}
.footer-meta a { color: #fff; text-decoration: underline; text-underline-offset: 4px; }
.footer-meta a:hover { color: var(--pink); }
.to-top { margin-top: 28px; }
/* ── KONAMI ── */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important; transition: none !important; }
.splash { display: none; }
}
@media (max-width: 700px) {
.sec { padding: 70px 5vw 60px; }
.hero-title .row-2 { margin-left: .5em; }
.fl { display: none; }
}
</style>
</head>
<body>
<!-- splash -->
<div class="splash" aria-hidden="true">
<div class="splash-letters"><span>M</span><span>I</span><span>L</span><span>E</span><span>S</span></div>
<div class="splash-tag">MAXIMALISM ONLINE</div>
</div>
<!-- fixed bg -->
<div class="bg-grid" aria-hidden="true"></div>
<div class="noise" aria-hidden="true"></div>
<div class="vignette" aria-hidden="true"></div>
<!-- cursor -->
<div class="cursor cursor-halo" id="halo"></div>
<div class="cursor cursor-dot" id="dot"></div>
<!-- progress bar -->
<div id="progress"></div>
<!-- nav -->
<nav class="nav">
<a class="nav-logo" href="#top"><span class="live"></span>MILES</a>
<div class="nav-links">
<a href="#about">About</a>
<a href="#skills">Skills</a>
<a href="#projects">Builds</a>
<a href="#devlog">Devlog</a>
<a href="#contact">Contact</a>
</div>
<div class="nav-right"><span class="pulse"></span>USUALLY ONLINE</div>
</nav>
<!-- ═══ HERO ═══ -->
<header class="hero" id="top">
<div class="hero-orb" aria-hidden="true">
<div class="ring ring-1"></div>
<div class="ring ring-2"></div>
<div class="ring ring-3"></div>
<div class="ring-core"></div>
</div>
<div class="fl fl-1">★ EST.<br>2008</div>
<div class="fl fl-2"></div>
<div class="fl fl-3">THE WOW</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 CO-FOUNDER</span>
</div>
<h1 class="hero-title" id="heroTitle">
<span class="row"><em>MI</em>L</span>
<span class="row row-2"><em>E</em>S<span class="star-dot">.</span></span>
</h1>
<p class="hero-sub">
I make <b>games</b>, <b>tools</b>, and <b>websites</b>.
Still in school, currently building a full <b>Electron replacement</b> — send help.
160 repos and counting.
</p>
<div class="hero-cta">
<a class="btn btn-pink" href="#projects">SEE THE BUILDS ↓</a>
<a class="btn btn-ghost" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener">GITHUB ↗</a>
<a class="btn btn-yellow" href="https://diskettelabs.com/" target="_blank" rel="noopener">DISKETTE LABS ↗</a>
</div>
<div class="hero-scroll" aria-hidden="true">SCROLL<div class="arr"></div></div>
</header>
<!-- marquee 1 -->
<div class="mq mq-y" aria-hidden="true">
<div class="mq-track fwd" id="mq1"></div>
</div>
<!-- ═══ STATS ═══ -->
<section class="sec" id="stats">
<div class="sec-ghost" aria-hidden="true">000</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 00 — THE NUMBERS</div>
<h2 class="sec-title">THE <span class="out">NUMBERS</span><span class="dot">.</span></h2>
</div>
<div class="stats-grid">
<div class="stat-card s1" data-in="left"><span class="tick"></span><span class="n num" data-count="160">0</span><span class="l">REPOS ONLINE</span></div>
<div class="stat-card s2" data-in="pop"><span class="tick"></span><span class="n num" data-count="91">0</span><span class="l">STARS EARNED</span></div>
<div class="stat-card s3" data-in="right"><span class="tick"></span><span class="n num" data-count="13">0</span><span class="l">WATCHING ME</span></div>
<div class="stat-card s4" data-in="flip"><span class="tick"></span><span class="n num" data-count="37">0</span><span class="l">I WATCH</span></div>
</div>
</section>
<!-- ═══ ABOUT ═══ -->
<section class="sec" id="about">
<div class="sec-ghost" aria-hidden="true">01</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 01 — IDENTITY</div>
<h2 class="sec-title"><span class="out">WHO'S</span> MILES<span class="dot">?</span></h2>
<p class="sec-sub">the human behind the commits. pancakes &gt; waffles, cereal is soup.</p>
</div>
<div class="about-layout">
<div class="avatar-wrap" data-in="left">
<img class="avatar-img" src="https://avatars.githubusercontent.com/u/115095719?v=4" alt="Miles Wolf Allen" loading="lazy" width="260" height="260">
<span class="avatar-sticker">@mileswolfallen2</span>
</div>
<div class="bio-card" data-in="up">
<h3>// PLAYER BIO</h3>
<p>I build things on the internet. Some are games, some are tools, and some are just because I thought it'd be fun. I'm co-founder of <b style="color:#fff">Diskette Labs</b>, still in school, and chasing a world without bundled Chromium.</p>
<div class="bio-badges">
<span class="badge b-pk">PULL SHARK ×2</span>
<span class="badge b-y">YOLO</span>
<span class="badge b-g">QUICKDRAW</span>
<span class="badge b-o">GD ADDICT</span>
</div>
<div class="bio-actions">
<a class="btn btn-yellow" href="https://diskettelabs.com/" target="_blank" rel="noopener">DISKETTE LABS ↗</a>
<a class="btn btn-ghost" href="../about-me.html">MORE ABOUT ME →</a>
</div>
</div>
</div>
</section>
<!-- marquee 2 -->
<div class="mq mq-p" aria-hidden="true">
<div class="mq-track rev" id="mq2"></div>
</div>
<!-- ═══ SKILLS ═══ -->
<section class="sec" id="skills">
<div class="sec-ghost" aria-hidden="true">02</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 02 — LOADOUT</div>
<h2 class="sec-title">MY <span class="out">LOADOUT</span><span class="dot">.</span></h2>
<p class="sec-sub">what I actually know how to use (and what I'm currently failing at).</p>
</div>
<div class="skills-body">
<div data-in="up">
<div class="skill-group-label">READY TO USE</div>
<div class="pills" id="pillsReady">
<span class="pill" data-si="javascript">JS</span>
<span class="pill" data-si="nodedotjs">NODE</span>
<span class="pill" data-si="html5">HTML</span>
<span class="pill" data-si="css3">CSS</span>
<span class="pill" data-si="python">PYTHON</span>
<span class="pill" data-si="git">GIT</span>
<span class="pill" data-si="blender">BLENDER</span>
<span class="pill" data-si="unity">UNITY</span>
<span class="pill" data-si="unrealengine">UNREAL</span>
<span class="pill" data-si="cloudflare">CLOUDFLARE</span>
<span class="pill" data-si="linux">LINUX</span>
<span class="pill" data-si="netlify">NETLIFY</span>
<span class="pill" data-si="gnubash">BASH</span>
<span class="pill" data-si="github">GITHUB</span>
<span class="pill" data-si="electron">ELECTRON</span>
<span class="pill" data-si="typescript">GELECTRON</span>
</div>
</div>
<div data-in="up">
<div class="skill-group-label">CURRENTLY UNLOCKING</div>
<div class="pills">
<span class="pill soon" data-si="php">PHP</span>
<span class="pill soon" data-si="dotnet">.NET</span>
<span class="pill soon" data-si="nixos">NIX</span>
<span class="pill soon" data-si="rust">RUST</span>
</div>
</div>
</div>
</section>
<!-- ═══ PROJECTS ═══ -->
<section class="sec" id="projects">
<div class="sec-ghost" aria-hidden="true">03</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 03 — BUILDS</div>
<h2 class="sec-title">THE <span class="out">BUILDS</span><span class="dot">.</span></h2>
<p class="sec-sub">stuff I've made (and some I forked). every one of these was at least 80% 2am.</p>
</div>
<div class="proj-list">
<a class="proj" href="../projects/omniemu.html">
<span class="proj-num">01</span>
<div class="proj-tags"><span class="tag" style="background:var(--green);">ACTIVE</span><span class="chip chip-c" style="font-size:6px;">MIT · v0.3.2 · TS</span></div>
<div class="proj-title">OmniEmu 2.0 <span class="proj-arrow"></span></div>
<p class="proj-desc">All your emulators in one place. Install 20+ emulators, scan your ROMs, launch games without opening 15 apps.</p>
</a>
<a class="proj" href="../projects/gelectron.html">
<span class="proj-num">02</span>
<div class="proj-tags"><span class="tag" style="background:var(--yellow);">EARLY</span><span class="chip chip-y" style="font-size:6px;">RUST + NODE · MIT</span></div>
<div class="proj-title">Gelectron <span class="proj-arrow"></span></div>
<p class="proj-desc">Drop-in Electron replacement using native web views instead of bundling Chromium. The send-help project.</p>
</a>
<a class="proj" href="https://vanilla.milesallen.site/" target="_blank" rel="noopener">
<span class="proj-num">03</span>
<div class="proj-tags"><span class="tag" style="background:var(--cyan);">UNRELEASED</span><span class="chip chip-c" style="font-size:6px;">VANILLA JS</span></div>
<div class="proj-title">Vanilla <span class="proj-arrow"></span></div>
<p class="proj-desc">A clean, minimal web experiment. Pure vanilla JS, no frameworks, no cruft.</p>
</a>
<a class="proj" href="../projects/fedl.html">
<span class="proj-num">04</span>
<div class="proj-tags"><span class="tag" style="background:var(--red);">LIVE</span><span class="chip chip-p" style="font-size:6px;">2196 COMMITS</span></div>
<div class="proj-title">FEDL <span class="proj-arrow"></span></div>
<p class="proj-desc">The demon list for Geometry Dash. Ranked levels, run submissions, and a community way better at the game than me.</p>
</a>
<a class="proj" href="../projects/command-move.html">
<span class="proj-num">05</span>
<div class="proj-tags"><span class="tag" style="background:var(--purple);">SWIFT</span><span class="chip chip-g" style="font-size:6px;">MACOS · MIT</span></div>
<div class="proj-title">CommandMove <span class="proj-arrow"></span></div>
<p class="proj-desc">True cut-and-paste for Finder on macOS. Cmd+X, navigate, Cmd+V. What Apple forgot to build.</p>
</a>
<a class="proj" href="../projects/noto.html">
<span class="proj-num">06</span>
<div class="proj-tags"><span class="tag" style="background:var(--green);">ACTIVE</span><span class="chip chip-c" style="font-size:6px;">FLUTTER · AI</span></div>
<div class="proj-title">Noto <span class="proj-arrow"></span></div>
<p class="proj-desc">Note-taking with AI built in. Flutter, powered by Cortex AI, actually good at organizing my chaos.</p>
</a>
<a class="proj" href="../projects/nova.html">
<span class="proj-num">07</span>
<div class="proj-tags"><span class="tag" style="background:var(--yellow);">PYTHON</span><span class="chip chip-y" style="font-size:6px;">MIT · LOCAL AI</span></div>
<div class="proj-title">Nova AI <span class="proj-arrow"></span></div>
<p class="proj-desc">AI that runs on your machine. Desktop GUI, web interface, CLI. No cloud, no subscriptions, just vibes.</p>
</a>
<a class="proj" href="../projects/music-app.html">
<span class="proj-num">08</span>
<div class="proj-tags"><span class="tag" style="background:var(--cyan);">JS</span><span class="chip chip-c" style="font-size:6px;">APACHE-2.0</span></div>
<div class="proj-title">Music App <span class="proj-arrow"></span></div>
<p class="proj-desc">Music player that doesn't interrupt you with ads. YouTube Music minus the annoying parts.</p>
</a>
<a class="proj" href="../projects/rhythm-game.html">
<span class="proj-num">09</span>
<div class="proj-tags"><span class="tag" style="background:var(--pink);">HTML</span><span class="chip chip-p" style="font-size:6px;">MPL-2.0</span></div>
<div class="proj-title">Rhythm Game <span class="proj-arrow"></span></div>
<p class="proj-desc">Hit keys to the beat. Song selection, real-time notes, keyboard controls. My fingers hurt.</p>
</a>
<a class="proj" href="../projects/geode-mod.html">
<span class="proj-num">10</span>
<div class="proj-tags"><span class="tag" style="background:var(--orange);">WIP</span><span class="chip chip-o" style="font-size:6px;">MIT</span></div>
<div class="proj-title">FEDL Geode Mod <span class="proj-arrow"></span></div>
<p class="proj-desc">Geometry Dash mod that shows the demon list in-game. Roulette mode, banned mod detection, more.</p>
</a>
<a class="proj" href="../projects/moon-gaming.html">
<span class="proj-num">11</span>
<div class="proj-tags"><span class="tag" style="background:var(--yellow);">HTML</span><span class="chip chip-y" style="font-size:6px;">1542 COMMITS</span></div>
<div class="proj-title">Moon Gaming <span class="proj-arrow"></span></div>
<p class="proj-desc">Game hub with 1,500+ commits. A growing library of browser games I made at 2am.</p>
</a>
</div>
</section>
<!-- marquee 3 -->
<div class="mq mq-c" aria-hidden="true">
<div class="mq-track fwd" id="mq3"></div>
</div>
<!-- ═══ SCOREBOARD ═══ -->
<section class="sec" id="scoreboard">
<div class="sec-ghost" aria-hidden="true">04</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 04 — SCOREBOARD</div>
<h2 class="sec-title">SCORE<span class="out">BOARD</span><span class="dot">.</span></h2>
<p class="sec-sub">what my projects have achieved. all numbers real, some embarrassing.</p>
</div>
<div class="score-blocks">
<div class="score-block" data-in="up">
<div class="score-block-head"><span class="lvl-badge">HIGH SCORE</span><h3>FEDL</h3></div>
<div class="score-cells">
<div class="scell hi c1"><span class="v num" data-count="3839">0</span><span class="k">VERIFIED RUNS</span></div>
<div class="scell hi c2"><span class="v num" data-count="341">0</span><span class="k">RANKED LEVELS</span></div>
<div class="scell hi c3"><span class="v num" data-count="250" data-suffix="+">0</span><span class="k">ACTIVE PLAYERS</span></div>
<div class="scell hi c4"><span class="v num" data-count="3800" data-suffix="+">0</span><span class="k">YOUTUBE VIDEOS</span></div>
</div>
</div>
<div class="score-block" data-in="up">
<div class="score-block-head"><span class="lvl-badge">STAGE CLEAR</span><h3>OmniEmu 2.0</h3></div>
<div class="score-cells">
<div class="scell hi c1"><span class="v num" data-count="20" data-suffix="+">0</span><span class="k">EMULATORS</span></div>
<div class="scell hi c2"><span class="v num" data-count="3">0</span><span class="k">PLATFORMS</span></div>
<div class="scell"><span class="v">v0.3.2</span><span class="k">LATEST</span></div>
<div class="scell"><span class="v">TS</span><span class="k">BUILT WITH</span></div>
</div>
</div>
</div>
</section>
<!-- ═══ DEVLOG ═══ -->
<section class="sec" id="devlog">
<div class="sec-ghost" aria-hidden="true">05</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 05 — DEVLOG</div>
<h2 class="sec-title">THE <span class="out">DEVLOG</span><span class="dot">.</span></h2>
<p class="sec-sub">things I've learned, broken, and fixed.</p>
</div>
<div class="blog-grid">
<a class="blog-card" href="../blog/diskette-devlog.html" data-in="up">
<span class="blog-date">2026 // 001</span>
<h3 class="blog-title">Focusing on Diskette Labs</h3>
<p class="blog-excerpt">I'm co-founder of Diskette Labs, a product design collective. Where my focus is now — still open to collabs.</p>
<div class="blog-tags"><span class="btag">DESIGN</span><span class="btag">COLLECTIVE</span></div>
</a>
<a class="blog-card" href="../blog/gelectron-devlog.html" data-in="up">
<span class="blog-date">2026 // 002</span>
<h3 class="blog-title">Why I Built Gelectron</h3>
<p class="blog-excerpt">Electron bundles Chromium with every app. 150-300MB per app, hundreds of MB of RAM. Native web views fix that.</p>
<div class="blog-tags"><span class="btag">RUST</span><span class="btag">NATIVE</span></div>
</a>
<a class="blog-card" href="../blog/omniemu-devlog.html" data-in="up">
<span class="blog-date">2026 // 003</span>
<h3 class="blog-title">Why I Built OmniEmu</h3>
<p class="blog-excerpt">I got tired of opening 15 different emulator apps. So I built one that manages them all.</p>
<div class="blog-tags"><span class="btag">TYPESCRIPT</span><span class="btag">ELECTRON</span></div>
</a>
<a class="blog-card" href="../blog/command-move-devlog.html" data-in="up">
<span class="blog-date">2026 // 004</span>
<h3 class="blog-title">CommandMove: Cut &amp; Paste</h3>
<p class="blog-excerpt">macOS doesn't have Cmd+X for files. I fixed that with global event taps and AppleScript.</p>
<div class="blog-tags"><span class="btag">SWIFT</span><span class="btag">MACOS</span></div>
</a>
<a class="blog-card" href="../blog/fedl-devlog.html" data-in="up">
<span class="blog-date">2025 // 005</span>
<h3 class="blog-title">Running the FEDL</h3>
<p class="blog-excerpt">What it takes to maintain a Geometry Dash demon list. 3,800+ verified runs and a community that never sleeps.</p>
<div class="blog-tags"><span class="btag">NODE.JS</span><span class="btag">COMMUNITY</span></div>
</a>
<a class="blog-card" href="../blog/nova-devlog.html" data-in="up">
<span class="blog-date">2025 // 006</span>
<h3 class="blog-title">Building Nova AI</h3>
<p class="blog-excerpt">Local AI that doesn't phone home. Desktop GUI, web interface, CLI — no subscriptions, no cloud.</p>
<div class="blog-tags"><span class="btag">PYTHON</span><span class="btag">AI</span></div>
</a>
</div>
</section>
<!-- marquee 4 -->
<div class="mq mq-o" aria-hidden="true">
<div class="mq-track rev" id="mq4"></div>
</div>
<!-- ═══ CONTACT ═══ -->
<section class="sec" id="contact">
<div class="sec-ghost" aria-hidden="true">06</div>
<div class="sec-header">
<div class="sec-eyebrow">LEVEL 06 — REACH OUT</div>
<h2 class="sec-title">GET IN <span class="out">TOUCH</span><span class="dot">.</span></h2>
<p class="sec-sub">unless you're a bot, then please go away.</p>
</div>
<div class="contact-rows" data-in="up">
<a class="crow" href="mailto:[email protected]"><span class="k">EMAIL</span><span class="v">[email protected]<span class="arr"></span></span></a>
<a class="crow" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener"><span class="k">GITHUB</span><span class="v">mileswolfallen2<span class="arr"></span></span></a>
<a class="crow" href="https://replit.com/@MilesAllen1" target="_blank" rel="noopener"><span class="k">REPLIT</span><span class="v">@MilesAllen1<span class="arr"></span></span></a>
<a class="crow" href="https://codepen.io/mileswolfallen2-the-typescripter" target="_blank" rel="noopener"><span class="k">CODEPEN</span><span class="v">mileswolfallen2<span class="arr"></span></span></a>
<a class="crow" href="https://me.fedl.site" target="_blank" rel="noopener"><span class="k">WEBSITE</span><span class="v">me.fedl.site<span class="arr"></span></span></a>
</div>
</section>
<!-- ═══ FOOTER ═══ -->
<footer class="footer">
<div class="footer-word" aria-hidden="true">MILES</div>
<div class="footer-meta">
<a href="#top">BACK TO TOP ↑</a>
<a href="../uses.html">USES</a>
<a href="../now.html">NOW</a>
<a href="../colophon.html">COLOPHON</a>
<span>BUILT BY MILES © 2026</span>
</div>
<div class="to-top">
<button class="btn btn-yellow" onclick="window.scrollTo({top:0,behavior:'smooth'})">↑ TOP</button>
</div>
</footer>
<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;
/* ── marquee content ── */
var mqSets = {
mq1: ['BUILDING EMULATORS','SEND HELP','160 REPOS','STILL DEBUGGING','GD ADDICT','USUALLY ONLINE','FULL MAXIMALISM','CEREAL IS SOUP'],
mq2: ['RATED ★★★★★','EST. 2008','PULL SHARK ×2','OVERCLOCKED','DISKETTE LABS','COFFEE COUNT: UNKNOWN','BUGS: INFINITE','YOLO'],
mq3: ['OMNIEMU 2.0','GELECTRON','FEDL','NOTO','NOVA AI','COMMANDMOVE','MOON GAMING','1500+ COMMITS','MIT LICENSE'],
mq4: ['EMAIL ME','PLEASE','GITHUB ↗','REPLIT ↗','CODEPEN ↗','I RESPOND','MAYBE','FAST'],
};
Object.keys(mqSets).forEach(function (id) {
var el = document.getElementById(id);
if (!el) return;
var half = '';
mqSets[id].forEach(function (t) { half += '<span>★ ' + t + '</span>'; });
el.innerHTML = half + half;
});
/* ── skill icons ── */
document.querySelectorAll('.pill[data-si]').forEach(function (pill) {
var img = document.createElement('img');
img.alt = ''; img.loading = 'lazy';
img.src = 'https://cdn.simpleicons.org/' + pill.getAttribute('data-si') + '/000000';
pill.insertBefore(img, pill.firstChild);
});
/* ── cursor ── */
var dotEl = document.getElementById('dot');
var haloEl = document.getElementById('halo');
if (dotEl && fine) {
var tx = -200, ty = -200, dx = -200, dy = -200, hx = -200, hy = -200;
document.addEventListener('mousemove', function (e) { tx = e.clientX; ty = e.clientY; }, { passive: true });
(function loop() {
dx += (tx - dx) * 0.38; dy += (ty - dy) * 0.38;
hx += (tx - hx) * 0.07; hy += (ty - hy) * 0.07;
dotEl.style.left = dx + 'px'; dotEl.style.top = dy + 'px';
haloEl.style.left = hx + 'px'; haloEl.style.top = hy + 'px';
requestAnimationFrame(loop);
})();
document.addEventListener('mouseover', function (e) {
if (e.target.closest && e.target.closest('a,button,.btn,.pill,.proj,.crow,.blog-card,.stat-card')) {
dotEl.classList.add('big');
}
}, { passive: true });
document.addEventListener('mouseout', function (e) {
if (e.target.closest && e.target.closest('a,button,.btn,.pill,.proj,.crow,.blog-card,.stat-card')) {
dotEl.classList.remove('big');
}
}, { passive: true });
}
/* ── scroll progress ── */
var prog = document.getElementById('progress');
function updateProg() {
var pct = window.scrollY / (document.body.scrollHeight - window.innerHeight) * 100;
prog.style.width = pct + '%';
}
window.addEventListener('scroll', updateProg, { passive: true });
updateProg();
/* ── count-up ── */
function countUp(el) {
var target = parseInt(el.getAttribute('data-count'), 10) || 0;
var suffix = el.getAttribute('data-suffix') || '';
if (reduce) { el.textContent = target.toLocaleString('en-US') + suffix; return; }
var t0 = null;
function step(ts) {
if (t0 === null) t0 = ts;
var p = Math.min(1, (ts - t0) / 1300);
var ease = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * ease).toLocaleString('en-US') + suffix;
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.5 });
document.querySelectorAll('.num[data-count]').forEach(function (el) { cio.observe(el); });
}
/* ── reveal system ── */
function revealAll() {
document.querySelectorAll('[data-in]').forEach(function (el) { el.classList.add('in'); });
}
if ('IntersectionObserver' in window && !reduce) {
var rio = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { e.target.classList.add('in'); rio.unobserve(e.target); }
});
}, { threshold: 0.12, rootMargin: '0px 0px -36px 0px' });
document.querySelectorAll('[data-in]').forEach(function (el) { rio.observe(el); });
} else {
revealAll();
}
/* ── konami ── */
(function () {
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; party(); }
});
function party() {
var colors = ['#FF2EA6','#F7FF00','#00F4FF','#FF5C00','#00FF85','#FF1F1F'];
for (var i = 0; i < 40; i++) {
var s = document.createElement('div');
s.style.cssText = 'position:fixed;left:50%;top:50%;z-index:9999;pointer-events:none;width:' + (6 + Math.random() * 14) + 'px;height:' + (6 + Math.random() * 14) + 'px;border:2px solid #000;background:' + colors[i % colors.length] + ';box-shadow:3px 3px 0 #000;';
document.body.appendChild(s);
var ang = Math.random() * Math.PI * 2;
var dist = 60 + Math.random() * 380;
s.animate([
{ transform: 'translate(-50%,-50%) rotate(0deg)', opacity: 1 },
{ transform: 'translate(calc(-50% + ' + (Math.cos(ang)*dist) + 'px), calc(-50% + ' + (Math.sin(ang)*dist) + '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);
}
}
})();
/* ── GSAP choreography ── */
if (window.gsap && window.ScrollTrigger && !reduce) {
gsap.registerPlugin(ScrollTrigger);
/* hero title parallax */
gsap.to('#heroTitle', {
yPercent: -16, scale: .95, opacity: .2, ease: 'none',
scrollTrigger: { trigger: '#top', start: 'top top', end: 'bottom top', scrub: 1 }
});
gsap.to('.hero-sub', {
yPercent: -55, opacity: 0, ease: 'none',
scrollTrigger: { trigger: '#top', start: '8% top', end: 'bottom top', scrub: 1 }
});
gsap.to('.hero-orb', {
scale: 1.18, opacity: .25, ease: 'none',
scrollTrigger: { trigger: '#top', start: 'top top', end: 'bottom top', scrub: 1 }
});
/* section title band slide */
gsap.utils.toArray('.sec-title').forEach(function (t) {
var dir = gsap.utils.toArray('.sec').indexOf(t.closest('.sec')) % 2 === 0 ? 1 : -1;
gsap.fromTo(t, { xPercent: dir * 7 }, { xPercent: dir * -7, ease: 'none',
scrollTrigger: { trigger: t, start: 'top bottom', end: 'bottom top', scrub: 1 }
});
});
/* project cards stagger in */
gsap.utils.toArray('.proj').forEach(function (card) {
var dir = card.dataset.in ? 0 : (Math.random() > .5 ? 1 : -1);
gsap.set(card, { transition: 'none' });
gsap.fromTo(card,
{ y: 80, rotation: dir * 3.5, opacity: 0 },
{ y: 0, rotation: 0, opacity: 1, duration: .9, ease: 'power3.out',
scrollTrigger: { trigger: card, start: 'top 95%' },
onComplete: function () { gsap.set(card, { clearProps: 'transform,opacity,transition' }); }
});
});
ScrollTrigger.refresh();
} else if (!reduce) {
setTimeout(function () {
document.querySelectorAll('.proj').forEach(function (c) { c.style.opacity = '1'; });
}, 1400);
}
})();
</script>
</body>
</html>