/* global React, LeafSprig, LaurelHalf, Floret, PlaceholderTile */
const { useState: useStateS, useEffect: useEffectS, useRef: useRefS } = React;

// Resource resolver: in the standalone bundle, window.__resources.<id> holds an
// inlined blob URL; in the live/dev site it's undefined, so we fall back to the
// on-disk path. Single source of truth — no separate ".standalone" fork.
const RES = typeof window !== 'undefined' && window.__resources || {};
const img = (id, path) => RES[id] || path;

// Padlock glyph built from plain shapes (shackle arc + body) — no emoji.
function WallLock() {
  return (
    <span className="wall-lock" aria-hidden="true">
      <span className="wl-shackle" />
      <span className="wl-body" />
    </span>);

}

const WALL_URL = 'https://jhoncarloanddianejoy.site/upload.html';

// ===== HERO =====
function Hero({ data, wallStyle = 'pill', wallPreview = false }) {
  const [now, setNow] = useStateS(Date.now());
  useEffectS(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => {clearInterval(id);};
  }, []);
  const target = new Date('2026-06-28T14:00:00+08:00').getTime();
  const diff = Math.max(0, target - now);
  const days = Math.floor(diff / 86400000);
  const hours = Math.floor(diff % 86400000 / 3600000);
  const mins = Math.floor(diff % 3600000 / 60000);
  const secs = Math.floor(diff % 60000 / 1000);

  // The wall stays locked until the countdown reaches zero (same target).
  const locked = diff > 0 && !wallPreview;
  const remain =
  days > 0 ? `${days}d ${String(hours).padStart(2, '0')}h` :
  hours > 0 ? `${hours}h ${String(mins).padStart(2, '0')}m` :
  `${mins}m ${String(secs).padStart(2, '0')}s`;
  const wallCls = `hero-wall hw-${wallStyle} ${locked ? 'is-locked' : 'is-open'}`;
  const wallInner =
  <>
      <span className="hw-icon">
        {locked ? <WallLock /> : <span className="hw-live-dot" />}
      </span>
      <span className="hw-text">
        <span className="hw-title">Live Wedding Wall</span>
        <span className="hw-sub">
          {locked ? `Opens in ${remain}` : 'Share photos & well wishes'}
        </span>
      </span>
      <span className="hw-arrow" aria-hidden="true">→</span>
    </>;

  return (
    <section className="snap hero has-photo" id="home" data-screen-label="01 Hero">
      <div className="hero-photo" aria-hidden="true">
        <div className="hero-photo-img" />
        <div className="hero-photo-veil" />
        <div className="hero-photo-vignette" />
        <div className="hero-photo-grain" />
      </div>
      <div className="hero-photo-tag" aria-hidden="true">
        <span className="hpt-dot" />
        <span>Kidapawan · June 2026</span>
      </div>
      <div className="hero-photo-frame" aria-hidden="true">
        <span className="hpf tl" /><span className="hpf tr" />
        <span className="hpf bl" /><span className="hpf br" />
      </div>
      <div className="hero-inner">
      <div className="hero-eyebrow">together with their families</div>
      <h1 className="hero-name display">{data.bride}</h1>
      <span className="hero-amp italic-script">&amp;</span>
      <h1 className="hero-name display">{data.groom}</h1>
      <div className="hero-meta">
        <span>Sunday</span><span className="dot" />
        <span>June 28 · 2026</span><span className="dot" />
        <span>Kidapawan City</span>
      </div>
      <div className="countdown">
        {[['days', days], ['hours', hours], ['minutes', mins], ['seconds', secs]].map(([k, v]) =>
          <div key={k} className="cd-cell">
            <div className="cd-num">{String(v).padStart(2, '0')}</div>
            <div className="cd-label">{k}</div>
          </div>
          )}
      </div>
      {locked ?
      <div className={wallCls} role="note" aria-label={`Live wedding wall — opens in ${remain}`}>
          {wallInner}
        </div> :
      <a className={wallCls} href={WALL_URL} target="_blank" rel="noopener noreferrer">
          {wallInner}
        </a>}
      </div>
    </section>);

}

