Files

64 lines
16 KiB
HTML
Raw Permalink 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">
<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 youre 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>