Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgt0rx7v9sakbmqsin-tituloPlain Text
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#090014">
<title>Galaxia morada ✨ Flor de partículas</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap');
:root{--lav:#ead6ff;--violet:#a855f7;--deep:#090014}
*{box-sizing:border-box}
html,body{margin:0;width:100%;height:100%;overflow:hidden;background:var(--deep);color:var(--lav)}
body{font-family:'Indie Flower','Segoe Print',cursive}
canvas{position:fixed;inset:0;width:100%;height:100%;display:block}
#title{position:fixed;z-index:2;top:22px;left:12px;right:12px;text-align:center;font-size:clamp(1.5rem,4vw,2.7rem);font-weight:normal;text-shadow:0 0 10px #c084fc,0 0 24px #7e22ce;pointer-events:none}
#hint{position:fixed;z-index:2;left:12px;right:12px;bottom:18px;text-align:center;font-size:1rem;color:#d8b4fe;text-shadow:0 0 9px #a855f7;pointer-events:none}
#letterBtn{position:fixed;z-index:5;right:20px;bottom:52px;width:58px;height:58px;border:0;border-radius:50%;font-size:1.55rem;background:linear-gradient(135deg,#6d28d9,#c084fc);box-shadow:0 0 22px #a855f7aa;cursor:pointer;animation:pulse 1.7s infinite}
@keyframes pulse{50%{transform:scale(1.08)}}
#overlay{position:fixed;z-index:10;inset:0;display:none;align-items:center;justify-content:center;padding:20px;background:#080014d9;backdrop-filter:blur(7px)}
#overlay.show{display:flex}
#card{position:relative;width:min(600px,100%);max-height:82vh;overflow:auto;padding:34px 26px;border:1px solid #c084fc88;border-radius:22px;background:linear-gradient(150deg,#2e1050f5,#10051ff7);box-shadow:0 0 40px #a855f755;color:#f3e8ff}
#card h2{text-align:center;margin:0 0 18px;font-size:2rem;text-shadow:0 0 12px #a855f7}
#card p{font-size:1.15rem;line-height:1.7;margin:0 0 14px}
#close{position:absolute;right:12px;top:8px;background:none;border:0;color:#e9d5ff;font-size:1.5rem;cursor:pointer}
#editNote{display:block;text-align:center;color:#c4b5fd;font:12px system-ui;margin-top:18px}
@media(max-width:480px){#title{top:18px}#card{padding:30px 20px}#card p{font-size:1.05rem}#letterBtn{bottom:48px;right:14px}}
@media(prefers-reduced-motion:reduce){#letterBtn{animation:none}}
</style>
</head>
<body>
<canvas id="space" aria-label="Galaxia morada animada con una flor de partículas"></canvas>
<h1 id="title">Feliz Día de las Flores Moradas 🪻</h1>
<div id="hint">Una flor hecha de estrellas, solo para ti 💜</div>
<button id="letterBtn" aria-label="Abrir carta">💌</button>
<div id="overlay" role="dialog" aria-modal="true" aria-labelledby="letterTitle">
<section id="card">
<button id="close" aria-label="Cerrar">×</button>
<h2 id="letterTitle">Para ti 💜</h2>
<div id="letterBody">
<p>Hay personas que llegan y, sin darse cuenta, hacen que muchos de tus días sean mejores. Así eres tú para mí.</p>
<p>Gracias por cada momento compartido, por las risas y por estar ahí incluso en lo simple. Quiero que sepas cuánto vales y cuánto significas para mí.</p>
<p>Que esta flor morada te recuerde, hoy y siempre, lo especial que eres. 🌷💜</p>
</div>
<small id="editNote">Puedes personalizar este mensaje editando el texto dentro de <b>letterBody</b> en el archivo HTML.</small>
</section>
</div>
<script>
(() => {
const canvas = document.getElementById('space');
const ctx = canvas.getContext('2d');
let w, h, dpr, stars = [], dust = [], flower = [], t = 0;
const rand = (a,b) => Math.random()*(b-a)+a;
function resize(){
dpr = Math.min(window.devicePixelRatio || 1, 2);
w = innerWidth; h = innerHeight;
canvas.width = Math.round(w*dpr); canvas.height = Math.round(h*dpr);
ctx.setTransform(dpr,0,0,dpr,0,0);
stars = Array.from({length:Math.min(420,Math.floor(w*h/2300))},()=>({
x:Math.random()*w,y:Math.random()*h,r:rand(.35,1.7),a:rand(.25,.95),p:rand(0,Math.PI*2),speed:rand(.3,1.5)
}));
dust = Array.from({length:170},()=>({a:rand(0,Math.PI*2),r:rand(35,Math.min(w,h)*.46),s:rand(.001,.009),size:rand(.5,2)}));
flower = [];
// Pétalos en forma de flor, construidos con partículas.
const petals = 8, points = 1500;
for(let i=0;i<points;i++){
const u = Math.random()*Math.PI*2;
const radius = Math.abs(Math.cos(petals*u/2))*Math.min(w,h)*.19 * rand(.78,1.04);
flower.push({a:u,r:radius,size:rand(.65,1.9),phase:rand(0,6.28),color:Math.random()});
}
}
function glowDot(x,y,r,color,alpha=1){
ctx.globalAlpha=alpha; ctx.fillStyle=color; ctx.shadowBlur=r*5; ctx.shadowColor=color;
ctx.beginPath();ctx.arc(x,y,r,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;ctx.globalAlpha=1;
}
function draw(){
t += .008;
ctx.clearRect(0,0,w,h);
const bg=ctx.createRadialGradient(w*.5,h*.5,0,w*.5,h*.5,Math.max(w,h)*.72);
bg.addColorStop(0,'#1c0734');bg.addColorStop(.55,'#0d031b');bg.addColorStop(1,'#030008');
ctx.fillStyle=bg;ctx.fillRect(0,0,w,h);
for(const s of stars){
const twinkle=.45+.55*Math.sin(t*s.speed*3+s.p);
glowDot(s.x,s.y,s.r,'#e9d5ff',s.a*twinkle*.8);
}
const cx=w/2, cy=h/2+Math.min(20,h*.025);
// Anillos de galaxia
ctx.save();ctx.translate(cx,cy);ctx.rotate(-.18);
for(let ring=0;ring<4;ring++){
ctx.beginPath();ctx.ellipse(0,0,Math.min(w,h)*(.19+ring*.065),Math.min(w,h)*(.055+ring*.018),0,0,Math.PI*2);
ctx.strokeStyle=`rgba(168,85,247,${.12-ring*.02})`;ctx.lineWidth=1;ctx.stroke();
}
ctx.restore();
for(const p of dust){
p.a += p.s;
const x=cx+Math.cos(p.a+t*.13)*p.r;
const y=cy+Math.sin(p.a+t*.13)*p.r*.48;
glowDot(x,y,p.size,Math.random()>.45?'#c084fc':'#f5d0fe',.55);
}
// Flor morada de partículas
const scale=Math.min(w,h)*.001;
for(const p of flower){
const pulse=1+.025*Math.sin(t*2+p.phase);
const radius=p.r*pulse;
const x=cx+Math.cos(p.a)*radius;
const y=cy+Math.sin(p.a)*radius;
const col=p.color>.8?'#fff0ff':p.color>.45?'#d8b4fe':'#a855f7';
glowDot(x,y,p.size,col,.55+.4*Math.sin(t*2+p.phase)**2);
}
// Centro luminoso
const core=ctx.createRadialGradient(cx,cy,1,cx,cy,Math.min(w,h)*.075);
core.addColorStop(0,'rgba(255,240,255,.9)');core.addColorStop(.25,'rgba(216,180,254,.34)');core.addColorStop(1,'rgba(168,85,247,0)');
ctx.fillStyle=core;ctx.beginPath();ctx.arc(cx,cy,Math.min(w,h)*.075,0,Math.PI*2);ctx.fill();
glowDot(cx,cy,3,'#fff7ff',.95);
requestAnimationFrame(draw);
}
window.addEventListener('resize',resize);
resize();draw();
const overlay=document.getElementById('overlay');
document.getElementById('letterBtn').addEventListener('click',()=>overlay.classList.add('show'));
document.getElementById('close').addEventListener('click',()=>overlay.classList.remove('show'));
overlay.addEventListener('click',e=>{if(e.target===overlay)overlay.classList.remove('show')});
window.addEventListener('keydown',e=>{if(e.key==='Escape')overlay.classList.remove('show')});
})();
</script>
</body>
</html>7,033 chars · codepastes.com