// ===== STORY =====
function Story() {
  return (
    <section className="snap has-silhouette" id="story" data-screen-label="02 Story">
      <div className="story-silhouette" aria-hidden="true">
        <img src={img('g08', 'images/g08.jpg')} alt="" loading="lazy" />
      </div>
      <div className="section-head">
        <span className="num">I.</span>
        <span className="label">Our Story</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">It began with a <em>simple message</em> on a Facebook story.</h2>
      <div className="story-grid">
        <div className="story-col">
          <p>Their story began with a simple Facebook My Day. She was singing, and somehow, that brief moment became the start of something beautiful. Drawn by her voice, he sent a simple message: “Hello, your voice is beautiful.” She replied with a warm “Thank you,” and from that small exchange, a meaningful connection quietly began.</p>
          <p>What started as casual conversations soon turned into daily chats filled with laughter, stories, and shared moments. Day by day, they found comfort in each other’s presence, looking forward to every message and every conversation. Without realizing it, they had slowly become an important part of each other’s lives.</p>
          <p>Then came June — the month that changed everything. With sincerity, hope, and a little nervousness, he finally asked if he could court her. Her answer was “Yes,” and from that moment, their journey together became even more special.</p>
          <p>As the months passed, their bond continued to grow through patience, understanding, and genuine effort. Every conversation, every laugh, and every shared experience strengthened the connection they were building together.</p>
        </div>
        <div className="story-col">
          <p>Their first meeting in person took place at M’lang Plaza. Beneath the bright lights and busy surroundings, the world seemed to quiet the moment their eyes met. There was an instant spark — a mix of excitement, nervousness, and happiness that made the moment unforgettable. Though both were shy, they felt an undeniable sense of comfort with one another.</p>
          <p>From that day on, their relationship only grew stronger. Through endless conversations, shared dreams, unwavering support, and genuine understanding, they built a love rooted in sincerity and trust.</p>
          <p>And on August 4, 2024, their story reached one of its most beautiful milestones — the day they officially became a couple. What began with a simple compliment on a Facebook story blossomed into a love story shaped by patience, faith, and two hearts that found each other at exactly the right time.</p>
        </div>
      </div>

      <div className="prenup">
        <div className="prenup-head">
          <span className="prenup-label">Watch · Our Prenup Film</span>
          <span className="rule" />
        </div>
        <div className="prenup-frame">
          <div className="prenup-video">
            <iframe
              src="https://www.facebook.com/plugins/video.php?height=720&href=https%3A%2F%2Fwww.facebook.com%2Freel%2F1559776312237758%2F&show_text=false&width=1280&t=0"
              title="Jhon Carlo & Diane Joy — Prenup Film"
              style={{ border: 'none', overflow: 'hidden' }}
              scrolling="no"
              frameBorder="0"
              allowFullScreen={true}
              allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"
            />
            <button
              type="button"
              className="prenup-fs"
              aria-label="Watch full screen"
              onClick={(e) => {
                const el = e.currentTarget.closest('.prenup-video');
                const req = el.requestFullscreen || el.webkitRequestFullscreen || el.msRequestFullscreen;
                if (req) {
                  Promise.resolve(req.call(el)).catch(() => {
                    window.open('https://www.facebook.com/reel/1559776312237758/', '_blank', 'noopener');
                  });
                } else {
                  window.open('https://www.facebook.com/reel/1559776312237758/', '_blank', 'noopener');
                }
              }}
            >
              <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M21 16v3a2 2 0 0 1-2 2h-3M3 16v3a2 2 0 0 0 2 2h3" />
              </svg>
              <span>Full screen</span>
            </button>
          </div>
        </div>
        <div className="prenup-actions">
          <a
            className="prenup-fb-link"
            href="https://www.facebook.com/reel/1559776312237758/"
            target="_blank"
            rel="noopener noreferrer"
          >
            Trouble viewing? Watch on Facebook
            <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M7 17 17 7M9 7h8v8" />
            </svg>
          </a>
        </div>
      </div>
    </section>);

}

