mirror of
https://github.com/mileswolfallen2/miles-web.git
synced 2026-09-08 11:23:17 +00:00
844 lines
50 KiB
HTML
844 lines
50 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 — SOFT ⇄ LOUD</title>
|
||
<meta name="description" content="Redesign expression three — modern simplistic maximalism. The page fluctuates between Swiss-quiet and neon-loud as you scroll.">
|
||
<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='%23FF2EA6'/%3E%3Ctext x='50' y='74' font-size='64' 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=Anton&family=Instrument+Serif:ital@0;1&family=Space+Grotesk:wght@400;500;700&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||
<style>
|
||
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
||
:root{
|
||
--paper:#F4F1EA;
|
||
--ink:#131311;
|
||
--mut:#7d7a72;
|
||
--line:rgba(19,19,17,.16);
|
||
--yellow:#F7FF00;
|
||
--pink:#FF2EA6;
|
||
--cyan:#00F4FF;
|
||
--orange:#FF5C00;
|
||
--green:#00FF85;
|
||
--purple:#8A2BFF;
|
||
--grot:'Space Grotesk',sans-serif;
|
||
--mono:'Space Mono',monospace;
|
||
--disp:'Anton',Impact,sans-serif;
|
||
--serif:'Instrument Serif',serif;
|
||
--ease:cubic-bezier(.16,1,.3,1);
|
||
}
|
||
html{scroll-behavior:smooth;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:var(--ink) var(--paper)}
|
||
body{
|
||
background:var(--paper);color:var(--ink);
|
||
font-family:var(--grot);font-size:16px;line-height:1.6;
|
||
overflow-x:hidden;-webkit-font-smoothing:antialiased;
|
||
transition:background .5s ease,color .5s ease;
|
||
}
|
||
::selection{background:var(--pink);color:#000}
|
||
a{color:inherit;text-decoration:none}
|
||
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
|
||
img,svg{display:block}
|
||
|
||
.spark{position:fixed;width:6px;height:6px;border-radius:50%;pointer-events:none;z-index:1200;margin:-3px}
|
||
|
||
.loader{position:fixed;inset:0;z-index:1100;background:var(--paper);color:var(--ink);
|
||
display:flex;flex-direction:column;justify-content:space-between;padding:28px 5vw}
|
||
.loader-top{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut)}
|
||
.loader-mid{align-self:center;font-family:var(--disp);font-size:clamp(60px,14vw,180px);line-height:1;text-transform:uppercase;letter-spacing:.01em}
|
||
.loader-mid.is-loud{color:transparent;-webkit-text-stroke:2px var(--pink)}
|
||
.loader-bot{display:flex;align-items:flex-end;justify-content:space-between;gap:24px}
|
||
.loader-count{font-family:var(--mono);font-weight:700;font-size:clamp(28px,5vw,54px);line-height:1}
|
||
.loader-count small{font-size:.4em;color:var(--mut);font-weight:400}
|
||
.loader-bar{flex:1;height:2px;background:var(--line);position:relative;overflow:hidden;margin-bottom:10px}
|
||
.loader-bar i{position:absolute;inset:0;background:var(--pink);transform-origin:left;transform:scaleX(0)}
|
||
|
||
.nav{position:sticky;top:0;z-index:800;display:flex;align-items:center;justify-content:space-between;gap:18px;
|
||
padding:14px 5vw;background:color-mix(in srgb,var(--paper) 88%,transparent);
|
||
backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||
.brand{font-family:var(--mono);font-weight:700;font-size:13px;letter-spacing:.06em}
|
||
.brand span{color:var(--mut);font-weight:400}
|
||
.nav-links{display:flex;gap:26px}
|
||
.nav-links a{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);transition:color .25s}
|
||
.nav-links a:hover{color:var(--pink)}
|
||
.switch{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
|
||
.switch input{position:absolute;opacity:0;pointer-events:none}
|
||
.sw-track{width:44px;height:22px;border:1px solid var(--ink);border-radius:99px;position:relative;transition:background .3s,border-color .3s}
|
||
.sw-thumb{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ink);transition:transform .35s var(--ease),background .3s}
|
||
.switch input:checked + .sw-track{background:var(--pink);border-color:var(--pink)}
|
||
.switch input:checked + .sw-track .sw-thumb{transform:translateX(22px);background:#000}
|
||
.sw-txt{font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:var(--mut);min-width:38px}
|
||
.sw-txt.on{color:var(--pink)}
|
||
|
||
.vu{position:fixed;right:26px;top:50%;transform:translateY(-50%);z-index:700;
|
||
display:flex;flex-direction:column;align-items:center;gap:12px;--vu-c:var(--ink)}
|
||
.vu-cap{font-family:var(--mono);font-size:9px;letter-spacing:.1em;color:var(--mut)}
|
||
.vu-track{width:3px;height:32vh;background:var(--line);position:relative;border-radius:2px}
|
||
.vu-fill{position:absolute;bottom:0;left:0;width:100%;height:0;background:var(--vu-c);border-radius:2px;transition:background .4s}
|
||
.vu-needle{position:absolute;left:50%;top:0;width:16px;height:2px;background:var(--vu-c);transform:translate(-50%,-50%);transition:background .4s}
|
||
.vu-read{writing-mode:vertical-rl;font-family:var(--mono);font-size:9px;letter-spacing:.3em;color:var(--vu-c);transition:color .4s}
|
||
|
||
section{position:relative}
|
||
.q{background:var(--paper);color:var(--ink);padding:clamp(90px,11vw,150px) 5vw;transition:background .5s ease,color .5s ease}
|
||
.l{color:#000;padding:clamp(80px,10vw,130px) 5vw;overflow:hidden}
|
||
|
||
.q-head{display:flex;align-items:baseline;gap:22px;border-top:1px solid var(--line);padding-top:20px;margin-bottom:clamp(44px,6vw,84px)}
|
||
.q-no{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||
.q-title{font-family:var(--grot);font-weight:500;font-size:clamp(30px,4.6vw,62px);letter-spacing:-.02em;line-height:1}
|
||
.q-tag{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.18em;color:var(--mut);white-space:nowrap}
|
||
.l-head{display:flex;align-items:flex-start;gap:22px;margin-bottom:clamp(40px,5vw,70px);flex-wrap:wrap}
|
||
.l-no{font-family:var(--mono);font-size:11px;background:#000;color:#fff;padding:6px 10px;transform:rotate(-2deg)}
|
||
.l-title{font-family:var(--disp);font-size:clamp(52px,10vw,140px);line-height:.92;text-transform:uppercase;letter-spacing:.01em}
|
||
.l-tag{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.18em;background:#000;color:#fff;padding:8px 12px;transform:rotate(1.5deg);white-space:nowrap}
|
||
|
||
.hero{min-height:100svh;display:flex;flex-direction:column;padding-top:clamp(40px,8vh,90px);padding-bottom:0}
|
||
.hero-eyebrow{display:flex;justify-content:space-between;gap:16px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut);
|
||
border-bottom:1px solid var(--line);padding-bottom:14px;flex-wrap:wrap}
|
||
.hero-name{font-family:var(--disp);text-transform:uppercase;line-height:.86;letter-spacing:.005em;
|
||
font-size:clamp(66px,15.5vw,230px);position:relative;z-index:2;margin-block:auto;padding-block:clamp(24px,4vh,48px)}
|
||
.hero-name .ln{display:block}
|
||
.hero-name .outline{color:transparent;-webkit-text-stroke:2.5px var(--ink);transition:-webkit-text-stroke .5s}
|
||
.hero-star{display:inline-block;color:var(--pink);font-size:.42em;vertical-align:.62em;margin-left:.08em;animation:spin 7s linear infinite}
|
||
@keyframes spin{to{rotate:360deg}}
|
||
.chw{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.06em;margin-bottom:-.06em}
|
||
.ch{display:inline-block;will-change:transform}
|
||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
|
||
.hero-low{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:clamp(28px,4vw,60px);align-items:start;
|
||
border-top:1px solid var(--line);padding:26px 0 40px}
|
||
.hero-state{font-size:clamp(19px,2.3vw,30px);line-height:1.35;font-weight:500;letter-spacing:-.01em;max-width:24ch}
|
||
.hero-state em{font-family:var(--serif);font-style:italic;font-weight:400;font-size:1.12em}
|
||
.side-status{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-weight:700;font-size:11px;letter-spacing:.1em;text-transform:uppercase;
|
||
border:1px solid var(--ink);border-radius:99px;padding:10px 16px;margin-bottom:14px}
|
||
.side-status .dot{width:8px;height:8px;border-radius:50%;background:var(--green);animation:pulse 1.6s ease-in-out infinite}
|
||
.side-row{display:flex;justify-content:space-between;gap:14px;font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);padding:11px 0;border-bottom:1px solid var(--line)}
|
||
.side-row:last-child{border-bottom:none}
|
||
.side-row b{color:var(--ink);font-weight:700}
|
||
|
||
.band{background:#000;color:var(--yellow);overflow:hidden;transform:rotate(-1.2deg) scale(1.02);border-block:3px solid #000;position:relative;z-index:5}
|
||
.marquee{display:flex;width:max-content;animation:mq 26s linear infinite}
|
||
.band.rev .marquee{animation-direction:reverse}
|
||
.band:hover .marquee{animation-play-state:paused}
|
||
.mq-set{display:flex;align-items:center;gap:38px;padding-right:38px;
|
||
font-family:var(--disp);text-transform:uppercase;font-size:clamp(26px,4.6vw,58px);line-height:1;padding-block:16px;white-space:nowrap}
|
||
.mq-set .ast{color:var(--pink);animation:spin 5s linear infinite}
|
||
.mq-set .ghost{color:transparent;-webkit-text-stroke:1.5px var(--yellow)}
|
||
@keyframes mq{to{transform:translateX(-50%)}}
|
||
|
||
.about-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:clamp(40px,6vw,90px)}
|
||
.about-state{font-size:clamp(26px,3.6vw,50px);line-height:1.22;font-weight:500;letter-spacing:-.02em;margin-bottom:34px}
|
||
.about-state em{font-family:var(--serif);font-style:italic;font-weight:400;color:var(--pink);font-size:1.08em;transition:color .5s}
|
||
body.loud-mode .about-state em{color:var(--cyan)}
|
||
.about-copy p{max-width:56ch;color:var(--mut);margin-bottom:18px;font-size:15px}
|
||
.about-copy p b{color:var(--ink);font-weight:600}
|
||
.about-links{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
|
||
.pill{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;border:1px solid var(--ink);
|
||
padding:11px 18px;border-radius:99px;transition:all .3s var(--ease)}
|
||
.pill:hover{background:var(--ink);color:var(--paper);transform:translateY(-2px)}
|
||
.facts{border-top:1px solid var(--line)}
|
||
.fact{display:grid;grid-template-columns:110px 1fr;gap:16px;padding:16px 0;border-bottom:1px solid var(--line);align-items:baseline}
|
||
.fact dt{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut)}
|
||
.fact dd{font-size:15px;font-weight:500}
|
||
.fact dd .live{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green);margin-right:8px;animation:pulse 1.6s infinite}
|
||
.badge-chip{display:inline-block;font-family:var(--mono);font-size:10px;border:1px solid var(--line);padding:4px 9px;margin:2px 4px 2px 0;border-radius:99px}
|
||
|
||
.loadout{background:var(--pink)}
|
||
.loadout .l-title{text-shadow:5px 5px 0 #000}
|
||
.loadout .l-no,.loadout .l-tag{background:#000}
|
||
.chip-group{display:flex;align-items:baseline;gap:14px;margin:0 0 16px;font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase}
|
||
.chip-group:first-child{margin-top:0}
|
||
.chip-group + .chip-wall{margin-bottom:38px}
|
||
.chip-group i{flex:1;height:1px;background:rgba(0,0,0,.35)}
|
||
.chip-wall{display:flex;flex-wrap:wrap;gap:14px}
|
||
.chip{--r:0deg;font-family:var(--mono);font-weight:700;font-size:clamp(13px,1.5vw,17px);letter-spacing:.04em;text-transform:uppercase;
|
||
background:#fff;color:#000;border:2px solid #000;padding:14px 22px;rotate:var(--r);
|
||
box-shadow:4px 4px 0 #000;cursor:default;transition:box-shadow .2s,translate .2s;will-change:transform}
|
||
.chip:hover{animation:jello 1s;translate:-2px -2px;box-shadow:8px 8px 0 #000}
|
||
.chip.locked{background:transparent;color:#fff;border-style:dashed;box-shadow:none}
|
||
.chip.locked:hover{animation:rubberBand 1s}
|
||
|
||
.builds .rows{border-top:1px solid var(--line)}
|
||
.row{position:relative;display:grid;grid-template-columns:64px minmax(0,1fr) auto 44px;gap:20px;align-items:center;
|
||
padding:26px 10px;border-bottom:1px solid var(--line);overflow:hidden;isolation:isolate}
|
||
.row::before{content:'';position:absolute;inset:0;background:var(--ink);transform:scaleY(0);transform-origin:bottom;
|
||
transition:transform .45s var(--ease);z-index:-1}
|
||
.row::after{content:'';position:absolute;top:0;bottom:0;left:-80%;width:45%;
|
||
background:linear-gradient(105deg,transparent,rgba(255,255,255,.28),transparent);transform:skewX(-18deg);transition:left .7s var(--ease)}
|
||
.row:hover::before{transform:scaleY(1)}
|
||
.row:hover::after{left:130%}
|
||
.row-no{font-family:var(--mono);font-size:11px;color:var(--mut);transition:color .3s}
|
||
.row-title{display:block;font-weight:700;font-size:clamp(21px,2.8vw,36px);letter-spacing:-.02em;line-height:1.1;transition:transform .45s var(--ease),color .3s}
|
||
.row-desc{display:block;color:var(--mut);font-size:13px;margin-top:5px;max-width:52ch;transition:color .3s}
|
||
.row-meta{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);white-space:nowrap;transition:color .3s}
|
||
.row-arrow{font-size:20px;justify-self:end;transition:transform .45s var(--ease),color .3s}
|
||
.row:hover .row-title{color:var(--paper);transform:translateX(10px)}
|
||
.row:hover .row-desc,.row:hover .row-meta,.row:hover .row-no{color:rgba(244,241,234,.65)}
|
||
.row:hover .row-arrow{color:var(--yellow);transform:translate(4px,-4px)}
|
||
|
||
.now{background:var(--yellow);color:#000}
|
||
.now .l-no{background:#000;color:var(--yellow)}
|
||
.now .l-tag{background:#000;color:var(--yellow)}
|
||
.focus-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
|
||
.focus{display:flex;flex-direction:column;gap:14px;border:2px solid #000;padding:26px 24px 30px;
|
||
box-shadow:6px 6px 0 #000;transition:transform .35s var(--ease),box-shadow .35s var(--ease)}
|
||
.focus:hover{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #000}
|
||
.focus-no{font-family:var(--mono);font-size:11px}
|
||
.focus-title{font-family:var(--disp);font-size:clamp(26px,2.6vw,40px);line-height:.95;text-transform:uppercase}
|
||
.focus-desc{font-size:14px;line-height:1.55;font-weight:500}
|
||
.focus-status{align-self:flex-start;font-family:var(--mono);font-size:10px;letter-spacing:.14em;background:#000;color:var(--yellow);padding:6px 10px;transform:rotate(-1.5deg);margin-top:auto}
|
||
.now-backburner{margin-top:28px;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;border-top:2px solid #000;padding-top:16px;
|
||
font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase}
|
||
|
||
.repeat-rows{border-top:1px solid var(--line)}
|
||
.rep{display:grid;grid-template-columns:56px minmax(0,1.1fr) minmax(0,1fr) auto;gap:20px;align-items:baseline;padding:20px 8px;border-bottom:1px solid var(--line);transition:padding-left .35s var(--ease)}
|
||
.rep:hover{padding-left:22px}
|
||
.rep-no{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||
.rep-main{min-width:0}
|
||
.rep-artist{display:block;font-weight:700;font-size:clamp(17px,2vw,24px);letter-spacing:-.01em}
|
||
.rep-work{display:block;font-family:var(--serif);font-style:italic;color:var(--mut);font-size:15px;margin-top:2px}
|
||
.rep-track{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||
.rep-count{font-family:var(--mono);font-size:11px;letter-spacing:.12em;white-space:nowrap}
|
||
.repeat-foot{margin-top:22px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut)}
|
||
.repeat-foot a{border-bottom:1px solid var(--pink);transition:color .25s}
|
||
.repeat-foot a:hover{color:var(--pink)}
|
||
|
||
.score{background:#0c0c0b;color:#fff}
|
||
.score .l-no{background:var(--yellow);color:#000}
|
||
.score .l-tag{background:var(--yellow);color:#000}
|
||
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));border:2px solid #fff}
|
||
.stat{padding:clamp(26px,3.4vw,46px) clamp(20px,2.6vw,36px);border-right:2px solid #fff;position:relative;overflow:hidden;isolation:isolate}
|
||
.stat:last-child{border-right:none}
|
||
.stat::before{content:'';position:absolute;inset:0;background:var(--sc,var(--yellow));transform:translateY(101%);transition:transform .45s var(--ease);z-index:-1}
|
||
.stat:hover::before{transform:translateY(0)}
|
||
.stat-n{display:block;font-family:var(--disp);font-size:clamp(52px,7.5vw,110px);line-height:.95;color:var(--sc,var(--yellow));transition:color .3s}
|
||
.stat:hover .stat-n{color:#000}
|
||
.stat-l{display:block;margin-top:12px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.55);transition:color .3s}
|
||
.stat:hover .stat-l{color:#000}
|
||
|
||
.devlog .entries{border-top:1px solid var(--line)}
|
||
.entry{display:grid;grid-template-columns:90px minmax(0,1fr) auto;gap:22px;align-items:baseline;padding:24px 8px;border-bottom:1px solid var(--line);position:relative;transition:padding-left .35s var(--ease)}
|
||
.entry:hover{padding-left:26px}
|
||
.entry::before{content:'✳';position:absolute;left:2px;color:var(--pink);opacity:0;transition:opacity .3s;font-size:14px}
|
||
.entry:hover::before{opacity:1}
|
||
.entry-year{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||
.entry-title{font-weight:700;font-size:clamp(18px,2.4vw,30px);letter-spacing:-.015em}
|
||
.entry-tags{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);white-space:nowrap}
|
||
|
||
.contact{background:var(--cyan)}
|
||
.contact .l-title{font-size:clamp(76px,19vw,270px);text-shadow:7px 7px 0 #000}
|
||
.contact .sayhi .ch{display:inline-block;animation:floaty 2.6s ease-in-out infinite;animation-delay:calc(var(--i)*.09s)}
|
||
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-.07em)}}
|
||
.contact-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(36px,5vw,80px);align-items:start;margin-top:clamp(36px,5vw,60px)}
|
||
.contact-copy p{max-width:44ch;font-weight:500;font-size:clamp(16px,1.8vw,21px);line-height:1.5}
|
||
.contact-copy p em{font-family:var(--serif);font-style:italic;font-size:1.1em}
|
||
.magnet{display:inline-flex;align-items:center;gap:14px;margin-top:34px;background:#000;color:var(--cyan);
|
||
font-family:var(--mono);font-weight:700;font-size:clamp(14px,1.7vw,19px);letter-spacing:.03em;
|
||
padding:20px 30px;border:2px solid #000;box-shadow:6px 6px 0 #fff;will-change:transform}
|
||
.magnet:hover{box-shadow:10px 10px 0 #fff}
|
||
.socials{border-top:2px solid #000}
|
||
.soc{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:17px 4px;border-bottom:2px solid #000;
|
||
font-family:var(--mono);font-weight:700;font-size:clamp(13px,1.5vw,17px);text-transform:uppercase;letter-spacing:.05em;transition:padding-left .3s var(--ease),background .3s}
|
||
.soc:hover{padding-left:18px;background:rgba(0,0,0,.08)}
|
||
.soc span:last-child{font-weight:400;font-size:12px}
|
||
|
||
.foot{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
|
||
padding:26px 5vw;border-top:1px solid var(--line);
|
||
font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut)}
|
||
.foot b{color:var(--pink)}
|
||
.foot-links a{transition:color .25s}
|
||
.foot-links a:hover{color:var(--pink)}
|
||
|
||
html.js .rv{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease) var(--d,0s),transform .9s var(--ease) var(--d,0s)}
|
||
html.js .rv.is-in{opacity:1;transform:none}
|
||
html.js .chip{opacity:0}
|
||
html.nf .chip,html.rm .chip{opacity:1}
|
||
.bt .bw{display:inline-block;opacity:0;filter:blur(10px);transform:translateY(.35em);
|
||
transition:opacity .7s var(--ease) calc(var(--i)*22ms),filter .7s var(--ease) calc(var(--i)*22ms),transform .7s var(--ease) calc(var(--i)*22ms)}
|
||
.bt.is-in .bw{opacity:1;filter:blur(0);transform:none}
|
||
.shine{background:linear-gradient(110deg,currentColor 42%,var(--pink) 50%,currentColor 58%);background-size:240% 100%;
|
||
-webkit-background-clip:text;background-clip:text;color:transparent;animation:shine 3.4s linear infinite}
|
||
@keyframes shine{to{background-position:-240% 0}}
|
||
|
||
html.nf .rv,html.rm .rv{opacity:1;transform:none}
|
||
html.nf .bt .bw,html.rm .bt .bw{opacity:1;filter:none;transform:none}
|
||
|
||
body.loud-mode{--paper:#101010;--ink:#F4F1EA;--mut:#8f8c83;--line:rgba(244,241,234,.17)}
|
||
body.loud-mode .q-title,body.loud-mode .about-state{color:var(--paper)}
|
||
body.loud-mode .q-title{text-shadow:4px 4px 0 var(--acc,var(--pink))}
|
||
body.loud-mode .row::before{background:var(--pink)}
|
||
body.loud-mode .row:hover .row-title{color:#000}
|
||
body.loud-mode .row:hover .row-desc,body.loud-mode .row:hover .row-meta,body.loud-mode .row:hover .row-no{color:rgba(0,0,0,.65)}
|
||
body.loud-mode .pill:hover{background:var(--pink);border-color:var(--pink);color:#000}
|
||
body.loud-mode .loader{background:#101010;color:#F4F1EA}
|
||
|
||
@media(max-width:960px){
|
||
.vu{display:none}
|
||
.about-grid,.contact-grid,.hero-low{grid-template-columns:1fr}
|
||
.focus-grid{grid-template-columns:1fr}
|
||
}
|
||
@media(max-width:760px){
|
||
.nav-links{display:none}
|
||
.row{grid-template-columns:40px minmax(0,1fr) 34px}
|
||
.row-meta{display:none}
|
||
.entry{grid-template-columns:60px minmax(0,1fr)}
|
||
.entry-tags{display:none}
|
||
.q-tag{display:none}
|
||
.rep{grid-template-columns:40px minmax(0,1fr) auto}
|
||
.rep-track{display:none}
|
||
.stat{border-right:none;border-bottom:2px solid #fff}
|
||
.stat:last-child{border-bottom:none}
|
||
}
|
||
@media(prefers-reduced-motion:reduce){
|
||
*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
|
||
html{scroll-behavior:auto}
|
||
html.js .rv{opacity:1;transform:none}
|
||
.bt .bw{opacity:1;filter:none;transform:none}
|
||
}
|
||
</style>
|
||
<noscript><style>
|
||
.loader{display:none}
|
||
.rv,.chip{opacity:1 !important;transform:none !important}
|
||
.bt .bw{opacity:1;filter:none;transform:none}
|
||
</style></noscript>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="loader" id="loader" aria-hidden="true">
|
||
<div class="loader-top"><span>PORTFOLIO — VOL.09</span><span>SOFT ⇄ LOUD</span></div>
|
||
<div class="loader-mid" id="loaderWord">SOFT</div>
|
||
<div class="loader-bot">
|
||
<div class="loader-count"><span id="loaderNum">000</span><small>%</small></div>
|
||
<div class="loader-bar"><i id="loaderBar"></i></div>
|
||
</div>
|
||
</div>
|
||
|
||
<header class="nav" id="nav">
|
||
<a class="brand" href="#top">M—WA <span>©26</span></a>
|
||
<nav class="nav-links" aria-label="Sections">
|
||
<a href="#about" data-scramble>Identity</a>
|
||
<a href="#loadout" data-scramble>Loadout</a>
|
||
<a href="#builds" data-scramble>Builds</a>
|
||
<a href="#now" data-scramble>Now</a>
|
||
<a href="#devlog" data-scramble>Devlog</a>
|
||
<a href="#contact" data-scramble>Contact</a>
|
||
</nav>
|
||
<label class="switch">
|
||
<input type="checkbox" id="loudToggle" aria-label="Force loud mode">
|
||
<span class="sw-track"><i class="sw-thumb"></i></span>
|
||
<span class="sw-txt" id="swTxt">AUTO</span>
|
||
</label>
|
||
</header>
|
||
|
||
<aside class="vu" id="vu" aria-hidden="true">
|
||
<span class="vu-cap">S</span>
|
||
<div class="vu-track"><div class="vu-fill" id="vuFill"></div><div class="vu-needle" id="vuNeedle"></div></div>
|
||
<span class="vu-cap">L</span>
|
||
<div class="vu-read" id="vuRead">SOFT</div>
|
||
</aside>
|
||
|
||
<main id="top">
|
||
|
||
<section class="q hero" data-vibe="0.18">
|
||
<div class="hero-eyebrow" id="heroEyebrow">
|
||
<span>Portfolio — Vol.09</span>
|
||
<span class="shine">Modern Simplistic Maximalism</span>
|
||
<span>milesallen.site</span>
|
||
</div>
|
||
<h1 class="hero-name" id="heroName">
|
||
<span class="ln" data-split>MILES</span>
|
||
<span class="ln outline" data-split>WOLF ALLEN</span>
|
||
<span class="hero-star" aria-hidden="true">*</span>
|
||
</h1>
|
||
<div class="hero-low">
|
||
<p class="hero-state" id="heroState">I make games, tools & websites that feel <em>good</em> to use.</p>
|
||
<div class="hero-side" id="heroSide">
|
||
<span class="side-status"><i class="dot"></i>Usually online</span>
|
||
<div class="side-row"><span>Direction</span><b>scroll ↓</b></div>
|
||
<div class="side-row"><span>Public repos</span><b>160+</b></div>
|
||
<div class="side-row"><span>Loud mode</span><b>press L</b></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="band" aria-hidden="true">
|
||
<div class="marquee">
|
||
<div class="mq-set">
|
||
<span>Building emulators</span><span class="ast">*</span>
|
||
<span class="ghost">Send help</span><span class="ast">*</span>
|
||
<span>160 repos and counting</span><span class="ast">*</span>
|
||
<span class="ghost">Still debugging</span><span class="ast">*</span>
|
||
<span>Geometry Dash addict</span><span class="ast">*</span>
|
||
<span class="ghost">Usually online</span><span class="ast">*</span>
|
||
</div>
|
||
<div class="mq-set">
|
||
<span>Building emulators</span><span class="ast">*</span>
|
||
<span class="ghost">Send help</span><span class="ast">*</span>
|
||
<span>160 repos and counting</span><span class="ast">*</span>
|
||
<span class="ghost">Still debugging</span><span class="ast">*</span>
|
||
<span>Geometry Dash addict</span><span class="ast">*</span>
|
||
<span class="ghost">Usually online</span><span class="ast">*</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="q about" id="about" data-vibe="0.08" style="--acc:var(--pink)">
|
||
<header class="q-head rv">
|
||
<span class="q-no">01</span>
|
||
<h2 class="q-title">Who's Miles</h2>
|
||
<span class="q-tag">Identity — Soft</span>
|
||
</header>
|
||
<div class="about-grid">
|
||
<div class="about-copy">
|
||
<p class="about-state bt">Software should feel <em>good</em> to use — so I build emulators, games & tools until it does.</p>
|
||
<p class="rv">I build things on the internet. Some of them are games, some are tools, and some exist purely because I thought it would be fun. <b>160 repos and 91 stars later</b>, I'm still figuring it out as I go.</p>
|
||
<p class="rv" style="--d:.08s">Right now I'm co-founder of <b>Diskette Labs</b>, building <b>Gelectron</b> (a full Electron replacement), and running <b>FEDL</b> — the Geometry Dash demon list. Still in school, somehow.</p>
|
||
<div class="about-links rv" style="--d:.14s">
|
||
<a class="pill" href="https://milesallen.site/about-me.html">More about me →</a>
|
||
<a class="pill" href="https://diskettelabs.com/" target="_blank" rel="noopener">Diskette Labs ↗</a>
|
||
</div>
|
||
</div>
|
||
<dl class="facts rv" style="--d:.1s">
|
||
<div class="fact"><dt>Status</dt><dd><span class="live"></span>usually online</dd></div>
|
||
<div class="fact"><dt>Crew</dt><dd>Diskette Labs — co-founder</dd></div>
|
||
<div class="fact"><dt>Badges</dt><dd><span class="badge-chip">Pull Shark ×2</span><span class="badge-chip">YOLO</span><span class="badge-chip">Quickdraw</span></dd></div>
|
||
<div class="fact"><dt>Believes</dt><dd>pancakes > waffles · cereal is soup</dd></div>
|
||
<div class="fact"><dt>Stack</dt><dd>JS · TS · Rust · Swift · Python · Blender</dd></div>
|
||
</dl>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="l loadout" id="loadout" data-vibe="1">
|
||
<header class="l-head">
|
||
<span class="l-no">02</span>
|
||
<h2 class="l-title">My<br>Loadout</h2>
|
||
<span class="l-tag">Skills — Loud</span>
|
||
</header>
|
||
<div class="chip-group">Ready to use<i></i>16</div>
|
||
<div class="chip-wall" id="chipWall" data-io>
|
||
<span class="chip" data-r="-3">JavaScript</span>
|
||
<span class="chip" data-r="2">Backend JS</span>
|
||
<span class="chip" data-r="-1">HTML</span>
|
||
<span class="chip" data-r="3">CSS</span>
|
||
<span class="chip" data-r="-2">Python</span>
|
||
<span class="chip" data-r="1">Git</span>
|
||
<span class="chip" data-r="-3">Blender</span>
|
||
<span class="chip" data-r="2">Unity</span>
|
||
<span class="chip" data-r="-1">Unreal</span>
|
||
<span class="chip" data-r="3">Cloudflare</span>
|
||
<span class="chip" data-r="-2">Linux</span>
|
||
<span class="chip" data-r="1">Netlify</span>
|
||
<span class="chip" data-r="-3">Bash</span>
|
||
<span class="chip" data-r="2">GitHub</span>
|
||
<span class="chip" data-r="-1">Electron</span>
|
||
<span class="chip" data-r="3">Gelectron</span>
|
||
</div>
|
||
<div class="chip-group">Currently unlocking<i></i>4</div>
|
||
<div class="chip-wall" data-io>
|
||
<span class="chip locked" data-r="-2">PHP</span>
|
||
<span class="chip locked" data-r="1">.NET</span>
|
||
<span class="chip locked" data-r="2">Nix</span>
|
||
<span class="chip locked" data-r="-1">Yarn</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="q builds" id="builds" data-vibe="0.08" style="--acc:var(--cyan)">
|
||
<header class="q-head rv">
|
||
<span class="q-no">03</span>
|
||
<h2 class="q-title">The Builds</h2>
|
||
<span class="q-tag">Projects — Soft</span>
|
||
</header>
|
||
<div class="rows">
|
||
<a class="row rv" href="https://milesallen.site/projects/omniemu.html"><span class="row-no">01</span><span class="row-main"><span class="row-title">OmniEmu 2.0</span><span class="row-desc">All your emulators in one place. Install 20+, scan ROMs, launch games.</span></span><span class="row-meta">Active · TS · MIT</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.03s" href="https://milesallen.site/projects/gelectron.html"><span class="row-no">02</span><span class="row-main"><span class="row-title">Gelectron</span><span class="row-desc">Drop-in Electron replacement on native web views. No bundled Chromium.</span></span><span class="row-meta">Early · Rust</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.06s" href="https://vanilla.milesallen.site/" target="_blank" rel="noopener"><span class="row-no">03</span><span class="row-main"><span class="row-title">Vanilla</span><span class="row-desc">A clean, minimal web experiment. No frameworks, no cruft.</span></span><span class="row-meta">Unreleased · JS</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.09s" href="https://me.fedl.site/" target="_blank" rel="noopener"><span class="row-no">04</span><span class="row-main"><span class="row-title">FEDL</span><span class="row-desc">The demon list for Geometry Dash. Ranked levels, verified runs.</span></span><span class="row-meta">Live · 3800+ runs</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.12s" href="https://milesallen.site/projects/command-move.html"><span class="row-no">05</span><span class="row-main"><span class="row-title">CommandMove</span><span class="row-desc">True cut-and-paste for Finder. Cmd+X, navigate, Cmd+V.</span></span><span class="row-meta">macOS · Swift</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.15s" href="https://milesallen.site/projects/noto.html"><span class="row-no">06</span><span class="row-main"><span class="row-title">Noto</span><span class="row-desc">Note-taking with AI built in. Powered by Cortex, good at organizing chaos.</span></span><span class="row-meta">Active · Flutter</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.18s" href="https://milesallen.site/projects/nova.html"><span class="row-no">07</span><span class="row-main"><span class="row-title">Nova AI</span><span class="row-desc">Local AI assistant. Desktop GUI, web interface and CLI. No cloud.</span></span><span class="row-meta">Python · MIT</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.21s" href="https://milesallen.site/projects/music-app.html"><span class="row-no">08</span><span class="row-main"><span class="row-title">Music App</span><span class="row-desc">YouTube Music energy without the annoying parts.</span></span><span class="row-meta">JS · Apache-2.0</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.24s" href="https://milesallen.site/projects/rhythm-game.html"><span class="row-no">09</span><span class="row-main"><span class="row-title">Rhythm Game</span><span class="row-desc">Hit keys to the beat. Real-time notes. My fingers hurt.</span></span><span class="row-meta">HTML · MPL-2.0</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.27s" href="https://milesallen.site/projects/geode-mod.html"><span class="row-no">10</span><span class="row-main"><span class="row-title">FEDL Geode Mod</span><span class="row-desc">The demon list, in-game. Roulette mode, banned mod detection.</span></span><span class="row-meta">WIP · Geode</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.30s" href="https://milesallen.site/projects/decompilations.html"><span class="row-no">11</span><span class="row-main"><span class="row-title">Decompilations</span><span class="row-desc">Native PC ports of games that probably shouldn't exist.</span></span><span class="row-meta">HTML · MIT</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.33s" href="https://milesallen.site/projects/issue-tracker.html"><span class="row-no">12</span><span class="row-main"><span class="row-title">Issue Tracker</span><span class="row-desc">Built my own because why not. Node.js, no frameworks, raw determination.</span></span><span class="row-meta">Node.js</span><span class="row-arrow">↗</span></a>
|
||
<a class="row rv" style="--d:.36s" href="https://milesallen.site/projects/moon-gaming.html"><span class="row-no">13</span><span class="row-main"><span class="row-title">Moon Gaming</span><span class="row-desc">Browser-game hub built at 2am. A growing library of chaos.</span></span><span class="row-meta">1542 commits</span><span class="row-arrow">↗</span></a>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="l score" id="score" data-vibe="1">
|
||
<header class="l-head">
|
||
<span class="l-no">04</span>
|
||
<h2 class="l-title">Scoreboard</h2>
|
||
<span class="l-tag">Numbers — Peak</span>
|
||
</header>
|
||
<div class="stats">
|
||
<div class="stat rv" data-io style="--sc:var(--yellow)"><span class="stat-n" data-count="3800" data-suffix="+">0</span><span class="stat-l">Verified FEDL runs</span></div>
|
||
<div class="stat rv" data-io style="--sc:var(--cyan);--d:.06s"><span class="stat-n" data-count="20" data-suffix="+">0</span><span class="stat-l">Emulators in OmniEmu</span></div>
|
||
<div class="stat rv" data-io style="--sc:var(--pink);--d:.12s"><span class="stat-n" data-count="1542">0</span><span class="stat-l">Moon Gaming commits</span></div>
|
||
<div class="stat rv" data-io style="--sc:var(--green);--d:.18s"><span class="stat-n" data-count="160" data-suffix="+">0</span><span class="stat-l">Public repos</span></div>
|
||
<div class="stat rv" data-io style="--sc:var(--orange);--d:.24s"><span class="stat-n" data-count="91">0</span><span class="stat-l">Stars earned</span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="q devlog" id="devlog" data-vibe="0.08" style="--acc:var(--purple)">
|
||
<header class="q-head rv">
|
||
<span class="q-no">05</span>
|
||
<h2 class="q-title">Devlog</h2>
|
||
<span class="q-tag">Writing — Soft</span>
|
||
</header>
|
||
<div class="entries">
|
||
<a class="entry rv" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Focusing on Diskette Labs</span><span class="entry-tags">Design · Collective</span></a>
|
||
<a class="entry rv" style="--d:.05s" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Why I Built Gelectron</span><span class="entry-tags">Rust · Native</span></a>
|
||
<a class="entry rv" style="--d:.1s" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Why I Built OmniEmu</span><span class="entry-tags">TypeScript · Electron</span></a>
|
||
<a class="entry rv" style="--d:.15s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">CommandMove: Cut & Paste on Mac</span><span class="entry-tags">Swift · macOS</span></a>
|
||
<a class="entry rv" style="--d:.2s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">Running the FEDL</span><span class="entry-tags">Node.js · Community</span></a>
|
||
<a class="entry rv" style="--d:.25s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">Building Nova AI</span><span class="entry-tags">Python · Local AI</span></a>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="l now" id="now" data-vibe="1">
|
||
<header class="l-head">
|
||
<span class="l-no">06</span>
|
||
<h2 class="l-title">The Now</h2>
|
||
<span class="l-tag">Focus — Loud</span>
|
||
</header>
|
||
<div class="focus-grid">
|
||
<a class="focus rv" href="https://vanilla.milesallen.site/" target="_blank" rel="noopener">
|
||
<span class="focus-no">01</span>
|
||
<h3 class="focus-title">Building Vanilla</h3>
|
||
<p class="focus-desc">Pure vanilla JS — no frameworks, no libraries, no cruft. Still unreleased but active, polishing daily. The whole point is proving the raw web can feel good.</p>
|
||
<span class="focus-status">Active — main project</span>
|
||
</a>
|
||
<a class="focus rv" style="--d:.08s" href="https://milesallen.site/projects/gelectron.html">
|
||
<span class="focus-no">02</span>
|
||
<h3 class="focus-title">Building Gelectron</h3>
|
||
<p class="focus-desc">Drop-in Electron replacement on native web views — no 150MB binaries, no 300MB of RAM to show a window. Filling out the API layer: BrowserWindow, IPC, Menu, Tray.</p>
|
||
<span class="focus-status">Early — side project</span>
|
||
</a>
|
||
<a class="focus rv" style="--d:.16s" href="https://me.fedl.site/" target="_blank" rel="noopener">
|
||
<span class="focus-no">03</span>
|
||
<h3 class="focus-title">Running the FEDL</h3>
|
||
<p class="focus-desc">The Geometry Dash demon list is still going strong — nearly 4,000 verified runs and 340+ ranked levels. Building better moderation tools and a cleaner submission pipeline.</p>
|
||
<span class="focus-status">Ongoing — community</span>
|
||
</a>
|
||
</div>
|
||
<div class="now-backburner">
|
||
<span>Back burner: Noto (it'll come around)</span>
|
||
<span>This site keeps evolving</span>
|
||
<span>Updated Aug 1, 2026</span>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="q repeat" id="repeat" data-vibe="0.08" style="--acc:var(--cyan)">
|
||
<header class="q-head rv">
|
||
<span class="q-no">07</span>
|
||
<h2 class="q-title">On Repeat</h2>
|
||
<span class="q-tag">Listening — Soft</span>
|
||
</header>
|
||
<div class="repeat-rows">
|
||
<div class="rep rv"><span class="rep-no">01</span><span class="rep-main"><span class="rep-artist">Jamie Paige</span><span class="rep-work">Machine Love</span></span><span class="rep-track">Birdbrain (w/ OK Glass)</span><span class="rep-count">12 tracks</span></div>
|
||
<div class="rep rv" style="--d:.05s"><span class="rep-no">02</span><span class="rep-main"><span class="rep-artist">FLAVOR FOLEY</span><span class="rep-work">Spoken For</span></span><span class="rep-track">BUTCHER VANITY</span><span class="rep-count">7 tracks</span></div>
|
||
<div class="rep rv" style="--d:.1s"><span class="rep-no">03</span><span class="rep-main"><span class="rep-artist">VocaloKAT</span><span class="rep-work">Singles</span></span><span class="rep-track">Affection Addiction (w/ Aku P)</span><span class="rep-count">4 tracks</span></div>
|
||
<div class="rep rv" style="--d:.15s"><span class="rep-no">04</span><span class="rep-main"><span class="rep-artist">EROMA</span><span class="rep-work">Singles</span></span><span class="rep-track">POTETO CHIP</span><span class="rep-count">5 tracks</span></div>
|
||
<div class="rep rv" style="--d:.2s"><span class="rep-no">05</span><span class="rep-main"><span class="rep-artist">AnythingBecomeMoe</span><span class="rep-work">Singles</span></span><span class="rep-track">PingPong TIKITAKA</span><span class="rep-count">3 tracks</span></div>
|
||
</div>
|
||
<p class="repeat-foot rv">Vocaloid on loop, Rage Against the Machine on bad days — <a href="https://milesallen.site/music.html">full listening history →</a></p>
|
||
</section>
|
||
|
||
<section class="l contact" id="contact" data-vibe="1">
|
||
<header class="l-head">
|
||
<span class="l-no">08</span>
|
||
<h2 class="l-title sayhi" id="sayHi">Say Hi</h2>
|
||
<span class="l-tag">Contact — Loud</span>
|
||
</header>
|
||
<div class="contact-grid">
|
||
<div class="contact-copy">
|
||
<p class="rv">Unless you're a bot — then please go away. Everyone else: I'm open to collaborations, weird ideas, and <em>very</em> specific bug reports.</p>
|
||
<a class="magnet" id="mailBtn" href="mailto:[email protected]">[email protected] <span>↗</span></a>
|
||
</div>
|
||
<div class="socials rv" style="--d:.1s">
|
||
<a class="soc" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener"><span data-scramble>GitHub</span><span>mileswolfallen2</span></a>
|
||
<a class="soc" href="https://replit.com/@MilesAllen1" target="_blank" rel="noopener"><span data-scramble>Replit</span><span>@MilesAllen1</span></a>
|
||
<a class="soc" href="https://codepen.io/mileswolfallen2-the-typescripter" target="_blank" rel="noopener"><span data-scramble>CodePen</span><span>mileswolfallen2</span></a>
|
||
<a class="soc" href="https://me.fedl.site" target="_blank" rel="noopener"><span data-scramble>FEDL</span><span>me.fedl.site</span></a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
|
||
<footer class="foot q" data-vibe="0">
|
||
<span>© 2026 Miles Wolf Allen</span>
|
||
<span class="foot-links"><a href="https://milesallen.site/uses.html">Uses</a> · <a href="https://milesallen.site/colophon.html">Colophon</a> · <a href="https://milesallen.site/quotes.html">Quotes</a> · <a href="https://milesallen.site/music.html">Music</a></span>
|
||
<span>Press <b>L</b> to go loud</span>
|
||
</footer>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
|
||
<script>
|
||
(function(){
|
||
document.documentElement.classList.add('js');
|
||
const $=(s,c)=>(c||document).querySelector(s);
|
||
const $$=(s,c)=>Array.from((c||document).querySelectorAll(s));
|
||
const RM=matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
if(RM)document.documentElement.classList.add('rm');
|
||
const hasAnime=typeof anime!=='undefined';
|
||
if(!hasAnime||RM)document.documentElement.classList.add('nf');
|
||
|
||
function splitChars(el,mask){
|
||
const text=el.textContent;
|
||
el.setAttribute('aria-label',text.trim());
|
||
el.textContent='';
|
||
const chars=[];
|
||
text.split('').forEach(ch=>{
|
||
let c=document.createElement('span');
|
||
c.className='ch';
|
||
c.textContent=ch===' '?'\u00A0':ch;
|
||
c.setAttribute('aria-hidden','true');
|
||
if(mask){
|
||
const w=document.createElement('span');
|
||
w.className='chw';
|
||
w.appendChild(c);
|
||
el.appendChild(w);
|
||
}else{
|
||
el.appendChild(c);
|
||
}
|
||
chars.push(c);
|
||
});
|
||
return chars;
|
||
}
|
||
|
||
function splitWords(el){
|
||
const walk=node=>{
|
||
Array.from(node.childNodes).forEach(n=>{
|
||
if(n.nodeType===3){
|
||
const frag=document.createDocumentFragment();
|
||
n.textContent.split(/(\s+)/).forEach(part=>{
|
||
if(!part)return;
|
||
if(/^\s+$/.test(part)){frag.appendChild(document.createTextNode(' '));return;}
|
||
const s=document.createElement('span');
|
||
s.className='bw';
|
||
s.textContent=part;
|
||
frag.appendChild(s);
|
||
});
|
||
node.replaceChild(frag,n);
|
||
}else if(n.nodeType===1){walk(n);}
|
||
});
|
||
};
|
||
walk(el);
|
||
$$('.bw',el).forEach((w,i)=>w.style.setProperty('--i',i));
|
||
}
|
||
|
||
let heroChars1=[],heroChars2=[];
|
||
if(hasAnime&&!RM){
|
||
const lns=$$('#heroName [data-split]');
|
||
heroChars1=splitChars(lns[0],true);
|
||
heroChars2=splitChars(lns[1],true);
|
||
}
|
||
$$('.bt').forEach(splitWords);
|
||
const sayHi=$('#sayHi');
|
||
if(sayHi&&!RM){
|
||
const chars=splitChars(sayHi,false);
|
||
chars.forEach((c,i)=>c.style.setProperty('--i',i));
|
||
}
|
||
|
||
const loader=$('#loader');
|
||
function killLoader(){if(loader)loader.remove();}
|
||
|
||
function startIntro(){
|
||
if(!hasAnime||RM)return;
|
||
const {animate,createTimeline,stagger,spring}=anime;
|
||
createTimeline({defaults:{ease:'outExpo',duration:900}})
|
||
.add('#nav',{y:[-26,0],opacity:[0,1]},0)
|
||
.add('#heroEyebrow',{y:[18,0],opacity:[0,1]},60)
|
||
.add(heroChars1,{y:['115%','0%'],delay:stagger(36)},120)
|
||
.add(heroChars2,{y:['115%','0%'],delay:stagger(36)},300)
|
||
.add('.hero-star',{scale:[0,1],rotate:[-180,0],ease:spring({stiffness:180,damping:13})},650)
|
||
.add('#heroState',{y:[26,0],opacity:[0,1]},720)
|
||
.add('.side-status',{scale:[.8,1],opacity:[0,1],ease:spring({stiffness:200,damping:14})},800)
|
||
.add('.side-row',{y:[14,0],opacity:[0,1],delay:stagger(80)},880)
|
||
.add('#vu',{opacity:[0,1],x:[24,0]},950);
|
||
}
|
||
|
||
if(!hasAnime||RM){
|
||
killLoader();
|
||
}else{
|
||
const {animate}=anime;
|
||
const num=$('#loaderNum'),bar=$('#loaderBar'),word=$('#loaderWord');
|
||
const words=['SOFT','LOUD','SOFT','LOUD'];
|
||
let wi=0;
|
||
const wordTimer=setInterval(()=>{
|
||
wi=(wi+1)%words.length;
|
||
word.textContent=words[wi];
|
||
word.classList.toggle('is-loud',words[wi]==='LOUD');
|
||
},300);
|
||
const state={v:0};
|
||
animate(state,{
|
||
v:100,duration:1600,ease:'inOutQuad',
|
||
onUpdate:()=>{
|
||
num.textContent=String(Math.round(state.v)).padStart(3,'0');
|
||
bar.style.transform='scaleX('+(state.v/100)+')';
|
||
},
|
||
onComplete:()=>{
|
||
clearInterval(wordTimer);
|
||
animate(loader,{
|
||
y:['0%','-102%'],duration:850,ease:'inOutQuint',delay:120,
|
||
onComplete:()=>{killLoader();startIntro();}
|
||
});
|
||
}
|
||
});
|
||
setTimeout(()=>{if(document.body.contains(loader))killLoader();},4000);
|
||
}
|
||
|
||
const io=new IntersectionObserver(entries=>{
|
||
entries.forEach(en=>{
|
||
if(!en.isIntersecting)return;
|
||
const el=en.target;
|
||
el.classList.add('is-in');
|
||
const counter=$('[data-count]',el);
|
||
if(counter)runCount(counter);
|
||
if(el.classList.contains('chip-wall'))burstChips(el);
|
||
io.unobserve(el);
|
||
});
|
||
},{threshold:.18,rootMargin:'0px 0px -6% 0px'});
|
||
$$('.rv,.bt,[data-io]').forEach(el=>io.observe(el));
|
||
|
||
function runCount(el){
|
||
const target=parseFloat(el.dataset.count)||0;
|
||
const suffix=el.dataset.suffix||'';
|
||
if(!hasAnime||RM){el.textContent=target.toLocaleString()+suffix;return;}
|
||
const {animate}=anime;
|
||
const o={n:0};
|
||
animate(o,{
|
||
n:target,duration:1700,ease:'outExpo',
|
||
onUpdate:()=>{el.textContent=Math.round(o.n).toLocaleString()+suffix;}
|
||
});
|
||
}
|
||
|
||
function burstChips(wall){
|
||
const chips=$$('.chip',wall);
|
||
if(!hasAnime||RM){
|
||
chips.forEach(c=>c.style.rotate=c.dataset.r+'deg');
|
||
return;
|
||
}
|
||
const {animate,stagger,spring}=anime;
|
||
animate(chips,{
|
||
scale:[0,1],y:[44,0],
|
||
rotate:(el,i)=>[parseFloat(el.dataset.r)-14,parseFloat(el.dataset.r)],
|
||
delay:stagger(34),
|
||
ease:spring({stiffness:210,damping:14})
|
||
});
|
||
animate(chips,{opacity:[0,1],duration:420,delay:stagger(34),ease:'outQuad'});
|
||
}
|
||
|
||
const SCRAMBLE='ABCDEFGHIJKLMNOPQRSTUVWXYZ#*+/<>_';
|
||
$$('[data-scramble]').forEach(a=>{
|
||
if(RM)return;
|
||
const orig=a.textContent;
|
||
let raf=null,it=0,total=Math.max(12,orig.length*2);
|
||
a.addEventListener('mouseenter',()=>{
|
||
if(raf)cancelAnimationFrame(raf);
|
||
it=0;
|
||
const tick=()=>{
|
||
it++;
|
||
const reveal=Math.floor((it/total)*orig.length);
|
||
a.textContent=orig.split('').map((c,i)=>{
|
||
if(c===' ')return ' ';
|
||
return i<reveal?c:SCRAMBLE[Math.random()*SCRAMBLE.length|0];
|
||
}).join('');
|
||
if(it<total){raf=requestAnimationFrame(tick);}
|
||
else{a.textContent=orig;raf=null;}
|
||
};
|
||
tick();
|
||
});
|
||
});
|
||
|
||
$$('.magnet').forEach(btn=>{
|
||
if(RM||!hasAnime)return;
|
||
const {animate,spring}=anime;
|
||
let raf=null;
|
||
btn.addEventListener('mousemove',e=>{
|
||
const r=btn.getBoundingClientRect();
|
||
const dx=e.clientX-(r.left+r.width/2);
|
||
const dy=e.clientY-(r.top+r.height/2);
|
||
if(raf)cancelAnimationFrame(raf);
|
||
raf=requestAnimationFrame(()=>{
|
||
animate(btn,{x:dx*.3,y:dy*.3,duration:320,ease:'out'});
|
||
});
|
||
});
|
||
btn.addEventListener('mouseleave',()=>{
|
||
if(raf)cancelAnimationFrame(raf);
|
||
animate(btn,{x:0,y:0,duration:750,ease:spring({stiffness:170,damping:12})});
|
||
});
|
||
});
|
||
|
||
if(!RM){
|
||
const sparkColors=['var(--pink)','var(--yellow)','var(--cyan)','var(--green)'];
|
||
document.addEventListener('pointerdown',e=>{
|
||
if(e.button!==0||!hasAnime)return;
|
||
const {animate}=anime;
|
||
for(let i=0;i<8;i++){
|
||
const p=document.createElement('i');
|
||
p.className='spark';
|
||
p.style.background=sparkColors[i%sparkColors.length];
|
||
p.style.left=e.clientX+'px';
|
||
p.style.top=e.clientY+'px';
|
||
document.body.appendChild(p);
|
||
const ang=(i/8)*Math.PI*2;
|
||
animate(p,{
|
||
x:[0,Math.cos(ang)*52],
|
||
y:[0,Math.sin(ang)*52],
|
||
opacity:[1,0],scale:[1,.15],
|
||
duration:560,ease:'outCubic',
|
||
onComplete:()=>p.remove()
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
const body=document.body;
|
||
const toggle=$('#loudToggle');
|
||
const swTxt=$('#swTxt');
|
||
function setLoud(on){
|
||
body.classList.toggle('loud-mode',on);
|
||
toggle.checked=on;
|
||
swTxt.textContent=on?'LOUD':'AUTO';
|
||
swTxt.classList.toggle('on',on);
|
||
vuTarget=on?1:currentVibe();
|
||
}
|
||
toggle.addEventListener('change',()=>setLoud(toggle.checked));
|
||
addEventListener('keydown',e=>{
|
||
if(e.metaKey||e.ctrlKey||e.altKey)return;
|
||
const tag=(document.activeElement&&document.activeElement.tagName)||'';
|
||
if(/INPUT|TEXTAREA|SELECT/.test(tag))return;
|
||
if(e.key.toLowerCase()==='l'){setLoud(!body.classList.contains('loud-mode'));}
|
||
});
|
||
|
||
const vu=$('#vu'),vuFill=$('#vuFill'),vuNeedle=$('#vuNeedle'),vuRead=$('#vuRead');
|
||
let vuTarget=0,vuCur=0;
|
||
function currentVibe(){
|
||
const mid=innerHeight*.5;
|
||
let val=null;
|
||
$$('[data-vibe]').forEach(s=>{
|
||
const r=s.getBoundingClientRect();
|
||
if(r.top<=mid&&r.bottom>=mid)val=parseFloat(s.dataset.vibe);
|
||
});
|
||
return val===null?vuTarget:val;
|
||
}
|
||
vuTarget=currentVibe();
|
||
addEventListener('scroll',()=>{
|
||
if(!body.classList.contains('loud-mode'))vuTarget=currentVibe();
|
||
},{passive:true});
|
||
(function vuLoop(){
|
||
vuCur+=(vuTarget-vuCur)*.07;
|
||
const pct=(vuCur*100).toFixed(2)+'%';
|
||
vuFill.style.height=pct;
|
||
vuNeedle.style.top=pct;
|
||
vuRead.textContent=vuCur<.33?'SOFT':vuCur<.72?'MID':'LOUD';
|
||
const c=vuCur<.33?'var(--ink)':vuCur<.72?'var(--orange)':'var(--pink)';
|
||
vu.style.setProperty('--vu-c',c);
|
||
requestAnimationFrame(vuLoop);
|
||
})();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|