mirror of
https://github.com/mileswolfallen2/miles-web.git
synced 2026-09-08 11:23:17 +00:00
64 lines
16 KiB
HTML
64 lines
16 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>MWA FM // On Air</title>
|
||
<meta name="description" content="MWA FM is an experimental broadcast from Miles Wolf Allen: projects, ideas, and transmissions from the workbench.">
|
||
<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='%23ff5f56'/%3E%3Ccircle cx='50' cy='50' r='20' fill='%2310172b'/%3E%3Cpath d='M17 50a33 33 0 0 1 66 0M7 50a43 43 0 0 1 86 0' fill='none' stroke='%2310172b' stroke-width='7'/%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=DM+Mono:wght@400;500&family=Manrope:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root { --night:#10172b; --panel:#18213b; --panel-2:#202b49; --white:#eef3ff; --dim:#8390ad; --red:#ff5f56; --cyan:#65e8dc; --yellow:#ffd166; --purple:#ad94ff; --mono:'DM Mono',monospace; --sans:Manrope,sans-serif; }
|
||
* { box-sizing:border-box; }
|
||
html { background:var(--night); scroll-behavior:smooth; }
|
||
body { margin:0; color:var(--white); background:radial-gradient(circle at 20% 0,#28375f 0,transparent 35%),var(--night); font-family:var(--sans); overflow-x:hidden; }
|
||
::selection { color:var(--night); background:var(--cyan); }
|
||
a { color:inherit; text-decoration:none; } button { color:inherit; font:inherit; cursor:pointer; }
|
||
.noise { position:fixed; inset:0; z-index:20; pointer-events:none; opacity:.04; background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 160 160' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
|
||
.wrap { width:min(1180px,90vw); margin:auto; }
|
||
.topbar { display:flex; justify-content:space-between; align-items:center; gap:20px; padding:23px 0; border-bottom:1px solid rgba(238,243,255,.14); font:400 10px var(--mono); letter-spacing:.1em; text-transform:uppercase; }
|
||
.station { display:flex; align-items:center; gap:10px; } .station i { width:10px; height:10px; border-radius:50%; background:var(--red); box-shadow:0 0 17px var(--red); animation:blink 1.4s steps(1) infinite; } @keyframes blink { 50% { opacity:.25; } }
|
||
.nav { display:flex; gap:24px; color:var(--dim); } .nav a:hover { color:var(--cyan); } .live { color:var(--red); }
|
||
.hero { min-height:78vh; position:relative; display:grid; grid-template-columns:1fr .72fr; gap:8vw; align-items:center; padding:100px 0 125px; }
|
||
.hero::before { content:'93.7'; position:absolute; right:-3vw; bottom:5%; color:rgba(238,243,255,.04); font:800 clamp(10rem,30vw,31rem)/.7 var(--sans); letter-spacing:-.12em; pointer-events:none; }
|
||
.eyebrow { color:var(--cyan); font:400 10px var(--mono); text-transform:uppercase; letter-spacing:.15em; } h1 { margin:18px 0 25px; font:800 clamp(5rem,13vw,12rem)/.77 var(--sans); letter-spacing:-.11em; position:relative; z-index:1; } h1 span { display:block; } h1 .outline { color:transparent; -webkit-text-stroke:1px var(--white); } h1 .red { color:var(--red); }
|
||
.hero p { max-width:490px; color:var(--dim); font:400 13px/1.85 var(--mono); position:relative; z-index:1; } .hero p strong { color:var(--white); font-weight:400; }
|
||
.listen { display:inline-flex; align-items:center; gap:12px; margin-top:28px; padding:13px 17px; color:var(--night); background:var(--cyan); border:0; font:500 10px var(--mono); text-transform:uppercase; box-shadow:5px 5px 0 rgba(0,0,0,.25); transition:.2s; } .listen:hover { transform:translate(-3px,-3px); box-shadow:8px 8px 0 rgba(0,0,0,.25); }
|
||
.broadcast { position:relative; z-index:1; padding:27px; border:1px solid rgba(238,243,255,.18); background:rgba(16,23,43,.73); box-shadow:18px 18px 0 rgba(0,0,0,.2); transform:rotate(2deg); } .broadcast::before { content:'ON AIR'; position:absolute; top:-13px; right:22px; padding:6px 9px; color:var(--night); background:var(--red); font:500 9px var(--mono); transform:rotate(-4deg); }
|
||
.broadcast-head { display:flex; justify-content:space-between; color:var(--dim); font:400 9px var(--mono); text-transform:uppercase; } .broadcast-head b { color:var(--red); font-weight:400; }
|
||
.wave { display:flex; align-items:center; justify-content:center; gap:5px; height:130px; margin:24px 0; border-block:1px solid rgba(238,243,255,.14); } .wave i { width:4px; height:var(--h); background:var(--cyan); animation:wave .8s ease-in-out infinite alternate; animation-delay:var(--d); } @keyframes wave { to { transform:scaleY(.25); opacity:.35; } }
|
||
.broadcast-title { margin:0; font-size:23px; } .broadcast-sub { margin:6px 0 0; color:var(--dim); font:400 10px var(--mono); }
|
||
.ticker { overflow:hidden; background:var(--yellow); color:var(--night); transform:rotate(-1deg) scale(1.03); } .ticker-track { display:flex; width:max-content; animation:marquee 24s linear infinite; } .ticker span { padding:12px 20px; font:500 10px var(--mono); letter-spacing:.1em; text-transform:uppercase; } @keyframes marquee { to { transform:translateX(-50%); } }
|
||
section { padding:130px 0; } .section-head { display:flex; align-items:end; justify-content:space-between; gap:25px; margin-bottom:45px; } .section-label { color:var(--cyan); font:400 10px var(--mono); letter-spacing:.13em; text-transform:uppercase; } h2 { margin:9px 0 0; font:800 clamp(3.3rem,8vw,7.5rem)/.76 var(--sans); letter-spacing:-.11em; } h2 em { color:var(--red); font-style:normal; } .section-head p { max-width:280px; color:var(--dim); font:400 11px/1.7 var(--mono); }
|
||
.console { display:grid; grid-template-columns:270px 1fr; gap:1px; border:1px solid rgba(238,243,255,.15); background:rgba(238,243,255,.15); } .console-side, .console-main { background:var(--panel); padding:25px; } .console-side { min-height:485px; } .console-side h3 { margin:0 0 24px; color:var(--white); font:600 18px var(--sans); } .channel { width:100%; display:flex; justify-content:space-between; margin-bottom:8px; padding:13px 12px; border:1px solid rgba(238,243,255,.13); color:var(--dim); background:transparent; font:400 10px var(--mono); text-transform:uppercase; text-align:left; transition:.2s; } .channel:hover, .channel.active { color:var(--night); border-color:var(--cyan); background:var(--cyan); } .channel span { color:var(--red); } .channel.active span { color:var(--night); }
|
||
.console-readout { display:flex; justify-content:space-between; gap:10px; margin-top:55px; padding-top:17px; border-top:1px solid rgba(238,243,255,.13); color:var(--dim); font:400 9px var(--mono); text-transform:uppercase; } .console-readout b { color:var(--yellow); font-weight:400; }
|
||
.transmissions { display:grid; gap:1px; background:rgba(238,243,255,.14); } .transmission { display:grid; grid-template-columns:80px 1fr auto; gap:20px; align-items:center; padding:21px 22px; background:var(--panel-2); transition:.25s; } .transmission:hover { background:#293756; transform:translateX(6px); } .transmission[hidden] { display:none; } .transmission .number { color:var(--red); font:400 10px var(--mono); } .transmission h3 { margin:0; font-size:17px; } .transmission p { margin:5px 0 0; color:var(--dim); font:400 10px/1.6 var(--mono); } .transmission .arrow { color:var(--cyan); font-size:18px; }
|
||
.dial-zone { display:grid; grid-template-columns:.8fr 1.2fr; gap:8vw; align-items:center; padding:35px; border:1px solid rgba(238,243,255,.14); background:linear-gradient(120deg,rgba(101,232,220,.07),rgba(173,148,255,.05)); } .dial-copy h3 { margin:0 0 15px; font-size:31px; } .dial-copy p { max-width:370px; color:var(--dim); font:400 11px/1.8 var(--mono); } .dial { display:flex; flex-direction:column; align-items:center; gap:19px; } .knob { width:145px; height:145px; position:relative; border:14px solid #2d3b5c; border-radius:50%; background:radial-gradient(circle at 35% 30%,#5f719b,#17213a 54%,#090e1b 56%); box-shadow:0 0 0 2px var(--cyan),0 18px 35px rgba(0,0,0,.35); transform:rotate(-35deg); transition:transform .25s; } .knob::after { content:''; position:absolute; left:50%; top:8px; width:3px; height:42px; background:var(--yellow); transform:translateX(-50%); } .dial input { width:min(310px,100%); accent-color:var(--cyan); } .dial-value { color:var(--cyan); font:400 12px var(--mono); letter-spacing:.1em; }
|
||
.contact { display:grid; grid-template-columns:1fr 1fr; gap:9vw; align-items:end; } .contact h2 { color:var(--white); } .contact h2 span { color:var(--cyan); } .contact p { max-width:370px; color:var(--dim); font:400 12px/1.8 var(--mono); } .contact-list { border-top:1px solid rgba(238,243,255,.18); } .contact-list a { display:flex; justify-content:space-between; gap:15px; padding:21px 0; border-bottom:1px solid rgba(238,243,255,.18); color:var(--dim); font:400 10px var(--mono); text-transform:uppercase; } .contact-list a span:last-child { color:var(--cyan); } .contact-list a:hover { padding-left:9px; color:var(--white); }
|
||
footer { display:flex; justify-content:space-between; gap:20px; padding:27px 0 40px; border-top:1px solid rgba(238,243,255,.14); color:var(--dim); font:400 10px var(--mono); text-transform:uppercase; } footer a { color:var(--cyan); }
|
||
@media(max-width:760px) { .topbar { padding-block:18px; } .nav-links { display:none; } .hero { grid-template-columns:1fr; padding:75px 0 105px; gap:60px; } .broadcast { max-width:390px; } .console { grid-template-columns:1fr; } .console-side { min-height:0; } .console-readout { margin-top:28px; } .section-head { display:block; } .section-head p { margin-top:18px; } .dial-zone,.contact { grid-template-columns:1fr; gap:40px; } .contact { align-items:start; } footer { flex-direction:column; } }
|
||
@media(prefers-reduced-motion:reduce) { *,*::before,*::after { animation:none!important; transition:none!important; scroll-behavior:auto!important; } }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="noise" aria-hidden="true"></div>
|
||
<div class="wrap"><nav class="topbar"><a class="station" href="#top"><i></i><span>MWA FM / 93.7</span></a><div class="nav"><a href="#broadcast">Broadcast</a><a href="#archive">Archive</a><a href="#contact">Call in</a></div><span class="live">● on air</span></nav>
|
||
<main id="top"><header class="hero"><div><div class="eyebrow">a broadcast from the workbench</div><h1><span class="outline">MILES</span><span>WOLF <b class="red">ALLEN</b></span></h1><p>This is a station for <strong>tools, games, websites, and thoughts</strong> that refuse to stay quiet. Tune in for the current signal.</p><a class="listen" href="#broadcast">listen to the latest transmission ↓</a></div><aside class="broadcast"><div class="broadcast-head"><span>now transmitting</span><b>● live</b></div><div class="wave" aria-label="Animated audio waveform"><i style="--h:35%;--d:-.1s"></i><i style="--h:80%;--d:-.3s"></i><i style="--h:48%;--d:-.5s"></i><i style="--h:95%;--d:-.2s"></i><i style="--h:62%;--d:-.6s"></i><i style="--h:37%;--d:-.1s"></i><i style="--h:74%;--d:-.4s"></i><i style="--h:52%;--d:-.7s"></i><i style="--h:88%;--d:-.3s"></i><i style="--h:42%;--d:-.5s"></i><i style="--h:68%;--d:-.2s"></i><i style="--h:32%;--d:-.6s"></i><i style="--h:90%;--d:-.1s"></i><i style="--h:50%;--d:-.4s"></i></div><h2 class="broadcast-title">Making software feel obvious</h2><p class="broadcast-sub">a recurring broadcast / no ads / probably too much coffee</p></aside></header>
|
||
<div class="ticker"><div class="ticker-track"><span>local ai</span><span>native web views</span><span>geometry dash</span><span>small games</span><span>big questions</span><span>local ai</span><span>native web views</span><span>geometry dash</span><span>small games</span><span>big questions</span></div></div>
|
||
<section id="broadcast"><div class="section-head"><div><div class="section-label">01 / incoming signal</div><h2>The <em>broadcast.</em></h2></div><p>Choose a channel to filter the current transmissions from the studio.</p></div><div class="console"><aside class="console-side"><h3>Channel selector</h3><button class="channel active" data-channel="all" type="button">all frequencies <span>01</span></button><button class="channel" data-channel="tools" type="button">tools + systems <span>02</span></button><button class="channel" data-channel="games" type="button">games + play <span>03</span></button><button class="channel" data-channel="web" type="button">web + community <span>04</span></button><div class="console-readout"><span>signal</span><b>strong / 7 bars</b></div></aside><div class="console-main"><div class="transmissions"><a class="transmission" data-type="tools" href="https://milesallen.site/projects/gelectron.html"><span class="number">TX / 001</span><div><h3>Gelectron is moving</h3><p>native web views / fewer megabytes / rust</p></div><span class="arrow">↗</span></a><a class="transmission" data-type="tools" href="https://milesallen.site/projects/omniemu.html"><span class="number">TX / 002</span><div><h3>One home for every emulator</h3><p>roms / launchers / the cabinet is open</p></div><span class="arrow">↗</span></a><a class="transmission" data-type="web" href="https://milesallen.site/projects/fedl.html"><span class="number">TX / 003</span><div><h3>The list keeps getting harder</h3><p>fedl / community / a very high skill ceiling</p></div><span class="arrow">↗</span></a><a class="transmission" data-type="games" href="https://milesallen.site/projects/moon-gaming.html"><span class="number">TX / 004</span><div><h3>Make room for play</h3><p>browser games / late nights / moon gaming</p></div><span class="arrow">↗</span></a></div></div></div></section>
|
||
<section id="archive"><div class="section-head"><div><div class="section-label">02 / dial it in</div><h2>Find your <em>frequency.</em></h2></div><p>Turn the dial. It changes the station readout because a portfolio should have at least one satisfying knob.</p></div><div class="dial-zone"><div class="dial-copy"><h3>What are you in the mood for?</h3><p>Something practical? Something playful? Something that probably started as a “what if?” and became a whole folder? Tune until the signal feels right.</p></div><div class="dial"><div class="knob" id="knob"></div><input id="frequency" type="range" min="0" max="100" value="37" aria-label="Tune the station frequency"><div class="dial-value" id="dialValue">93.7 FM / CURIOUS</div></div></div></section>
|
||
<section id="contact"><div class="contact"><div><div class="section-label">03 / call in</div><h2>Say <span>hello.</span></h2><p>The studio line is open. Send a question, a strange idea, or a link to something you’re making.</p></div><div class="contact-list"><a href="mailto:[email protected]"><span>email</span><span>[email protected] ↗</span></a><a href="https://github.com/mileswolfallen2" target="_blank" rel="noopener"><span>github</span><span>@mileswolfallen2 ↗</span></a><a href="https://diskettelabs.com/" target="_blank" rel="noopener"><span>collective</span><span>diskette labs ↗</span></a></div></div></section></main>
|
||
<footer><span>© 2026 Miles Wolf Allen / transmission complete</span><a href="#top">return to the top ↑</a><span>93.7 / still online</span></footer>
|
||
</div>
|
||
<script>
|
||
const channels = document.querySelectorAll('.channel');
|
||
const transmissions = document.querySelectorAll('.transmission');
|
||
channels.forEach(channel => channel.addEventListener('click', () => { channels.forEach(item => item.classList.remove('active')); channel.classList.add('active'); const value = channel.dataset.channel; transmissions.forEach(item => { item.hidden = value !== 'all' && item.dataset.type !== value; }); }));
|
||
const frequency = document.getElementById('frequency'); const knob = document.getElementById('knob'); const dialValue = document.getElementById('dialValue'); const moods = ['SOFT LAUNCH','CURIOUS','MAKING','FULL BEAM','SIDE QUEST'];
|
||
frequency.addEventListener('input', () => { const value = Number(frequency.value); knob.style.transform = `rotate(${value * 2.3 - 35}deg)`; const station = (88.2 + value * .18).toFixed(1); dialValue.textContent = `${station} FM / ${moods[Math.min(4, Math.floor(value / 21))]}`; });
|
||
</script>
|
||
</body>
|
||
</html>
|