// ===== CEREMONY (Order of Service) =====
function Ceremony() {
  const prelude = [
  ['Prelude', 'Musicians & violinist'],
  ['Emcee\u2019s Words', 'Ludinette Casio-Samijon'],
  ['Reading', 'Scripture passage'],
  ['Invocation', 'Pastor Elfred Regoniel'],
  ['Moments of Music', 'Clarissa Vinson Cadelon']];

  const processional = [
  ['Minister', 'Processional'],
  ['Best Man', 'Best Man March'],
  ['Groom', 'Song'],
  ['Parents of the Groom', ''],
  ['Principal Sponsors', ''],
  ['Bible, Pen & Covenant Bearers', ''],
  ['Groomsmen & Bridesmaids', ''],
  ['Flower Girls', ''],
  ['Maid of Honor', ''],
  ['Parents of the Bride', ''],
  ['Bridal Entrance', 'Bridal March']];

  const rite = [
  ['Parental Blessings & Giving Away of the Bride', ''],
  ['Groom Meets Bride', 'Music / Violin'],
  ['Special Song', 'Ludinette Casio-Samijon'],
  ['Nuptial Homily', 'Pastor Welnie Miado'],
  ['Exchange of Vows', ''],
  ['Dedicatory Prayer', 'Pastor Welnie Miado'],
  ['Dedicatory Song', 'Cherry Mae Ayre'],
  ['Signing of the Marriage Covenant', 'Song'],
  ['Lighting of the Unity Candle', 'The Couple'],
  ['Pronouncement & Official Legal Kiss', ''],
  ['Presentation of the Newlyweds', ''],
  ['Recessional', 'Song']];

  return (
    <section className="snap" id="ceremony" data-screen-label="04 Ceremony">
      <div className="section-head">
        <span className="num">III.</span>
        <span className="label">Order of Service</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">How the <em>ceremony</em> will unfold.</h2>

      <div className="cer-stack">
        <div className="cer-block cer-fw">
          <div className="cer-block-head">Prelude &amp; Welcome</div>
          <div className="cer-rows">
            {prelude.map(([l, v]) =>
            <div key={l} className="cer-row">
                <span className="cer-lab">{l}</span>
                <span className="cer-lead" />
                <span className="cer-val">{v}</span>
              </div>
            )}
          </div>
        </div>

        <div className="cer-block cer-fw">
          <div className="cer-block-head">Processional</div>
          <div className="cer-rows">
            {processional.map(([l, v], i) =>
            <div key={l} className={`cer-row cer-step ${v ? '' : 'solo'}`}>
                <span className="cer-step-n">{String(i + 1).padStart(2, '0')}</span>
                <span className="cer-lab">{l}</span>
                {v && <span className="cer-lead" />}
                {v && <span className="cer-val">{v}</span>}
              </div>
            )}
          </div>
        </div>

        <div className="cer-block cer-fw">
          <div className="cer-block-head">The Ceremony</div>
          <div className="cer-rows">
            {rite.map(([l, v], i) =>
            <div key={l} className={`cer-row cer-step ${v ? '' : 'solo'}`}>
                <span className="cer-step-n">{String(i + 1).padStart(2, '0')}</span>
                <span className="cer-lab">{l}</span>
                {v && <span className="cer-lead" />}
                {v && <span className="cer-val">{v}</span>}
              </div>
            )}
          </div>
        </div>
      </div>

      <div className="cer-verse">
        <p>&ldquo;Therefore what God has joined together, let no one separate.&rdquo;</p>
        <a className="cer-print" href="ceremony-program.html" target="_blank" rel="noopener">Open printable program &rarr;</a>
      </div>
    </section>);

}

