Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/zzgt648wtayxdyhtNicole.XVPlain Text
```html
<!DOCTYPE html>
<html lang="es" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mis 15 Años - Nicole</title>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts -->
<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=Cinzel:wght@400;600;700;800;900&family=Great+Vibes&family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,600;1,400&display=swap" rel="stylesheet">
<!-- FontAwesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Canvas Confetti -->
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
<!-- Tailwind Configuration -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
gold: {
light: '#F8E8B0',
DEFAULT: '#D4AF37',
dark: '#A38020',
accent: '#E5C158'
},
beige: {
light: '#FDFBF7',
DEFAULT: '#F5F2EB',
dark: '#EBDCCB'
},
dark: {
950: '#070707',
900: '#0B0B0B',
800: '#121212',
700: '#1A1A1A',
600: '#282828'
}
},
fontFamily: {
title: ['Cinzel', 'serif'],
script: ['"Great Vibes"', 'cursive'],
serif: ['"Playfair Display"', 'serif'],
sans: ['Montserrat', 'sans-serif'],
}
}
}
}
</script>
<style>
.font-script { font-family: 'Great Vibes', cursive; }
.font-title { font-family: 'Cinzel', serif; }
.font-serif-detail { font-family: 'Playfair Display', serif; }
/* Gold Gradient Text */
.gold-gradient-text {
background: linear-gradient(135deg, #F8E8B0 0%, #D4AF37 45%, #AA820A 75%, #F8E8B0 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* Gold Gradient Borders & Shadows */
.gold-border-glow {
border: 1px solid rgba(212, 175, 55, 0.35);
box-shadow: 0 0 25px -5px rgba(212, 175, 55, 0.15);
}
.gold-box-shadow {
box-shadow: 0 10px 30px -10px rgba(212, 175, 55, 0.25);
}
/* Particles Layer */
#particlesCanvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
/* Custom Scrollbars */
::-webkit-scrollbar {
width: 7px;
}
::-webkit-scrollbar-track {
background: #0B0B0B;
}
::-webkit-scrollbar-thumb {
background: #A38020;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #D4AF37;
}
</style>
</head>
<body class="bg-dark-950 text-beige-dark font-sans antialiased selection:bg-gold selection:text-dark-900 relative min-h-screen overflow-x-hidden">
<!-- Gold Particle Canvas -->
<canvas id="particlesCanvas"></canvas>
<!-- Audio Player ("Do I Wanna Know?") -->
<audio id="bgAudio" loop preload="auto">
<source src="https://cdn.pixabay.com/download/audio/2022/11/06/audio_c89b7b6408.mp3?filename=do-i-wanna-know-riff-style-126284.mp3" type="audio/mpeg">
<source src="https://cdn.pixabay.com/download/audio/2022/03/15/audio_c8c8a816a2.mp3?filename=a-small-miracle-132333.mp3" type="audio/mpeg">
</audio>
<!-- Floating Audio Control -->
<div class="fixed bottom-6 right-6 z-40">
<button id="musicToggle" onclick="toggleMusic()" class="flex items-center gap-3 bg-dark-800/90 border border-gold/40 text-gold-light px-4 py-3 rounded-full shadow-2xl hover:border-gold hover:scale-105 transition-all duration-300 backdrop-blur-md group">
<span id="musicIcon" class="w-8 h-8 rounded-full bg-gold/20 flex items-center justify-center text-gold group-hover:bg-gold group-hover:text-dark-950 transition-colors">
<i class="fa-solid fa-music text-xs"></i>
</span>
<span id="musicStatus" class="text-xs font-title tracking-wider uppercase pr-1 text-beige">Do I Wanna Know?</span>
</button>
</div>
<!-- Floating Admin Trigger Button -->
<div class="fixed bottom-6 left-6 z-40">
<button onclick="requestAdminAccess()" class="flex items-center gap-2 bg-dark-800/90 border border-gold/40 text-gold-light px-3.5 py-3 rounded-full shadow-2xl hover:bg-gold hover:text-dark-950 transition-all duration-300 backdrop-blur-md text-xs font-title tracking-wider">
<i class="fa-solid fa-sliders text-sm"></i>
<span class="hidden sm:inline">Modo Editor</span>
</button>
</div>
<!-- HERO SECTION -->
<section class="relative min-h-screen flex items-center justify-center text-center p-6 bg-cover bg-center bg-fixed z-10" style="background-image: linear-gradient(to bottom, rgba(7,7,7,0.75), rgba(7,7,7,0.95)), url('https://images.unsplash.com/photo-1519741497674-611481863552?auto=format&fit=crop&w=1920&q=80');">
<div class="max-w-4xl mx-auto space-y-6 my-auto py-12 relative z-20">
<p class="font-title text-xs sm:text-sm tracking-[0.4em] uppercase text-gold-light/90">
Mis XV Años
</p>
<h1 class="font-script text-7xl sm:text-8xl md:text-9xl gold-gradient-text drop-shadow-2xl py-1">
Nicole
</h1>
<div class="flex items-center justify-center gap-4 my-2">
<div class="h-[1px] w-12 md:w-24 bg-gradient-to-r from-transparent to-gold"></div>
<p id="heroDisplayDate" class="font-title text-sm md:text-xl text-beige tracking-widest uppercase">
28 . 11 . 2026
</p>
<div class="h-[1px] w-12 md:w-24 bg-gradient-to-l from-transparent to-gold"></div>
</div>
<p class="max-w-lg mx-auto text-xs md:text-sm text-beige/80 font-light italic leading-relaxed px-4">
"Hay momentos en la vida que son especiales por sí solos, pero compartirlos con las personas que más queremos los convierte en memorias inolvidables."
</p>
<!-- CUENTA REGRESIVA -->
<div class="pt-8">
<p class="text-[11px] font-title tracking-widest text-gold-light/80 uppercase mb-4">La noche soñada comienza en:</p>
<div class="grid grid-cols-4 gap-3 md:gap-5 max-w-md mx-auto">
<div class="bg-dark-800/80 border border-gold/30 p-3 md:p-4 rounded-2xl shadow-xl backdrop-blur-sm">
<span id="days" class="block font-title text-2xl md:text-4xl font-bold text-gold-light">00</span>
<span class="text-[9px] md:text-xs text-beige/70 uppercase tracking-wider font-title">Días</span>
</div>
<div class="bg-dark-800/80 border border-gold/30 p-3 md:p-4 rounded-2xl shadow-xl backdrop-blur-sm">
<span id="hours" class="block font-title text-2xl md:text-4xl font-bold text-gold-light">00</span>
<span class="text-[9px] md:text-xs text-beige/70 uppercase tracking-wider font-title">Horas</span>
</div>
<div class="bg-dark-800/80 border border-gold/30 p-3 md:p-4 rounded-2xl shadow-xl backdrop-blur-sm">
<span id="minutes" class="block font-title text-2xl md:text-4xl font-bold text-gold-light">00</span>
<span class="text-[9px] md:text-xs text-beige/70 uppercase tracking-wider font-title">Min</span>
</div>
<div class="bg-dark-800/80 border border-gold/30 p-3 md:p-4 rounded-2xl shadow-xl backdrop-blur-sm">
<span id="seconds" class="block font-title text-2xl md:text-4xl font-bold text-gold-light">00</span>
<span class="text-[9px] md:text-xs text-beige/70 uppercase tracking-wider font-title">Seg</span>
</div>
</div>
</div>
<!-- Scroll indicator -->
<div class="pt-10">
<a href="#detalles" class="inline-flex flex-col items-center text-gold/80 hover:text-gold transition-colors duration-300">
<span class="text-[10px] uppercase tracking-widest mb-2 font-title">Ver Evento</span>
<i class="fa-solid fa-chevron-down text-sm animate-bounce"></i>
</a>
</div>
</div>
</section>
<!-- UBICACIÓN & DETALLES DEL EVENTO -->
<section id="detalles" class="py-24 px-4 max-w-6xl mx-auto relative z-10">
<div class="text-center mb-16 space-y-3">
<span class="text-gold font-script text-4xl">Dónde & Cuándo</span>
<h2 class="font-title text-3xl md:text-4xl gold-gradient-text uppercase tracking-widest">Detalles del Evento</h2>
<div class="w-20 h-[2px] bg-gradient-to-r from-transparent via-gold to-transparent mx-auto"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- FIESTA Y LUGAR -->
<div class="bg-dark-800/90 rounded-2xl p-8 border border-gold/20 shadow-2xl hover:border-gold/50 transition-all duration-300 text-center flex flex-col justify-between group gold-box-shadow">
<div class="space-y-4">
<div class="w-16 h-16 mx-auto bg-dark-700 rounded-full border border-gold/40 flex items-center justify-center text-gold group-hover:scale-110 group-hover:bg-gold group-hover:text-dark-950 transition-all duration-300">
<i class="fa-solid fa-glass-cheers text-2xl"></i>
</div>
<h3 class="font-title text-xl text-gold-light">Recepción & Fiesta</h3>
<p id="displayEventDate" class="text-gold-accent font-semibold text-xs tracking-widest uppercase">SÁBADO 28 DE NOVIEMBRE, 2026</p>
<p id="displayEventTime" class="text-beige/70 text-sm"><i class="fa-regular fa-clock mr-1 text-gold"></i> 21:00 HS</p>
<div class="border-t border-dark-600 my-4"></div>
<h4 id="displayVenueName" class="font-bold text-beige text-base">Salón de Eventos</h4>
<p id="displayVenueAddress" class="text-beige-dark/70 text-sm">Amado Nervo 2333, Merlo, Buenos Aires</p>
</div>
<div class="mt-8">
<a id="mapLink" href="https://www.google.com/maps/search/?api=1&query=Amado+Nervo+2333,+Merlo,+Buenos+Aires" target="_blank" rel="noopener noreferrer" class="inline-flex items-center justify-center gap-2 w-full py-3 px-6 rounded-xl bg-gold/10 border border-gold/50 text-gold-light hover:bg-gold hover:text-dark-950 transition-all duration-300 font-title text-xs tracking-wider uppercase">
<i class="fa-solid fa-map-location-dot"></i> Abrir en Maps
</a>
</div>
</div>
<!-- DRESS CODE -->
<div class="bg-dark-800/90 rounded-2xl p-8 border border-gold/20 shadow-2xl hover:border-gold/50 transition-all duration-300 text-center flex flex-col justify-between group gold-box-shadow">
<div class="space-y-4">
<div class="w-16 h-16 mx-auto bg-dark-700 rounded-full border border-gold/40 flex items-center justify-center text-gold group-hover:scale-110 group-hover:bg-gold group-hover:text-dark-950 transition-all duration-300">
<i class="fa-solid fa-user-tie text-2xl"></i>
</div>
<h3 class="font-title text-xl text-gold-light">Dress Code</h3>
<p id="displayDressCode" class="text-gold-accent font-semibold text-xs tracking-widest uppercase">Elegante / Formal</p>
<div class="border-t border-dark-600 my-4"></div>
<p class="text-beige/70 text-xs leading-relaxed">
Queremos que esta noche brille con glamour. Te esperamos con tu mejor atuendo de fiesta.
</p>
<p class="text-[11px] text-gold-light/80 italic mt-2">
* Reservar los tonos dorado y blanco para la homenajeadas.
</p>
</div>
<div class="mt-8">
<div class="py-3 px-4 rounded-xl bg-dark-700/50 border border-dark-600 text-beige/60 text-xs font-title uppercase tracking-wider">
Glamour & Estilo
</div>
</div>
</div>
<!-- INFORMACIÓN IMPORTANTE -->
<div class="bg-dark-800/90 rounded-2xl p-8 border border-gold/20 shadow-2xl hover:border-gold/50 transition-all duration-300 text-center flex flex-col justify-between group gold-box-shadow">
<div class="space-y-4">
<div class="w-16 h-16 mx-auto bg-dark-700 rounded-full border border-gold/40 flex items-center justify-center text-gold group-hover:scale-110 group-hover:bg-gold group-hover:text-dark-950 transition-all duration-300">
<i class="fa-solid fa-calendar-check text-2xl"></i>
</div>
<h3 class="font-title text-xl text-gold-light">Puntualidad & RSVP</h3>
<p class="text-gold-accent font-semibold text-xs tracking-widest uppercase">Confirmación Previa</p>
<div class="border-t border-dark-600 my-4"></div>
<p class="text-beige/70 text-xs leading-relaxed">
Por razones de organización, solicitamos amablemente confirmar tu presencia con anticipación.
</p>
</div>
<div class="mt-8">
<a href="#rsvp" class="inline-flex items-center justify-center gap-2 w-full py-3 px-6 rounded-xl bg-gradient-to-r from-gold-dark via-gold to-gold-light text-dark-950 font-bold hover:opacity-90 transition-all duration-300 font-title text-xs tracking-wider uppercase">
<i class="fa-solid fa-check text-xs"></i> Confirmar Ahora
</a>
</div>
</div>
</div>
</section>
<!-- MESA DE REGALOS & SUGERIR CANCIÓN -->
<section class="py-20 bg-dark-800/50 border-y border-gold/20 px-4 relative z-10">
<div class="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- MESA DE REGALOS -->
<div class="bg-dark-800 p-8 rounded-2xl border border-gold/30 shadow-2xl flex flex-col justify-between">
<div class="space-y-4">
<div class="flex items-center gap-3 text-gold">
<i class="fa-solid fa-gift text-3xl"></i>
<h3 class="font-title text-2xl text-gold-light">Mesa de Regalos</h3>
</div>
<p class="text-beige/70 text-sm leading-relaxed">
Tu presencia es el mejor regalo para Nicole. Si deseas realizar un obsequio para ayudar a cumplir sus sueños, te dejamos los datos bancarios.
</p>
</div>
<div class="mt-8 space-y-4">
<div class="bg-dark-900/90 p-4 rounded-xl border border-gold/20 flex items-center justify-between">
<div>
<span class="text-[10px] text-gold-light/70 uppercase tracking-widest font-title block">Alias Bancario</span>
<span id="aliasValue" class="font-mono text-gold font-bold text-base md:text-lg">nicole.mis15.mp</span>
</div>
<button onclick="copyText('aliasValue', 'copyAlertAlias')" class="px-4 py-2 bg-gold/10 hover:bg-gold hover:text-dark-950 text-gold border border-gold/40 rounded-lg text-xs font-title tracking-wider uppercase transition-all duration-300">
<i class="fa-regular fa-copy mr-1"></i> Copiar
</button>
</div>
<p id="copyAlertAlias" class="text-center text-xs text-emerald-400 font-medium hidden">¡Alias copiado al portapapeles!</p>
<div class="bg-dark-900/90 p-4 rounded-xl border border-gold/20 flex items-center justify-between">
<div>
<span class="text-[10px] text-gold-light/70 uppercase tracking-widest font-title block">CBU / CVU</span>
<span id="cbuValue" class="font-mono text-beige/90 text-xs md:text-sm">0000003100084739201948</span>
</div>
<button onclick="copyText('cbuValue', 'copyAlertCbu')" class="px-4 py-2 bg-gold/10 hover:bg-gold hover:text-dark-950 text-gold border border-gold/40 rounded-lg text-xs font-title tracking-wider uppercase transition-all duration-300">
<i class="fa-regular fa-copy mr-1"></i> Copiar
</button>
</div>
<p id="copyAlertCbu" class="text-center text-xs text-emerald-400 font-medium hidden">¡CBU copiado al portapapeles!</p>
</div>
</div>
<!-- SUGERIR CANCIÓN PARA EL DJ -->
<div class="bg-dark-800 p-8 rounded-2xl border border-gold/30 shadow-2xl flex flex-col justify-between">
<div class="space-y-4">
<div class="flex items-center gap-3 text-gold">
<i class="fa-solid fa-compact-disc text-3xl animate-spin" style="animation-duration: 10s;"></i>
<h3 class="font-title text-2xl text-gold-light">Sugerir Canción</h3>
</div>
<p class="text-beige/70 text-sm leading-relaxed">
¿Qué tema no puede faltar en la fiesta? Recomienda tus canciones favoritas para la playlist de Nicole.
</p>
</div>
<form id="songForm" onsubmit="handleSongSubmit(event)" class="mt-6 space-y-4">
<div>
<input type="text" id="songInput" required placeholder="Nombre de la Canción y Artista" class="w-full px-4 py-3 rounded-xl bg-dark-900 border border-gold/30 text-beige placeholder-beige/40 focus:outline-none focus:border-gold text-sm transition-colors">
</div>
<button type="submit" class="w-full py-3.5 px-6 rounded-xl bg-gold/20 border border-gold/50 text-gold hover:bg-gold hover:text-dark-950 font-title text-xs tracking-wider uppercase transition-all duration-300 flex items-center justify-center gap-2">
<i class="fa-solid fa-paper-plane"></i> Enviar Recomendación
</button>
</form>
<p id="songSuccess" class="text-center text-xs text-emerald-400 font-medium hidden mt-3">¡Sugerencia enviada correctamente!</p>
</div>
</div>
</section>
<!-- FORMULARIO RSVP -->
<section id="rsvp" class="py-24 px-4 max-w-3xl mx-auto relative z-10">
<div class="bg-dark-800 rounded-3xl p-8 md:p-12 border border-gold/40 shadow-2xl relative overflow-hidden gold-box-shadow">
<div class="text-center mb-10 space-y-2">
<span class="text-gold font-script text-4xl">RSVP</span>
<h2 class="font-title text-3xl gold-gradient-text uppercase tracking-widest">Confirma tu Asistencia</h2>
<p class="text-beige/60 text-xs">Completa el formulario para enviarle tu confirmación a Nicole</p>
</div>
<form id="rsvpForm" onsubmit="submitRSVP(event)" class="space-y-6">
<!-- Nombre -->
<div>
<label class="block text-xs font-title uppercase tracking-wider text-gold-light mb-2">Nombre Completo *</label>
<input type="text" id="rsvpName" required placeholder="Ingresa tu nombre y apellido" class="w-full px-4 py-3.5 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm transition-colors">
</div>
<!-- Asistencia -->
<div>
<label class="block text-xs font-title uppercase tracking-wider text-gold-light mb-2">¿Confirmas tu asistencia? *</label>
<div class="grid grid-cols-2 gap-4">
<label class="cursor-pointer">
<input type="radio" name="attending" value="Sí, asistiré" checked class="peer hidden">
<div class="p-3.5 text-center rounded-xl border border-gold/30 bg-dark-900 peer-checked:border-gold peer-checked:bg-gold/20 peer-checked:text-gold text-beige/70 text-xs font-title uppercase tracking-wider transition-all">
<i class="fa-solid fa-circle-check mr-1 text-gold"></i> ¡Ahí estaré!
</div>
</label>
<label class="cursor-pointer">
<input type="radio" name="attending" value="No podré asistir" class="peer hidden">
<div class="p-3.5 text-center rounded-xl border border-gold/30 bg-dark-900 peer-checked:border-gold peer-checked:bg-gold/20 peer-checked:text-gold text-beige/70 text-xs font-title uppercase tracking-wider transition-all">
<i class="fa-solid fa-circle-xmark mr-1 text-gold"></i> No podré ir
</div>
</label>
</div>
</div>
<!-- Acompañantes -->
<div>
<label class="block text-xs font-title uppercase tracking-wider text-gold-light mb-2">Acompañantes / Cupos</label>
<select id="rsvpGuests" class="w-full px-4 py-3.5 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm transition-colors">
<option value="Solo yo">Asistiré Solo/a</option>
<option value="+1 Acompañante">+1 Acompañante</option>
<option value="+2 Acompañantes">+2 Acompañantes</option>
<option value="Grupo Familiar">Grupo Familiar</option>
</select>
</div>
<!-- Restricciones Alimentarias -->
<div>
<label class="block text-xs font-title uppercase tracking-wider text-gold-light mb-2">Menú Especial / Restricciones</label>
<input type="text" id="rsvpDiet" placeholder="Ej: Vegetariano, Celíaco, Ninguna" class="w-full px-4 py-3.5 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm transition-colors">
</div>
<!-- Mensaje personal -->
<div>
<label class="block text-xs font-title uppercase tracking-wider text-gold-light mb-2">Mensaje o nota adicional</label>
<textarea id="rsvpMessage" rows="2" placeholder="Un mensaje especial para Nicole..." class="w-full px-4 py-3.5 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm transition-colors"></textarea>
</div>
<!-- Botón Confirmar por WhatsApp -->
<button type="submit" class="w-full py-4 px-6 rounded-xl bg-gradient-to-r from-emerald-600 via-emerald-500 to-emerald-700 text-white font-title text-sm tracking-wider uppercase shadow-xl hover:opacity-95 transition-all duration-300 flex items-center justify-center gap-3">
<i class="fa-brands fa-whatsapp text-xl"></i> Confirmar Asistencia por WhatsApp
</button>
</form>
</div>
</section>
<!-- GALERÍA DE FOTOS -->
<section class="py-20 px-4 max-w-6xl mx-auto relative z-10">
<div class="text-center mb-16 space-y-3">
<span class="text-gold font-script text-4xl">Fotografías</span>
<h2 class="font-title text-3xl md:text-4xl gold-gradient-text uppercase tracking-widest">Galería de Recuerdos</h2>
<div class="w-20 h-[2px] bg-gradient-to-r from-transparent via-gold to-transparent mx-auto"></div>
</div>
<div id="galleryGrid" class="grid grid-cols-2 md:grid-cols-4 gap-4">
<!-- Renderizado dinámico desde script/localStorage -->
</div>
</section>
<!-- MURO DE DESEOS -->
<section class="py-20 bg-dark-800/40 border-t border-gold/20 px-4 relative z-10">
<div class="max-w-4xl mx-auto space-y-10">
<div class="text-center space-y-2">
<span class="text-gold font-script text-4xl">Cariño & Dedicatorias</span>
<h2 class="font-title text-3xl gold-gradient-text uppercase tracking-widest">Muro de Deseos</h2>
<p class="text-beige/60 text-xs">Déjale tus felicitaciones a Nicole en su gran día</p>
</div>
<!-- Formulario de Mensaje -->
<form id="wishForm" onsubmit="submitWish(event)" class="bg-dark-800 p-6 rounded-2xl border border-gold/30 shadow-xl space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" id="wishAuthor" required placeholder="Tu Nombre o Familia" class="w-full px-4 py-3 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm">
<button type="submit" class="py-3 px-6 rounded-xl bg-gradient-to-r from-gold-dark via-gold to-gold-light text-dark-950 font-bold font-title text-xs tracking-wider uppercase transition-all duration-300">
<i class="fa-solid fa-heart mr-1"></i> Publicar Deseo
</button>
</div>
<textarea id="wishText" required rows="3" placeholder="Escribe tus palabras aquí..." class="w-full px-4 py-3 rounded-xl bg-dark-900 border border-gold/30 text-beige focus:outline-none focus:border-gold text-sm"></textarea>
</form>
<!-- Lista de Deseos -->
<div id="wishesGrid" class="grid grid-cols-1 md:grid-cols-2 gap-4 max-h-96 overflow-y-auto pr-2">
<!-- Carga dinámica -->
</div>
</div>
</section>
<!-- FOOTER -->
<footer class="bg-dark-950 border-t border-gold/20 py-12 text-center space-y-4 relative z-10">
<p class="font-script text-5xl gold-gradient-text">Nicole</p>
<p id="footerSubtitle" class="font-title text-xs tracking-widest text-gold-light/70 uppercase">28 de Noviembre de 2026 — Merlo, Buenos Aires</p>
<div class="w-16 h-[1px] bg-gold/30 mx-auto"></div>
<p class="text-[10px] text-beige/40 uppercase tracking-widest font-title">¡Te esperamos para celebrar esta noche inolvidable!</p>
</footer>
<!-- MODAL DE ADMINISTRACIÓN / EDITOR -->
<div id="adminModal" class="fixed inset-0 z-50 hidden bg-black/85 backdrop-blur-md flex items-center justify-center p-4">
<div class="bg-dark-800 border border-gold/40 rounded-3xl p-6 md:p-8 max-w-4xl w-full max-h-[90vh] overflow-y-auto space-y-6 relative shadow-2xl text-beige">
<div class="flex items-center justify-between border-b border-gold/20 pb-4">
<div class="flex items-center gap-3">
<i class="fa-solid fa-sliders text-2xl text-gold"></i>
<div>
<h3 class="font-title text-xl text-gold-light">Panel de Administración</h3>
<p class="text-xs text-beige/60">Edita la información de la web y gestiona tus invitados</p>
</div>
</div>
<button onclick="closeAdminModal()" class="text-beige/60 hover:text-gold text-2xl">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<!-- TABS DE NAVEGACIÓN EN ADMIN -->
<div class="flex border-b border-dark-600 gap-2 overflow-x-auto pb-2">
<button onclick="switchAdminTab('event')" id="tabBtn-event" class="px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-gold text-dark-950 font-bold">
<i class="fa-solid fa-pen-to-square mr-1"></i> Datos Evento
</button>
<button onclick="switchAdminTab('rsvps')" id="tabBtn-rsvps" class="px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-dark-700 text-beige hover:text-gold">
<i class="fa-solid fa-users mr-1"></i> Confirmados (<span id="rsvpCountBadge">0</span>)
</button>
<button onclick="switchAdminTab('gallery')" id="tabBtn-gallery" class="px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-dark-700 text-beige hover:text-gold">
<i class="fa-solid fa-images mr-1"></i> Fotos
</button>
<button onclick="switchAdminTab('wishes')" id="tabBtn-wishes" class="px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-dark-700 text-beige hover:text-gold">
<i class="fa-solid fa-comments mr-1"></i> Moderar Deseos
</button>
</div>
<!-- TAB 1: DATOS DEL EVENTO -->
<div id="adminTab-event" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Fecha Evento (Texto)</label>
<input type="text" id="editEventDateText" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Horario de Inicio</label>
<input type="text" id="editEventTime" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Nombre del Salón / Lugar</label>
<input type="text" id="editVenueName" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Dirección Completa</label>
<input type="text" id="editVenueAddress" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Teléfono WhatsApp RSVP (con cód. país)</label>
<input type="text" id="editWhatsappPhone" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Dress Code</label>
<input type="text" id="editDressCode" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Alias Bancario</label>
<input type="text" id="editBankAlias" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">CBU / CVU Bancario</label>
<input type="text" id="editBankCbu" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
</div>
</div>
<div>
<label class="block text-xs font-title uppercase text-gold-light mb-1">Fecha ISO para Cuenta Regresiva (YYYY-MM-DDTHH:MM:SS)</label>
<input type="text" id="editTargetDate" class="w-full px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs" placeholder="2026-11-28T21:00:00">
</div>
<button onclick="saveEventSettings()" class="w-full py-3 bg-gold text-dark-950 font-bold font-title text-xs tracking-wider uppercase rounded-xl hover:opacity-90 transition-opacity">
<i class="fa-solid fa-floppy-disk mr-1"></i> Guardar Cambios del Evento
</button>
</div>
<!-- TAB 2: CONFIRMADOS (RSVP) -->
<div id="adminTab-rsvps" class="space-y-4 hidden">
<div class="flex justify-between items-center">
<span class="text-xs text-beige/70">Lista de respuestas registradas localmente:</span>
<button onclick="exportRSVPsCSV()" class="px-3 py-1.5 bg-emerald-700/50 border border-emerald-500/30 text-emerald-200 text-xs rounded-lg hover:bg-emerald-700">
<i class="fa-solid fa-file-csv mr-1"></i> Descargar CSV
</button>
</div>
<div class="max-h-72 overflow-y-auto border border-dark-600 rounded-xl bg-dark-900 p-3">
<table class="w-full text-left text-xs">
<thead>
<tr class="text-gold border-b border-dark-600 pb-2">
<th class="py-2">Nombre</th>
<th>Asistencia</th>
<th>Cupo</th>
<th>Menú/Notas</th>
<th>Acción</th>
</tr>
</thead>
<tbody id="adminRsvpTableBody" class="text-beige/80">
<!-- Carga dinámica -->
</tbody>
</table>
</div>
<div class="flex justify-end">
<button onclick="clearAllRSVPs()" class="px-3 py-1.5 bg-rose-900/40 border border-rose-500/30 text-rose-300 text-xs rounded-lg hover:bg-rose-900">
Borrar Registro de Confirmados
</button>
</div>
</div>
<!-- TAB 3: FOTOS GALERÍA -->
<div id="adminTab-gallery" class="space-y-4 hidden">
<div class="flex gap-2">
<input type="text" id="newPhotoUrl" placeholder="Pegar URL de la imagen (https://...)" class="flex-1 px-3 py-2 rounded-lg bg-dark-900 border border-gold/30 text-xs">
<button onclick="addGalleryPhoto()" class="px-4 py-2 bg-gold text-dark-950 font-bold text-xs rounded-lg font-title uppercase">
<i class="fa-solid fa-plus"></i> Agregar
</button>
</div>
<div id="adminGalleryPreview" class="grid grid-cols-3 sm:grid-cols-4 gap-3 max-h-64 overflow-y-auto p-2 border border-dark-600 rounded-xl bg-dark-900">
<!-- Carga dinámica de thumbnails con botón eliminar -->
</div>
</div>
<!-- TAB 4: MODERAR DESEOS -->
<div id="adminTab-wishes" class="space-y-4 hidden">
<p class="text-xs text-beige/60">Elimina comentarios inapropiados del Muro de Deseos:</p>
<div id="adminWishesList" class="space-y-2 max-h-72 overflow-y-auto p-2 border border-dark-600 rounded-xl bg-dark-900">
<!-- Carga dinámica -->
</div>
</div>
</div>
</div>
<!-- LIGHTBOX FOTO MODAL -->
<div id="lightboxModal" class="fixed inset-0 z-50 hidden bg-black/95 flex items-center justify-center p-4" onclick="closeLightbox()">
<button class="absolute top-6 right-6 text-gold text-3xl">
<i class="fa-solid fa-xmark"></i>
</button>
<img id="lightboxImg" src="" alt="Foto Ampliada" class="max-h-[85vh] max-w-[90vw] rounded-xl border border-gold/30 shadow-2xl object-contain">
</div>
<!-- JAVASCRIPT APP LOGIC -->
<script>
// --- DATA DEFAULT STORED IN LOCALSTORAGE ---
const DEFAULT_CONFIG = {
eventDateText: "SÁBADO 28 DE NOVIEMBRE, 2026",
eventTime: "21:00 HS",
venueName: "Salón de Eventos",
venueAddress: "Amado Nervo 2333, Merlo, Buenos Aires",
whatsappPhone: "5491172161980",
dressCode: "Elegante / Formal",
bankAlias: "nicole.mis15.mp",
bankCbu: "0000003100084739201948",
targetDateISO: "2026-11-28T21:00:00",
adminPin: "1234",
photos: [
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=800&q=80",
"https://images.unsplash.com/photo-1517841905240-472988babdf9?auto=format&fit=crop&w=800&q=80",
"https://images.unsplash.com/photo-1524504388940-b1c1722653e1?auto=format&fit=crop&w=800&q=80",
"https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=800&q=80"
]
};
function getConfig() {
const saved = localStorage.getItem('nicole_xv_config');
return saved ? JSON.parse(saved) : DEFAULT_CONFIG;
}
function saveConfig(cfg) {
localStorage.setItem('nicole_xv_config', JSON.stringify(cfg));
applyConfigUI();
}
function applyConfigUI() {
const cfg = getConfig();
document.getElementById('displayEventDate').innerText = cfg.eventDateText;
document.getElementById('displayEventTime').innerHTML = `<i class="fa-regular fa-clock mr-1 text-gold"></i> ${cfg.eventTime}`;
document.getElementById('displayVenueName').innerText = cfg.venueName;
document.getElementById('displayVenueAddress').innerText = cfg.venueAddress;
document.getElementById('displayDressCode').innerText = cfg.dressCode;
document.getElementById('aliasValue').innerText = cfg.bankAlias;
document.getElementById('cbuValue').innerText = cfg.bankCbu;
document.getElementById('mapLink').href = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(cfg.venueAddress)}`;
renderGallery();
}
// --- PARTÍCULAS CANVAS ---
const canvas = document.getElementById('particlesCanvas');
const ctx = canvas.getContext('2d');
let particles = [];
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.size = Math.random() * 2 + 0.5;
this.speedY = Math.random() * 0.4 + 0.15;
this.opacity = Math.random() * 0.7 + 0.3;
}
update() {
this.y -= this.speedY;
if (this.y < 0) {
this.y = canvas.height;
this.x = Math.random() * canvas.width;
}
}
draw() {
ctx.fillStyle = `rgba(212, 175, 55, ${this.opacity})`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
for (let i = 0; i < 60; i++) particles.push(new Particle());
function animateParticles() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => { p.update(); p.draw(); });
requestAnimationFrame(animateParticles);
}
animateParticles();
// --- CUENTA REGRESIVA ---
function updateCountdown() {
const cfg = getConfig();
const target = new Date(cfg.targetDateISO).getTime();
const now = new Date().getTime();
const diff = target - now;
if (diff <= 0 || isNaN(diff)) {
document.getElementById('days').innerText = "00";
document.getElementById('hours').innerText = "00";
document.getElementById('minutes').innerText = "00";
document.getElementById('seconds').innerText = "00";
return;
}
const d = Math.floor(diff / (1000 * 60 * 60 * 24));
const h = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const m = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const s = Math.floor((diff % (1000 * 60)) / 1000);
document.getElementById('days').innerText = d < 10 ? '0' + d : d;
document.getElementById('hours').innerText = h < 10 ? '0' + h : h;
document.getElementById('minutes').innerText = m < 10 ? '0' + m : m;
document.getElementById('seconds').innerText = s < 10 ? '0' + s : s;
}
setInterval(updateCountdown, 1000);
// --- REPRODUCTOR MÚSICA ---
const audio = document.getElementById('bgAudio');
const musicIcon = document.getElementById('musicIcon');
const musicStatus = document.getElementById('musicStatus');
let isPlaying = false;
function toggleMusic() {
if (isPlaying) {
audio.pause();
musicIcon.innerHTML = '<i class="fa-solid fa-play text-xs"></i>';
musicStatus.innerText = 'Música Pausada';
} else {
audio.play().then(() => {
musicIcon.innerHTML = '<i class="fa-solid fa-pause text-xs"></i>';
musicStatus.innerText = 'Do I Wanna Know?';
}).catch(e => console.log("Autoplay interact", e));
}
isPlaying = !isPlaying;
}
document.body.addEventListener('click', function startAudioOnce() {
if (!isPlaying) toggleMusic();
document.body.removeEventListener('click', startAudioOnce);
}, { once: true });
// --- UTILS COPIAR TEXTO ---
function copyText(elemId, alertId) {
const text = document.getElementById(elemId).innerText;
const temp = document.createElement("input");
temp.value = text;
document.body.appendChild(temp);
temp.select();
document.execCommand("copy");
document.body.removeChild(temp);
const alertElem = document.getElementById(alertId);
alertElem.classList.remove('hidden');
setTimeout(() => alertElem.classList.add('hidden'), 3000);
}
// --- SUGERIR CANCIÓN ---
function handleSongSubmit(e) {
e.preventDefault();
const input = document.getElementById('songInput');
if (input.value.trim()) {
document.getElementById('songSuccess').classList.remove('hidden');
input.value = '';
setTimeout(() => document.getElementById('songSuccess').classList.add('hidden'), 3000);
}
}
// --- RSVP CONFIRMACIÓN ---
function submitRSVP(e) {
e.preventDefault();
const cfg = getConfig();
const name = document.getElementById('rsvpName').value.trim();
const attending = document.querySelector('input[name="attending"]:checked').value;
const guests = document.getElementById('rsvpGuests').value;
const diet = document.getElementById('rsvpDiet').value.trim() || 'Ninguna';
const message = document.getElementById('rsvpMessage').value.trim() || 'Sin mensaje adicional';
// Guardar localmente
let rsvps = JSON.parse(localStorage.getItem('nicole_xv_rsvps') || '[]');
rsvps.push({ id: Date.now(), name, attending, guests, diet, message, date: new Date().toLocaleDateString() });
localStorage.setItem('nicole_xv_rsvps', JSON.stringify(rsvps));
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
colors: ['#D4AF37', '#F8E8B0', '#ffffff']
});
// Enviar WhatsApp
const text = `¡Hola Nicole! ✨ Confirmación para tu Fiesta de 15:%0A%0A• *Invitado/a:* ${encodeURIComponent(name)}%0A• *¿Asistirá?:* ${encodeURIComponent(attending)}%0A• *Cupo/Acompañantes:* ${encodeURIComponent(guests)}%0A• *Menú/Dieta:* ${encodeURIComponent(diet)}%0A• *Mensaje:* ${encodeURIComponent(message)}`;
setTimeout(() => {
window.open(`https://wa.me/${cfg.whatsappPhone}?text=${text}`, '_blank');
}, 600);
}
// --- GALERÍA FOTOS ---
function renderGallery() {
const cfg = getConfig();
const grid = document.getElementById('galleryGrid');
grid.innerHTML = cfg.photos.map(url => `
<div class="relative group overflow-hidden rounded-2xl border border-gold/20 aspect-square cursor-pointer" onclick="openLightbox('${url}')">
<img src="${url}" alt="Foto Nicole" class="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500">
<div class="absolute inset-0 bg-dark-950/60 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center text-gold">
<i class="fa-solid fa-magnifying-glass-plus text-2xl"></i>
</div>
</div>
`).join('');
}
function openLightbox(url) {
document.getElementById('lightboxImg').src = url;
document.getElementById('lightboxModal').classList.remove('hidden');
}
function closeLightbox() {
document.getElementById('lightboxModal').classList.add('hidden');
}
// --- MURO DE DESEOS ---
function loadWishes() {
const grid = document.getElementById('wishesGrid');
const saved = JSON.parse(localStorage.getItem('nicole_xv_wishes') || '[]');
const defaults = [
{ id: 1, author: "Familia Gómez", text: "¡Querida Nicole! Deseamos que esta noche sea inolvidable y que todos tus sueños se cumplan." },
{ id: 2, author: "Sofi & Amigas", text: "¡Llegaron los 15! A romper la pista bailando toda la noche." }
];
const all = [...saved, ...defaults];
grid.innerHTML = all.map(w => `
<div class="bg-dark-800 p-5 rounded-2xl border border-gold/20 space-y-2">
<div class="flex justify-between items-center">
<h5 class="font-bold text-gold-light text-xs font-title">${w.author}</h5>
<i class="fa-solid fa-sparkles text-[10px] text-gold"></i>
</div>
<p class="text-beige/80 text-xs italic">"${w.text}"</p>
</div>
`).join('');
}
function submitWish(e) {
e.preventDefault();
const author = document.getElementById('wishAuthor').value.trim();
const text = document.getElementById('wishText').value.trim();
if (author && text) {
let saved = JSON.parse(localStorage.getItem('nicole_xv_wishes') || '[]');
saved.unshift({ id: Date.now(), author, text });
localStorage.setItem('nicole_xv_wishes', JSON.stringify(saved));
document.getElementById('wishAuthor').value = '';
document.getElementById('wishText').value = '';
loadWishes();
}
}
// --- FUNCIONES DEL MODO EDITOR / ADMIN ---
function requestAdminAccess() {
const cfg = getConfig();
const pin = prompt("Ingresa el PIN de Administrador (por defecto: 1234):");
if (pin === cfg.adminPin) {
openAdminModal();
} else if (pin !== null) {
alert("PIN incorrecto.");
}
}
function openAdminModal() {
const cfg = getConfig();
document.getElementById('editEventDateText').value = cfg.eventDateText;
document.getElementById('editEventTime').value = cfg.eventTime;
document.getElementById('editVenueName').value = cfg.venueName;
document.getElementById('editVenueAddress').value = cfg.venueAddress;
document.getElementById('editWhatsappPhone').value = cfg.whatsappPhone;
document.getElementById('editDressCode').value = cfg.dressCode;
document.getElementById('editBankAlias').value = cfg.bankAlias;
document.getElementById('editBankCbu').value = cfg.bankCbu;
document.getElementById('editTargetDate').value = cfg.targetDateISO;
renderAdminRSVPs();
renderAdminGallery();
renderAdminWishes();
document.getElementById('adminModal').classList.remove('hidden');
}
function closeAdminModal() {
document.getElementById('adminModal').classList.add('hidden');
}
function switchAdminTab(tabName) {
['event', 'rsvps', 'gallery', 'wishes'].forEach(t => {
document.getElementById(`adminTab-${t}`).classList.add('hidden');
document.getElementById(`tabBtn-${t}`).className = "px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-dark-700 text-beige hover:text-gold";
});
document.getElementById(`adminTab-${tabName}`).classList.remove('hidden');
document.getElementById(`tabBtn-${tabName}`).className = "px-4 py-2 rounded-xl text-xs font-title uppercase tracking-wider bg-gold text-dark-950 font-bold";
}
function saveEventSettings() {
const cfg = getConfig();
cfg.eventDateText = document.getElementById('editEventDateText').value;
cfg.eventTime = document.getElementById('editEventTime').value;
cfg.venueName = document.getElementById('editVenueName').value;
cfg.venueAddress = document.getElementById('editVenueAddress').value;
cfg.whatsappPhone = document.getElementById('editWhatsappPhone').value;
cfg.dressCode = document.getElementById('editDressCode').value;
cfg.bankAlias = document.getElementById('editBankAlias').value;
cfg.bankCbu = document.getElementById('editBankCbu').value;
cfg.targetDateISO = document.getElementById('editTargetDate').value;
saveConfig(cfg);
updateCountdown();
alert("¡Cambios del evento guardados correctamente!");
}
function renderAdminRSVPs() {
const rsvps = JSON.parse(localStorage.getItem('nicole_xv_rsvps') || '[]');
document.getElementById('rsvpCountBadge').innerText = rsvps.length;
const tbody = document.getElementById('adminRsvpTableBody');
if (rsvps.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" class="py-4 text-center text-beige/40">No hay respuestas registradas aún.</td></tr>';
return;
}
tbody.innerHTML = rsvps.map(r => `
<tr class="border-b border-dark-700">
<td class="py-2 font-bold text-gold-light">${r.name}</td>
<td>${r.attending}</td>
<td>${r.guests}</td>
<td>${r.diet} / ${r.message}</td>
<td>
<button onclick="deleteRSVP(${r.id})" class="text-rose-400 hover:text-rose-300">
<i class="fa-solid fa-trash text-xs"></i>
</button>
</td>
</tr>
`).join('');
}
function deleteRSVP(id) {
let rsvps = JSON.parse(localStorage.getItem('nicole_xv_rsvps') || '[]');
rsvps = rsvps.filter(r => r.id !== id);
localStorage.setItem('nicole_xv_rsvps', JSON.stringify(rsvps));
renderAdminRSVPs();
}
function clearAllRSVPs() {
if (confirm("¿Estás seguro de borrar todas las confirmaciones guardadas?")) {
localStorage.removeItem('nicole_xv_rsvps');
renderAdminRSVPs();
}
}
function exportRSVPsCSV() {
const rsvps = JSON.parse(localStorage.getItem('nicole_xv_rsvps') || '[]');
if (rsvps.length === 0) return alert("No hay datos para exportar");
let csv = "Nombre,Asistencia,Cupos,Dieta,Mensaje,Fecha\n";
rsvps.forEach(r => {
csv += `"${r.name}","${r.attending}","${r.guests}","${r.diet}","${r.message}","${r.date}"\n`;
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "Confirmados_Nicole_15.csv";
link.click();
}
function renderAdminGallery() {
const cfg = getConfig();
const container = document.getElementById('adminGalleryPreview');
container.innerHTML = cfg.photos.map((url, idx) => `
<div class="relative group aspect-square rounded-lg overflow-hidden border border-gold/30">
<img src="${url}" class="w-full h-full object-cover">
<button onclick="removeGalleryPhoto(${idx})" class="absolute top-1 right-1 bg-rose-900/80 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs hover:bg-rose-600">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
`).join('');
}
function addGalleryPhoto() {
const input = document.getElementById('newPhotoUrl');
const url = input.value.trim();
if (url) {
const cfg = getConfig();
cfg.photos.push(url);
saveConfig(cfg);
input.value = '';
renderAdminGallery();
}
}
function removeGalleryPhoto(idx) {
const cfg = getConfig();
cfg.photos.splice(idx, 1);
saveConfig(cfg);
renderAdminGallery();
}
function renderAdminWishes() {
const container = document.getElementById('adminWishesList');
const saved = JSON.parse(localStorage.getItem('nicole_xv_wishes') || '[]');
if (saved.length === 0) {
container.innerHTML = '<p class="text-xs text-beige/40 py-2 text-center">No hay mensajes publicados por usuarios aún.</p>';
return;
}
container.innerHTML = saved.map(w => `
<div class="flex items-center justify-between bg-dark-800 p-2.5 rounded-lg border border-dark-600 text-xs">
<div>
<span class="font-bold text-gold">${w.author}:</span>
<span class="text-beige/80 ml-1">"${w.text}"</span>
</div>
<button onclick="deleteWish(${w.id})" class="text-rose-400 hover:text-rose-300 ml-2">
<i class="fa-solid fa-trash"></i>
</button>
</div>
`).join('');
}
function deleteWish(id) {
let saved = JSON.parse(localStorage.getItem('nicole_xv_wishes') || '[]');
saved = saved.filter(w => w.id !== id);
localStorage.setItem('nicole_xv_wishes', JSON.stringify(saved));
renderAdminWishes();
loadWishes();
}
// --- INICIALIZACIÓN DE LA PÁGINA ---
window.onload = function() {
applyConfigUI();
updateCountdown();
loadWishes();
};
</script>
</body>
</html>
```57,582 chars · codepastes.com