<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- MODIFICAR: Título de la pestaña del navegador -->
<title>¡Una Carta para Ti!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="fireworks-canvas"></canvas>
<div class="container">
<svg id="graduation-cap" viewBox="0 0 100 100">
<g fill="#333">
<line x1="50" y1="10" x2="50" y2="50" stroke="#d4af7a" stroke-width="2"/>
<circle cx="50" cy="50" r="4" fill="#d4af7a"/>
<rect x="52" y="10" width="10" height="2" fill="#d4af7a" transform="rotate(15 50 10)"/>
<polygon points="5,25 50,5 95,25 50,45" />
<path d="M20,45 C20,65 80,65 80,45 L75,45 C75,60 25,60 25,45 Z" />
</g>
</svg>
<div id="carta">
<div id="close-button">×</div>
<p id="carta-fecha"></p>
<h2 id="carta-saludo"></h2>
<div id="carta-cuerpo">
</div>
<p id="carta-despedida"></p>
<p class="firma"></p>
<button id="share-button">Compartir</button>
</div>
</div>
<div id="instruction">Arrastra y suelta el birrete para lanzarlo</div>
<script src="script.js"></script>
</body>
</html>
@import url('https://fonts.googleapis.com/css2?family=Parisienne&family=Cormorant+Garamond:wght@400;700&display=swap');
/* MODIFICAR: Paleta de colores base (para el género masculino o por defecto) */
:root {
--color-principal: #3a7ca5;
--color-fondo-carta: #f0f4f8;
--color-borde: #87c3e5;
--color-texto-principal: #1d3557;
--color-texto-secundario: #555;
--color-firma: var(--color-principal);
}
body, html { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background-color: #00001a; font-family: 'Cormorant Garamond', serif; display: flex; justify-content: center; align-items: center; }
#fireworks-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; }
.container { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; perspective: 1000px; width: 100%; height: 100%; }
.container::before { content: ''; position: absolute; top: 50%; left: 50%; width: 500px; height: 500px; background: radial-gradient(circle, rgba(255, 223, 150, 0.15) 0%, rgba(255, 223, 150, 0) 70%); transform: translate(-50%, -60%); border-radius: 50%; z-index: 9; pointer-events: none; animation: pulse-light 5s infinite ease-in-out; }
@keyframes pulse-light { 0%, 100% { opacity: 0.8; transform: translate(-50%, -60%) scale(1); } 50% { opacity: 1; transform: translate(-50%, -60%) scale(1.1); } }
#graduation-cap { width: 150px; height: 150px; cursor: grab; position: relative; z-index: 10; filter: drop-shadow(0 10px 15px rgba(0,0,0,0.3)); transition: transform 0.3s ease-out; animation: float-animation 4s ease-in-out infinite; }
#graduation-cap.dragging { cursor: grabbing; transform: scale(1.1); }
@keyframes float-animation { 0%, 100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-15px) rotate(1deg); } }
@keyframes throw-cap { 0% { transform: translateY(0) rotate(0deg) scale(1); animation-timing-function: ease-out; } 50% { transform: translateY(-400px) rotate(360deg) scale(0.8); animation-timing-function: ease-in; } 100% { transform: translateY(80vh) rotate(720deg) scale(0.5); opacity: 0; } }
#graduation-cap.thrown { animation: throw-cap 2.5s forwards; pointer-events: none; }
#carta { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scaleY(0); transform-origin: top; background-color: var(--color-fondo-carta); border: 10px solid var(--color-borde); border-radius: 8px; padding: 30px 40px; box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 0 2px var(--color-principal); width: 80%; max-width: 500px; text-align: left; z-index: 5; opacity: 0; transition: transform 1s cubic-bezier(0.68, -0.55, 0.27, 1.55), opacity 0.5s ease-out; visibility: hidden; }
#carta.visible { transform: translate(-50%, -50%) scaleY(1); opacity: 1; visibility: visible; }
#close-button { position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; background-color: rgba(0, 0, 0, 0.1); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; color: var(--color-texto-secundario); cursor: pointer; transition: all 0.2s ease; }
#close-button:hover { background-color: rgba(0, 0, 0, 0.2); transform: scale(1.1); }
#carta-fecha { text-align: right; font-size: 1rem; color: var(--color-texto-secundario); margin-bottom: 20px; }
#carta-saludo { font-family: 'Parisienne', cursive; font-size: 2.8rem; color: var(--color-principal); margin: 0 0 15px 0; font-weight: normal; }
#carta-cuerpo p { font-size: 1.15rem; line-height: 1.6; color: var(--color-texto-principal); margin: 10px 0; }
#carta-despedida { font-size: 1.2rem; font-style: italic; color: var(--color-texto-secundario); margin-top: 20px; }
.firma { font-family: 'Parisienne', cursive; font-size: 2rem; margin-top: 20px; color: var(--color-firma); text-align: right; }
#instruction { position: absolute; bottom: 30px; color: rgba(255, 255, 255, 0.7); font-size: 1.2rem; z-index: 11; text-shadow: 0 0 5px black; transition: opacity 0.5s; }
#share-button { display: none; margin: 25px auto 10px; padding: 10px 20px; font-family: 'Cormorant Garamond', serif; font-size: 1.1rem; font-weight: bold; color: white; background-color: var(--color-principal); border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s, transform 0.2s; }
#share-button:hover { background-color: var(--color-texto-principal); transform: scale(1.05); }
#share-button:active { transform: scale(1); }
const urlParams = new URLSearchParams(window.location.search);
// MODIFICAR: Valores por defecto si no se usan parámetros en la URL.
// Ejemplo de URL: preview?nombre=Ana&carrera=Diseño Gráfico&genero=mujer&fecha=15 de junio del 2026
const graduateName = urlParams.get('nombre') || "Alguien Genial";
const graduateMajor = urlParams.get('carrera') || "una carrera increíble";
const genero = urlParams.get('genero') || "mujer";
const graduateDate = urlParams.get('fecha') || "11 de Julio de 2025";
const remitente = "Tu Familia y Amigos";
let saludo, cuerpoCarta, despedida;
if (genero === 'mujer') {
// MODIFICAR: Texto para la versión femenina.
saludo = `Queridísima ${graduateName},`;
cuerpoCarta = `<p>¡No podemos estar más orgullosos de ti! Hoy, al terminar tu carrera en <strong>${graduateMajor}</strong>, no solo celebras un logro académico, sino el comienzo de un futuro brillante que te has ganado a pulso.</p><p>Hemos visto tu esfuerzo, tu pasión y las noches en vela. Eres una mujer increíblemente talentosa y fuerte. ¡El mundo es tuyo!</p>`;
despedida = "Con todo nuestro amor y admiración,";
// MODIFICAR: Paleta de colores para la versión femenina.
document.documentElement.style.setProperty('--color-principal', '#c2185b');
document.documentElement.style.setProperty('--color-fondo-carta', '#fce4ec');
document.documentElement.style.setProperty('--color-borde', '#f8bbd0');
document.documentElement.style.setProperty('--color-texto-principal', '#880e4f');
document.documentElement.style.setProperty('--color-texto-secundario', '#666');
document.documentElement.style.setProperty('--color-firma', '#c2185b');
} else {
// MODIFICAR: Texto para la versión masculina.
saludo = `Querido ${graduateName},`;
cuerpoCarta = `<p>¡No podemos estar más orgullosos de ti! Hoy, al terminar tu carrera en <strong>${graduateMajor}</strong>, no solo celebras un logro académico, sino el comienzo de un futuro brillante que te has ganado a pulso.</p><p>Hemos visto tu esfuerzo, tu dedicación y tu perseverancia. Eres un hombre increíblemente capaz y valiente. ¡El mundo es tuyo!</p>`;
despedida = "Con todo nuestro cariño y apoyo,";
}
document.getElementById('carta-fecha').textContent = graduateDate;
document.getElementById('carta-saludo').textContent = saludo;
document.getElementById('carta-cuerpo').innerHTML = cuerpoCarta;
document.getElementById('carta-despedida').textContent = despedida;
document.querySelector('.firma').textContent = remitente;
const canvas = document.getElementById('fireworks-canvas');
const ctx = canvas.getContext('2d');
let fireworks = [], particles = [];
let fireworksInterval, fireworkIntensity = 2000;
function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }
window.addEventListener('resize', resizeCanvas); resizeCanvas();
function Particle(x, y, color) { this.x = x; this.y = y; this.color = color; this.vel = { x: (Math.random() - 0.5) * 8, y: (Math.random() - 0.5) * 8 }; this.alpha = 1; this.gravity = 0.1; this.friction = 0.98; }
Particle.prototype.update = function() { this.vel.x *= this.friction; this.vel.y *= this.friction; this.vel.y += this.gravity; this.x += this.vel.x; this.y += this.vel.y; this.alpha -= 0.02; };
Particle.prototype.draw = function() { ctx.save(); ctx.globalAlpha = this.alpha; ctx.beginPath(); ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, false); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); };
function Firework(x, y, targetX, targetY) { this.x = x; this.y = y; this.targetX = targetX; this.targetY = targetY; const angle = Math.atan2(targetY - y, targetX - x); const speed = 10; this.vel = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; this.color = `hsl(${Math.random() * 360}, 100%, 50%)`; }
Firework.prototype.update = function() { this.x += this.vel.x; this.y += this.vel.y; if (Math.hypot(this.targetX - this.x, this.targetY - this.y) < 5) { this.explode(); return true; } return false; };
Firework.prototype.draw = function() { ctx.beginPath(); ctx.arc(this.x, this.y, 3, 0, Math.PI * 2); ctx.fillStyle = this.color; ctx.fill(); };
Firework.prototype.explode = function() { for (let i = 0; i < 100; i++) { particles.push(new Particle(this.x, this.y, this.color)); } };
function launchFirework() { fireworks.push(new Firework(canvas.width / 2, canvas.height, Math.random() * canvas.width, Math.random() * canvas.height / 2)); }
function restoreFireworks() { clearInterval(fireworksInterval); fireworkIntensity = 2000; fireworksInterval = setInterval(launchFirework, fireworkIntensity); }
function intensifyFireworks() { clearInterval(fireworksInterval); fireworkIntensity = 200; fireworksInterval = setInterval(launchFirework, fireworkIntensity); }
function animate() { requestAnimationFrame(animate); ctx.fillStyle = 'rgba(0, 0, 26, 0.1)'; ctx.fillRect(0, 0, canvas.width, canvas.height); fireworks.forEach((fw, i) => { if (fw.update()) fireworks.splice(i, 1); else fw.draw(); }); particles.forEach((p, i) => { if (p.alpha <= 0) particles.splice(i, 1); else { p.update(); p.draw(); } }); }
fireworksInterval = setInterval(launchFirework, fireworkIntensity); animate();
const cap = document.getElementById('graduation-cap'), carta = document.getElementById('carta'), instruction = document.getElementById('instruction'), closeButton = document.getElementById('close-button'), shareButton = document.getElementById('share-button');
let isDragging = false, hasBeenThrown = false;
function startThrow() { if (hasBeenThrown) return; isDragging = true; cap.classList.add('dragging'); }
function endThrow() { if (!isDragging || hasBeenThrown) return; isDragging = false; hasBeenThrown = true; cap.classList.remove('dragging'); cap.classList.add('thrown'); instruction.style.opacity = '0'; cap.addEventListener('animationend', () => { carta.classList.add('visible'); intensifyFireworks(); }, { once: true }); }
cap.addEventListener('mousedown', startThrow); window.addEventListener('mouseup', endThrow);
cap.addEventListener('touchstart', startThrow, { passive: true }); window.addEventListener('touchend', endThrow);
function resetAnimation() { carta.classList.remove('visible'); cap.classList.remove('thrown'); cap.style = ''; hasBeenThrown = false; isDragging = false; instruction.style.opacity = '1'; restoreFireworks(); }
closeButton.addEventListener('click', resetAnimation);
if (window.location.search) {
shareButton.style.display = 'block';
shareButton.addEventListener('click', async () => {
const url = window.location.href;
// MODIFICAR: Texto promocional que se añade al compartir.
const customText = "visita https://www.tlanex.com para más";
const shareData = {
title: '¡Una Carta para Ti!',
text: `¡Mira la increíble carta que me hicieron! ${customText}`,
url: url,
};
if (navigator.share) {
try { await navigator.share(shareData); } catch (err) { console.error('Error al compartir:', err); }
} else {
try {
const textToCopy = `${shareData.title}\n${shareData.url}\n\n${customText}`;
await navigator.clipboard.writeText(textToCopy);
alert('¡Enlace copiado al portapapeles!');
} catch (err) {
console.error('No se pudo copiar el enlace:', err);
alert('Error al copiar el enlace.');
}
}
});
}