// ===== SCHEDULE =====
function Schedule() {
  const events = [
  ['1:30 PM', 'Guest arrival & seating', 'Please arrive early and find your seat. The ceremony begins promptly at two.', 'SDA Central Church'],
  ['2:00 PM', 'The ceremony', 'Vows, prayers, and the exchange of rings. Phones on silent — we have a photographer.', 'SDA Central Church'],
  ['3:30 PM', 'Photos & travel to reception', 'Family portraits first, then a short drive to The Cascades. Transport is available for those who need it.', 'Church grounds'],
  ['4:30 PM', 'Reception & dinner', 'A long-table feast, toasts, and our first dance — seating arranged for comfort.', 'The Cascades by Amador'],
  ['5:30 PM', 'Programme & games', 'Speeches, traditions, and a little dancing into the early evening.', 'The Cascades by Amador'],
  ['7:00 PM', 'Send-off', 'Petals, well-wishes, and warm goodbyes as the celebration comes to a close.', 'The Cascades by Amador']];

  return (
    <section className="snap" id="schedule" data-screen-label="03 Schedule">
      <div className="section-head">
        <span className="num">II.</span>
        <span className="label">The Day</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">A schedule for <em>the longest, shortest</em> day of our lives.</h2>
      <div className="schedule-list" style={{ marginTop: 48 }}>
        {events.map(([t, title, desc, place]) =>
        <div key={t} className="event-row">
            <div className="event-time">{t}</div>
            <div>
              <div className="event-title">{title}</div>
              <div className="event-desc">{desc}</div>
            </div>
            <div className="event-place">{place}</div>
          </div>
        )}
      </div>
    </section>);

}

// ===== VENUE =====
function Venue() {
  return (
    <section className="snap" id="venue" data-screen-label="05 Venue">
      <div className="section-head">
        <span className="num">IV.</span>
        <span className="label">Venue &amp; Travel</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">Two stops, one <em>bright, lovely</em> day.</h2>
      <div className="venue-grid">
        <div className="venue-card">
          <div className="tagit">Ceremony · 2:00 PM</div>
          <h3>Kidapawan City Seventh-Day Adventist Central Church</h3>
          <div className="addr">Kidapawan City<br />Cotabato, Philippines</div>
          <div className="venue-map venue-photo">
            <img src={img('churchImg', 'images/church.jpg')} alt="Kidapawan City Seventh-Day Adventist Central Church" loading="lazy" />
          </div>
          <a className="venue-link" target="_blank" rel="noopener" href="https://maps.app.goo.gl/1gzS6k5Vk4ecbfFe7" style={{ fontSize: "12px" }}>Open in maps →</a>
        </div>
        <div className="venue-card">
          <div className="tagit">Reception · 4:30 PM</div>
          <h3>The Cascades by Amador</h3>
          <div className="addr">Amador, Kidapawan City<br />Cotabato, Philippines</div>
          <div className="venue-map venue-photo">
            <img src={img('cascadesImg', 'images/cascades.jpg')} alt="The Cascades by Amador" loading="lazy" />
          </div>
          <a className="venue-link" target="_blank" rel="noopener" href="https://maps.app.goo.gl/Rdy82K9JPyQatqPV8" style={{ fontSize: "12px" }}>Open in maps →</a>
        </div>
      </div>
    </section>);

}

