<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Fart-O-Gram ✨🦄💨</title>
<meta name="description" content="Send fabulously gassy messages with a very gay unicorn hippie vibe." />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Ctext y='1em' font-size='96'%3E%F0%9F%AA%84%3C/text%3E%3C/svg%3E" />
<style>
:root{
--bg: conic-gradient(from 0deg, #ff007f, #ff8a00, #ffe600, #16ff89, #00e1ff, #8a2be2, #ff007f);
--card: rgba(255,255,255,.18);
--glass: rgba(255,255,255,.7);
--text: #1a0937;
--accent: #ff3bd4;
--accent2: #00ffd5;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0; color:var(--text); font:16px/1.4 ui-rounded, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, "Apple Color Emoji","Segoe UI Emoji";
background: radial-gradient(1200px at 80% -10%, rgba(255,255,255,.45), transparent 40%), var(--bg);
background-size: 140% 140%, cover;
animation: swirl 18s linear infinite;
overflow-x:hidden;
}
@keyframes swirl{to{background-position: 140% 140%, center}}
.grid{
display:grid; place-items:center; min-height:100dvh; padding:24px;
}
.shell{
width:min(980px,100%); position:relative;
}
header{
display:flex; gap:16px; align-items:center; justify-content:center; text-align:center; flex-wrap:wrap;
padding:18px 14px; border-radius:20px; backdrop-filter: blur(10px);
background: linear-gradient(180deg, rgba(255,255,255,.85), rgba(255,255,255,.55));
box-shadow: 0 20px 60px rgba(0,0,0,.15), inset 0 0 0 1px rgba(255,255,255,.6);
}
.logo{
font-size:46px; filter: drop-shadow(0 6px 18px rgba(0,0,0,.15));
transform: rotate(-6deg);
}
h1{
margin:0; font-size: clamp(28px, 5vw, 46px);
letter-spacing:.5px;
background: linear-gradient(90deg, #111, #111);
-webkit-background-clip:text; background-clip:text; color:transparent;
}
h1 .burst{
background: conic-gradient(from 90deg, #ff00ea 0 16%, #ffb800 16% 33%, #00ffcc 33% 50%, #6a5bff 50% 66%, #ff00ea 66% 100%);
-webkit-background-clip:text; background-clip:text; color:transparent;
filter: drop-shadow(0 2px 0 rgba(255,255,255,.85));
}
.tag{margin-top:4px; opacity:.85; font-weight:600}
main{
margin-top:22px; display:grid; grid-template-columns: 1.1fr .9fr; gap:22px;
}
@media (max-width: 820px){ main{grid-template-columns: 1fr; } }
.card{
background: var(--card);
backdrop-filter: blur(12px);
border-radius: 22px;
padding: 18px;
box-shadow: 0 12px 44px rgba(0,0,0,.18), inset 0 0 0 1px rgba(255,255,255,.35);
position:relative;
}
form{
display:grid; gap:14px;
}
label{font-weight:700; display:block; margin-bottom:6px}
.row{display:grid; gap:14px; grid-template-columns: 1fr 1fr;}
@media (max-width: 620px){ .row{grid-template-columns: 1fr} }
input[type="text"], textarea, select{
width:100%;
padding:14px 14px;
border-radius:14px; border:1px solid rgba(0,0,0,.06);
background: rgba(255,255,255,.85);
outline:none; transition: .2s border, .2s box-shadow, .2s transform;
font: inherit;
}
textarea{min-height:120px; resize:vertical}
input:focus, textarea:focus, select:focus{
border-color:#7a58ff; box-shadow:0 0 0 4px rgba(122,88,255,.18);
transform: translateY(-1px);
}
.actions{display:flex; gap:10px; flex-wrap:wrap}
.btn{
appearance:none; border:0; padding:13px 18px;
border-radius:14px; cursor:pointer; font-weight:900; letter-spacing:.3px;
box-shadow:0 14px 40px rgba(0,0,0,.18), inset 0 0 0 1px rgba(255,255,255,.4);
transition: transform .1s ease, filter .2s ease, box-shadow .2s ease;
}
.btn-primary{
background: linear-gradient(135deg, #ff00ea, #7a58ff 60%, #00ffd5);
color:#fff; text-shadow: 0 1px 0 rgba(0,0,0,.25);
}
.btn-ghost{
background: rgba(255,255,255,.75); color:#3b2c7e; font-weight:800;
}
.btn:active{transform: translateY(1px)}
.tiny{font-size:12px; opacity:.8}
.stage{
position:relative; min-height:340px; display:grid; place-items:center; overflow:hidden; border-radius:18px;
background: radial-gradient(900px at 20% 120%, rgba(255,255,255,.55), rgba(255,255,255,.15)),
linear-gradient(180deg, rgba(255,255,255,.3), rgba(255,255,255,0));
}
canvas{position:absolute; inset:0; width:100%; height:100%}
.unicorn{
position:relative; z-index:2; font-size: clamp(80px, 20vw, 160px);
filter: drop-shadow(0 10px 18px rgba(0,0,0,.25));
animation: bob 2.6s ease-in-out infinite;
transform-origin:center bottom;
}
@keyframes bob{
0%,100%{ transform: translateY(0) rotate(-4deg)}
50%{ transform: translateY(-8px) rotate(-6deg)}
}
.badge{
position:absolute; top:12px; right:12px; z-index:3;
padding:8px 12px; border-radius:999px; font-weight:900; letter-spacing:.3px;
background:linear-gradient(90deg,#111,#111); color:#fff;
box-shadow: 0 10px 30px rgba(0,0,0,.25), inset 0 0 0 1px rgba(255,255,255,.4);
}
.badge .grad{
background: conic-gradient(from 180deg, #ff00ea, #ffe600, #00ffd5, #7a58ff, #ff00ea);
-webkit-background-clip:text; background-clip:text; color:transparent;
}
.vibes{display:flex; gap:8px; flex-wrap:wrap}
.pill{
border:0; padding:10px 14px; border-radius:999px; cursor:pointer; font-weight:800;
background: rgba(255,255,255,.9); box-shadow: inset 0 0 0 2px rgba(0,0,0,.05);
}
.pill[aria-checked="true"]{ outline:3px solid rgba(255,0,234,.45); box-shadow: 0 0 0 6px rgba(255,0,234,.15), inset 0 0 0 2px rgba(0,0,0,.08) }
footer{margin-top:18px; text-align:center; color:#28144f}
a{color:#28144f}
</style>
</head>
<body>
<div class="grid">
<div class="shell">
<header>
<div class="logo" aria-hidden="true">🦄💨🌈✌️</div>
<div>
<h1><span class="burst">Fart-O-Gram</span></h1>
<div class="tag">Outrageously proud. Hilariously gassy. Send a fabulously fragrant message.</div>
</div>
</header>
<main>
<section class="card">
<form id="fartForm" autocomplete="off">
<div class="row">
<div>
<label for="to">To</label>
<input id="to" name="to" type="text" placeholder="Bestie, Mom, Your Ex, Humanity…" />
</div>
<div>
<label for="from">From</label>
<input id="from" name="from" type="text" placeholder="A classy toot connoisseur" />
</div>
</div>
<div>
<label for="message">Your fabulously fragrant message</label>
<textarea id="message" name="message" placeholder="Type something sweet, spicy, or stinky…"></textarea>
</div>
<div>
<label>Choose a vibe</label>
<div class="vibes" role="radiogroup" aria-label="Fart vibe">
<button class="pill" role="radio" aria-checked="true" data-vibe="glitter">Glitter</button>
<button class="pill" role="radio" aria-checked="false" data-vibe="disco">Disco</button>
<button class="pill" role="radio" aria-checked="false" data-vibe="flowerpower">Flower Power</button>
<button class="pill" role="radio" aria-checked="false" data-vibe="stoner">Stoner</button>
<button class="pill" role="radio" aria-checked="false" data-vibe="pride">Pride Storm</button>
</div>
</div>
<div class="actions">
<button class="btn btn-primary" type="submit">Send the Fart 💌💨</button>
<button class="btn btn-ghost" type="button" id="previewBtn">Preview 💫</button>
<button class="btn btn-ghost" type="button" id="shareBtn">Share Link 🔗</button>
</div>
<div class="tiny">Sound on for maximum chaos. Links are local and encode your message in the URL.</div>
</form>
</section>
<section class="card stage" aria-live="polite">
<div class="badge">100% <span class="grad">Organic Vibes</span></div>
<canvas id="fx"></canvas>
<div class="unicorn" id="uni" aria-label="Unicorn blowing sparkly rainbow farts" role="img">🦄</div>
</section>
</main>
<footer class="tiny">
Built with love, glitter, and questionable acoustics. 🫶
</footer>
</div>
</div>
<script>
(() => {
// Elements
const form = document.getElementById('fartForm');
const toEl = document.getElementById('to');
const fromEl = document.getElementById('from');
const msgEl = document.getElementById('message');
const vibePills = [...document.querySelectorAll('.pill')];
const canvas = document.getElementById('fx');
const ctx = canvas.getContext('2d');
const previewBtn = document.getElementById('previewBtn');
const shareBtn = document.getElementById('shareBtn');
const uni = document.getElementById('uni');
// Canvas resize
const fit = () => {
const dpr = Math.max(1, window.devicePixelRatio || 1);
canvas.width = Math.floor(canvas.clientWidth * dpr);
canvas.height = Math.floor(canvas.clientHeight * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
new ResizeObserver(fit).observe(canvas);
fit();
// Vibe picker a11y
let currentVibe = 'glitter';
vibePills.forEach(btn=>{
btn.addEventListener('click', (e)=>{
e.preventDefault();
currentVibe = btn.dataset.vibe;
vibePills.forEach(b=>b.setAttribute('aria-checked', String(b===btn)));
pulse(uni);
});
});
// Particle system
const rand = (a,b)=> a + Math.random()*(b-a);
const TWO_PI = Math.PI*2;
let particles = [];
function spawnVibe(x, y, vibe, strength=1){
const base = Math.floor(120*strength);
const push = () => {
switch(vibe){
case 'glitter':
return { shape:'star', color: pick(['#ff00ea','#ffe600','#00ffd5','#7a58ff','#ff8a00']), life: rand(600,1000)};
case 'disco':
return { shape:'square', color: pick(['#00e1ff','#ff3bd4','#ffe600','#16ff89','#8a2be2']), life: rand(700,1100)};
case 'flowerpower':
return { shape:'flower', color: pick(['#ff66c4','#ffde59','#7ed957','#9a66ff','#ff914d']), life: rand(800,1200)};
case 'stoner':
return { shape:'circle', color: pick(['#baffc9','#85ffc7','#5ce1e6','#7a58ff','#c3f584']), life: rand(900,1300)};
case 'pride':
return { shape:'ribbon', color: pick(['#e40303','#ff8c00','#ffed00','#008026','#004dff','#750787','#000000','#784f17','#ffffff']), life: rand(700,1200)};
}
};
for(let i=0;i<base;i++){
const pconf = push();
const v = rand(-1.6, -0.4);
particles.push({
x, y, vx: rand(-1.4, 1.4), vy: v,
g: rand(0.015,0.04),
s: rand(4, 10),
a: 1,
t: 0,
...pconf
});
}
}
function pick(arr){ return arr[Math.floor(Math.random()*arr.length)] }
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
for(let i=particles.length-1;i>=0;i--){
const p = particles[i];
p.t += 16;
p.x += p.vx;
p.y += p.vy;
p.vy += p.g;
p.a = Math.max(0, 1 - p.t / p.life);
ctx.globalAlpha = p.a;
switch(p.shape){
case 'star': drawStar(p.x,p.y,p.s,5,p.s/2,p.color); break;
case 'square': drawSquare(p.x,p.y,p.s,p.color); break;