// ===== DRESS CODE =====
function DressCode() {
  return (
    <section className="snap" id="attire" data-screen-label="06 Dress Code">
      <div className="section-head">
        <span className="num">V.</span>
        <span className="label">What to Wear</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">Dressed in <em>red wine,</em> for a church-and-garden day.</h2>
      <div className="dress-wrap">
        <div>
          <p>Our colour motif is <em>red wine</em> — a deep, romantic burgundy. We'd be delighted if you'd dress in step with it, choosing soft, complementary tones over anything too bright.</p>
          <p style={{ marginTop: 8 }}>The ceremony is indoors at the church and the reception spills out into the garden, so dress for comfort in both. Kindly avoid pure white and ivory — and steer clear of bright red, which competes with our wine.</p>
        </div>
        <div className="swatch-grid">
          <div className="swatch do" style={{ background: '#5e1a2e' }}>Red Wine</div>
          <div className="swatch do2" style={{ background: '#8c5a6b' }}>Dusty Rose</div>
          <div className="swatch do3" style={{ background: '#c9a86a', color: '#1a0808' }}>Champagne</div>
          <div className="swatch no" style={{ background: '#ffffff', color: '#5a3a3a' }}>White</div>
          <div className="swatch no" style={{ background: '#f1e6d4' }}>Ivory</div>
          <div className="swatch no" style={{ background: '#c8332e', color: '#f6efe1' }}>Bright Red</div>
        </div>
      </div>

      <div className="attire-inspo">
        <div className="attire-inspo-head">
          <span className="attire-inspo-label">A little inspiration</span>
          <span className="rule" />
        </div>
        <div className="attire-grid">
          <figure className="attire-card">
            <img src={img('attireGown1', 'images/attire-gown-1.jpg')} alt="Red wine ball gown" loading="lazy" />
            <figcaption>For her · a wine-toned gown</figcaption>
          </figure>
          <figure className="attire-card">
            <img src={img('attireGown2', 'images/attire-gown-2.jpg')} alt="Red wine evening gown" loading="lazy" />
            <figcaption>For her · soft, draped silhouettes</figcaption>
          </figure>
          <figure className="attire-card">
            <img src={img('attireSuit1', 'images/attire-suit-1.jpg')} alt="Dark suit with wine accents" loading="lazy" />
            <figcaption>For him · dark suit, wine accents</figcaption>
          </figure>
          <figure className="attire-card">
            <img src={img('attireSuit2', 'images/attire-suit-2.jpg')} alt="Black tie with wine bow tie" loading="lazy" />
            <figcaption>For him · black tie, wine touches</figcaption>
          </figure>
        </div>
      </div>
    </section>);

}

// ===== ENTOURAGE =====
function Entourage() {
  const ninangs = [
  'Jarlen Burata Quiñones', 'Melcah Manuel', 'Medie Jumawid', 'Yammy Rose Flores',
  'Julie Agustin', 'Jocelyn Marquez', 'Dalia Gabais', 'Beverly Fajardo',
  'Helen Nuol', 'Judith Fajardo', 'Hilane Parcon', 'Lorna Biale',
  'Edna Beltran', 'Marilou Jamelarin', 'Beverly Labaro', 'Maribeth Flores',
  'Julieta Bautista', 'Clarissa Candelon', 'Charlyn Ladredo', 'Riza Buzarang',
  'Margarita Daga-as', 'Tessie Rapista', 'Mary T. Ruiz', 'Erlinda T. Uyanggurin',
  'Ma. Evelyn Yuson', 'Cheche Quiñones', 'Diona Soriano', 'Charity Bocala',
  'Dolly Pangramuyen', 'Madilyn Rosalia', 'Marcelina Rapista',
  'Shiela Delos Reyes Dusaban', 'Leah Beglau'];

  const ninongs = [
  'Samuel Guillem', 'Pastor Elfred Regoniel', 'Radomir Reyes', 'Roel Alcazaren',
  'Abraham Pareño', 'Joevannie Soriano', 'Erwin Roy Quiñones', 'Bryle Paclibar',
  'Lorenz Christian Rosalia', 'Emmanuel Malasador', 'Nelson Nondong', 'Tetopher Gabais',
  'Sam Biale', 'Samuel Jumawid', 'Jhonnie Rosalia', 'Frank Doronila',
  'Remarc Provido', 'Rhollyn Sean Rosalia'];

  const bridesmaids = ['Orlyn Pearl Felipe', 'Christmarie Baligasa', 'Iena Byb Alcazaren', 'Andrea Kate Castañeda'];
  const groomsmen = ['Kieth Rubio', 'Lanz Liguid', 'Justine Mahinay', 'Gerald Gaborno'];
  const usherettes = ['Atilla Reign Alcazaren', 'Eralyn Enterone'];
  const flowerGirls = ['Raffaella Love Caniban', 'Athelia B. Ulama', 'Kyomi Jonella B. Nayona'];

  const railRef = useRefS(null);
  const titleRef = useRefS(null);
  const [active, setActive] = useStateS(0);
  const [count, setCount] = useStateS(0);
  const [isMobile, setIsMobile] = useStateS(false);

  useEffectS(() => {
    const mq = window.matchMedia('(max-width: 760px)');
    const apply = () => setIsMobile(mq.matches);
    apply();
    mq.addEventListener('change', apply);
    return () => mq.removeEventListener('change', apply);
  }, []);

  useEffectS(() => {
    const rail = railRef.current;
    if (!rail) return;
    setCount(rail.children.length);
    if (!isMobile) return;
    const onScroll = () => {
      const cards = Array.from(rail.children);
      const mid = rail.scrollLeft + rail.clientWidth / 2;
      let idx = 0,best = Infinity;
      cards.forEach((card, i) => {
        const c = card.offsetLeft + card.offsetWidth / 2;
        const d = Math.abs(c - mid);
        if (d < best) {best = d;idx = i;}
      });
      setActive(idx);
    };
    rail.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => rail.removeEventListener('scroll', onScroll);
  }, [isMobile]);

  useEffectS(() => {
    if (!isMobile) return;
    const el = titleRef.current;
    if (!el) return;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduce) {el.classList.add('reveal-in');return;}
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {e.target.classList.add('reveal-in');io.unobserve(e.target);}
      });
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [isMobile]);

  const jumpTo = (i) => {
    const rail = railRef.current;
    if (!rail) return;
    const card = rail.children[i];
    if (card) rail.scrollTo({ left: card.offsetLeft - 20, behavior: 'smooth' });
  };

  return (
    <section className="snap" id="entourage" data-screen-label="07 Entourage">
      <div className="section-head">
        <span className="num">VI.</span>
        <span className="label">The Entourage</span>
        <span className="rule" />
      </div>
      <h2 className="section-title ent-title" ref={titleRef}>The people <em>walking with us</em> down the aisle.</h2>

      <div className="ent-swipe-hint" aria-hidden="true">
        <span>Swipe to browse</span>
        <span className="ent-swipe-arrow">→</span>
      </div>

      <div className="entourage-program" ref={railRef}>
        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">Our Parents</div>
          <div className="ent-fam">
            <div className="ent-fam-col">
              <div className="ent-fam-label">Parents of the Bride</div>
              <div className="ent-fam-names"><div>Christopher T. Rapista</div><div>Evangeline B. Rapista</div></div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Father of the Groom</div>
              <div className="ent-fam-names"><div>Domingo Benitez Baligasa</div></div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Grandmother of the Groom</div>
              <div className="ent-fam-names"><div>Fe Torita Liguid</div></div>
            </div>
          </div>
        </div>

        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">Principal Sponsors</div>
          <div className="ent-sub">Ninangs &amp; Ninongs</div>
          <div className="spon-cols">
            <div className="spon-group">
              <div className="spon-head">Ninangs</div>
              <ul className="spon-names">{ninangs.map((n, i) => <li key={i}>{n}</li>)}</ul>
            </div>
            <div className="spon-group">
              <div className="spon-head">Ninongs</div>
              <ul className="spon-names">{ninongs.map((n, i) => <li key={i}>{n}</li>)}</ul>
            </div>
          </div>
        </div>

        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">Of Honour</div>
          <div className="ent-fam cols-2">
            <div className="ent-fam-col">
              <div className="ent-fam-label">Best Man</div>
              <div className="ent-fam-names"><div>Paul Bryan Lobrigo</div></div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Maid of Honor</div>
              <div className="ent-fam-names"><div>Rnetch Beam Biale</div></div>
            </div>
          </div>
        </div>

        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">The Wedding Party</div>
          <div className="ent-fam cols-2">
            <div className="ent-fam-col">
              <div className="ent-fam-label">Groomsmen</div>
              <div className="ent-fam-names">{groomsmen.map((n, i) => <div key={i}>{n}</div>)}</div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Bridesmaids</div>
              <div className="ent-fam-names">{bridesmaids.map((n, i) => <div key={i}>{n}</div>)}</div>
            </div>
          </div>
        </div>

        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">Flowers &amp; Ushers</div>
          <div className="ent-fam cols-2">
            <div className="ent-fam-col">
              <div className="ent-fam-label">Flower Girls</div>
              <div className="ent-fam-names">{flowerGirls.map((n, i) => <div key={i}>{n}</div>)}</div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Usherettes</div>
              <div className="ent-fam-names">{usherettes.map((n, i) => <div key={i}>{n}</div>)}</div>
            </div>
          </div>
        </div>

        <div className="ent-block ent-block-full">
          <div className="ent-role ent-role-lead">The Bearers</div>
          <div className="ent-fam">
            <div className="ent-fam-col">
              <div className="ent-fam-label">Bible Bearer</div>
              <div className="ent-fam-names"><div>Ezekiel Hunt Agustin</div></div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Covenant Bearer</div>
              <div className="ent-fam-names"><div>Kai Clamuel B. Nayona</div></div>
            </div>
            <div className="ent-fam-col">
              <div className="ent-fam-label">Pen Bearer</div>
              <div className="ent-fam-names"><div>Vincent B. Gaborno</div></div>
            </div>
          </div>
        </div>
      </div>

      <div className="ent-dots" aria-hidden="true">
        {Array.from({ length: count }).map((_, i) =>
        <button key={i} className={`ent-dot ${active === i ? 'on' : ''}`} onClick={() => jumpTo(i)} aria-label={`Go to group ${i + 1}`} />
        )}
      </div>
    </section>);

}

// ===== GALLERY + LIGHTBOX =====
function Gallery({ onOpen, photos }) {
  const gridRef = useRefS(null);
  const [isMobile, setIsMobile] = useStateS(false);
  const [active, setActive] = useStateS(0);

  useEffectS(() => {
    const mq = window.matchMedia('(max-width: 760px)');
    const apply = () => setIsMobile(mq.matches);
    apply();
    mq.addEventListener('change', apply);
    return () => mq.removeEventListener('change', apply);
  }, []);

  // Desktop: staggered entrance reveal + subtle parallax drift
  useEffectS(() => {
    const grid = gridRef.current;
    if (!grid || isMobile) return;
    const tiles = Array.from(grid.querySelectorAll('.g-tile'));
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    if (reduce) {
      tiles.forEach((t) => t.classList.add('in'));
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {e.target.classList.add('in');io.unobserve(e.target);}
      });
    }, { threshold: 0.12 });
    tiles.forEach((t) => io.observe(t));

    const scroller = grid.closest('.snap-scroll') || window;
    let raf = 0;
    const update = () => {
      raf = 0;
      const vh = window.innerHeight || 800;
      for (const t of tiles) {
        const img = t.querySelector('img');
        if (!img) continue;
        const r = t.getBoundingClientRect();
        const rel = (r.top + r.height / 2 - vh / 2) / vh;
        const shift = Math.max(-1, Math.min(1, rel)) * -14;
        img.style.setProperty('--py', shift.toFixed(1) + 'px');
      }
    };
    const onScroll = () => {if (!raf) raf = requestAnimationFrame(update);};
    scroller.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    update();
    return () => {
      io.disconnect();
      scroller.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, [photos, isMobile]);

  // Mobile: coverflow swipe carousel — active card scales up, neighbors dim
  useEffectS(() => {
    const rail = gridRef.current;
    if (!rail || !isMobile) return;
    const cards = Array.from(rail.children);
    cards.forEach((c) => c.classList.add('in')); // no entrance gating in carousel mode
    let raf = 0;
    const update = () => {
      raf = 0;
      const mid = rail.scrollLeft + rail.clientWidth / 2;
      let idx = 0,best = Infinity;
      cards.forEach((card, i) => {
        const c = card.offsetLeft + card.offsetWidth / 2;
        const d = c - mid;
        const ad = Math.abs(d);
        if (ad < best) {best = ad;idx = i;}
        const t = Math.max(-1, Math.min(1, d / rail.clientWidth));
        const scale = (1 - Math.min(Math.abs(t), 1) * 0.16).toFixed(3);
        const opacity = (1 - Math.min(Math.abs(t), 1) * 0.55).toFixed(3);
        card.style.setProperty('--cf-scale', scale);
        card.style.setProperty('--cf-op', opacity);
      });
      setActive(idx);
    };
    const onScroll = () => {if (!raf) raf = requestAnimationFrame(update);};
    rail.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    update();
    return () => {
      rail.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
      cards.forEach((c) => {c.style.removeProperty('--cf-scale');c.style.removeProperty('--cf-op');});
    };
  }, [photos, isMobile]);

  const jumpTo = (i) => {
    const rail = gridRef.current;
    if (!rail) return;
    const card = rail.children[i];
    if (card) rail.scrollTo({ left: card.offsetLeft - (rail.clientWidth - card.offsetWidth) / 2, behavior: 'smooth' });
  };

  return (
    <section className="snap" id="gallery" data-screen-label="08 Gallery">
      <div className="section-head">
        <span className="num">VII.</span>
        <span className="label">A Few Frames</span>
        <span className="rule" />
      </div>
      <h2 className="section-title">A few of our <em>favorite frames.</em></h2>

      <div className="gal-swipe-hint" aria-hidden="true">
        <span>Swipe to browse</span>
        <span className="gal-swipe-arrow">→</span>
      </div>

      <div className={`gallery-grid ${isMobile ? 'gallery-rail' : ''}`} ref={gridRef}>
        {photos.map((p, i) =>
        <div key={i} className={`g-tile ${p.cls} ${isMobile && active === i ? 'cf-active' : ''}`} style={{ '--d': `${i % 5 * 80}ms` }} onClick={() => onOpen(i)}>
            <img src={p.src} alt="" loading="lazy" />
            <span className="g-overlay" aria-hidden="true" />
            <span className="g-num" aria-hidden="true">{String(i + 1).padStart(2, '0')}</span>
          </div>
        )}
      </div>

      <div className="gal-dots" aria-hidden="true">
        {photos.map((_, i) =>
        <button key={i} className={`gal-dot ${active === i ? 'on' : ''}`} onClick={() => jumpTo(i)} aria-label={`Go to photo ${i + 1}`} />
        )}
      </div>
    </section>);

}

function Lightbox({ idx, onClose, total, onPrev, onNext, photos }) {
  useEffectS(() => {
    function onKey(e) {
      if (e.key === 'Escape') onClose();else
      if (e.key === 'ArrowLeft') onPrev();else
      if (e.key === 'ArrowRight') onNext();
    }
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose, onPrev, onNext]);
  if (idx === null) return null;
  const p = photos[idx];
  return (
    <div className="lightbox" onClick={onClose}>
      <div className="lb-inner" onClick={(e) => e.stopPropagation()}>
        <button className="lb-close" onClick={onClose}>Close [esc]</button>
        <button className="lb-nav prev" onClick={onPrev}>‹</button>
        <button className="lb-nav next" onClick={onNext}>›</button>
        <img className="lb-img" src={p.src} alt="" />
        <div className="lb-cap">{String(idx + 1).padStart(2, '0')} / {String(total).padStart(2, '0')}</div>
      </div>
    </div>);

}

window.Hero = Hero;
window.Story = Story;
window.Ceremony = Ceremony;
window.Schedule = Schedule;
window.Venue = Venue;
window.DressCode = DressCode;
window.Entourage = Entourage;
window.Gallery = Gallery;
window.Lightbox = Lightbox;