/* global React, ReactDOM, qrcode,
   FloralSprig, BouquetSprig, OliveBranch,
   CandleIcon, ChurchIcon, FlutesIcon, RingsIcon, CalendarIcon, DressIcon,
   InlineIcon, Squiggle, HeartLine, OrnamentDivider, RibbonFlourish, BigBouquet,
   useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakSelect */

const { useState, useEffect, useRef, useMemo, useCallback } = React;

/* ============ Reveal on scroll ============ */
function useReveal() {
  useEffect(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('is-in');
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    const observeAll = () => {
      document.querySelectorAll('.reveal:not(.is-in)').forEach((el) => io.observe(el));
    };
    observeAll();
    const mo = new MutationObserver(observeAll);
    mo.observe(document.body, { childList: true, subtree: true });
    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

/* ============ Guest detection ============ */
function useGuest() {
  return useMemo(() => {
    const params = new URLSearchParams(window.location.search);
    const code = (params.get('g') || '').trim().toLowerCase();
    const list = window.GUESTS || {};
    if (code && list[code]) return { code, ...list[code], known: true };
    return { code: '', ...window.DEFAULT_GUEST, known: false };
  }, []);
}

/* ============ Sticker — small polaroid-style photo placeholder ============ */
function Sticker({ id, label, size = 100, tilt = 'l', style, placeholder, src }) {
  return (
    <div className={`sticker tilt-${tilt}`} style={{ width: size, height: size, ...style }}>
      <span className="sticker-tape"></span>
      <div className="sticker-slot">
        <image-slot
          id={id}
          shape="circle"
          placeholder={placeholder || 'фото'}
          {...(src ? { src } : {})}
        ></image-slot>
      </div>
      {label && <div className="sticker-label">{label}</div>}
    </div>
  );
}

/* ============ NAV ============ */
function Nav({ onMusicToggle, musicPlaying, hasMusic, guest }) {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 40);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  const go = (id) => (e) => {
    e.preventDefault();
    const el = document.getElementById(id);
    if (el) window.scrollTo({ top: el.offsetTop - 60, behavior: 'smooth' });
  };
  return (
    <header className={`nav ${scrolled ? 'scrolled' : ''}`}>
      <a className="nav-mono" href="#top" onClick={go('top')}>ЯR</a>
      <nav className="nav-links">
        {guest.known && <a className="nav-link hide-sm" onClick={go('invite')}>Запрошення</a>}
        <a className="nav-link" onClick={go('schedule')}>Деталі</a>
        {guest.known && <a className="nav-link" onClick={go('rsvp')}>RSVP</a>}
        {hasMusic && (
          <button
            className={`nav-music ${musicPlaying ? 'playing' : ''}`}
            onClick={onMusicToggle}
            aria-label={musicPlaying ? 'Pause music' : 'Play music'}
          >
            <InlineIcon.music />
          </button>
        )}
      </nav>
    </header>
  );
}

/* ============ HERO ============ */
function Hero({ cfg }) {
  return (
    <section className="hero" id="top">
      <FloralSprig className="hero-sprig tl" />
      <FloralSprig className="hero-sprig tr" />
      <FloralSprig className="hero-sprig bl" />
      <FloralSprig className="hero-sprig br" />

      <div className="hero-stack">
        <div className="hero-eyebrow-row reveal">
          <span className="line"></span>
          <span className="star"></span>
          <span className="eyebrow">5 вересня · 2026 · Львів</span>
          <span className="star"></span>
          <span className="line"></span>
        </div>

        <div className="hero-name-block reveal" data-delay="1">
          <h1 className="hero-name">Roman</h1>
          <div className="hero-amp">&amp;</div>
          <h1 className="hero-name">Yana</h1>
        </div>

        <div className="hero-sub reveal" data-delay="3">
          <span className="hairline"></span>
          <span className="smallcaps">ми одружуємось</span>
          <span className="hairline"></span>
        </div>

        <div className="hero-photos reveal" data-delay="4">
          <div className="hero-photo hp-left">
            <span className="photo-tape tl"></span>
            <div className="photo-slot">
              <image-slot
                id="hero-roman"
                shape="rect"
                placeholder="малий Роман"
                {...(cfg.photos?.heroRoman ? { src: cfg.photos.heroRoman } : {})}
              ></image-slot>
            </div>
            
          </div>

          <div className="hero-photos-heart">♡</div>

          <div className="hero-photo hp-right">
            <span className="photo-tape tr"></span>
            <div className="photo-slot">
              <image-slot
                id="hero-yana"
                shape="rect"
                placeholder="мала Яна"
                {...(cfg.photos?.heroYana ? { src: cfg.photos.heroYana } : {})}
              ></image-slot>
            </div>
        
          </div>
        </div>
      </div>

      <a
        className="hero-scroll"
        href="#invite"
        onClick={(e) => {
          e.preventDefault();
          document.getElementById('invite').scrollIntoView({ behavior: 'smooth' });
        }}
        aria-label="Scroll"
      >
        <span className="label">нижче</span>
        <InlineIcon.arrow />
      </a>
    </section>
  );
}

/* ============ INVITE STRIP ============ */
function Invite({ cfg, guest }) {
  if (!guest.known) return null;
  const greeting = guest.name.trim().split(/\s+/).length >= 2 ? 'Дорогі' : 'Дорогий(а)';
  return (
    <section className="invite" id="invite">
      <BouquetSprig className="invite-sprig l" style={{ color: 'var(--accent)' }} />
      <BouquetSprig className="invite-sprig r" style={{ color: 'var(--accent)' }} />

      {/* scattered hand-drawn decorations */}
      <DiscoBall className="deco invite-deco-disco" style={{ color: 'var(--ink-2)' }} />
      <CandleTrio className="deco invite-deco-candles" style={{ color: 'var(--ink-2)' }} />
      <WineAndGlass className="deco invite-deco-wine" style={{ color: 'var(--ink-2)' }} />
      <Sparkle size={22} className="deco invite-deco-sp1" style={{ color: 'var(--taupe)' }} />
      <Sparkle size={16} className="deco invite-deco-sp2" style={{ color: 'var(--taupe)' }} />
      <Sparkle size={18} className="deco invite-deco-sp3" style={{ color: 'var(--taupe)' }} />

      {/* banquet table fills the empty lower half */}
      <TableScene className="invite-table" style={{ color: 'var(--ink-2)' }} />

      <div className="invite-inner">
       <p className="invite-copy reveal" data-delay="1">
          {greeting},
        </p>
        <div className="invite-greet reveal">
          <span className="accent">{guest.name}</span>,
        </div>
        <p className="invite-copy reveal" data-delay="1">
          запрошуємо Вас розділити з нами радість особливого дня —<br />
          дня нашого весілля.
        </p>
        <div className="invite-meta reveal" data-delay="2">
          <span>{cfg.weddingDateDisplay}</span>
          <span className="dot"></span>
          <span>{cfg.weddingDayLabel}</span>
          <span className="dot"></span>
          <span>Львів</span>
        </div>
      </div>

    </section>
  );
}

/* ============ SCHEDULE ============ */
function Schedule({ cfg }) {
  const steps = [
    {
      icon: <CalendarIcon size={56} />,
      time: cfg.weddingDateDisplay,
      label: 'Дата',
      where: cfg.weddingDayLabel,
    },
    {
      icon: <ChurchIcon size={56} />,
      time: cfg.ceremonyTime,
      label: 'Церемонія',
      where: cfg.ceremonyVenue,
      map: cfg.ceremonyMap,
    },
    {
      icon: <FlutesIcon size={56} />,
      time: cfg.receptionTime,
      label: 'Святкування',
      where: cfg.receptionVenue,
      map: cfg.receptionMap,
    },
  ];
  return (
    <section className="schedule" id="schedule">
      {/* hand-drawn decorations scattered around the section */}
      <HandCoupe className="deco schedule-deco-coupe" style={{ color: 'var(--ink-2)' }} />
      <ChampagnePop className="deco schedule-deco-pop" style={{ color: 'var(--ink-2)' }} />
      <Sparkle size={20} className="deco schedule-deco-sp1" style={{ color: 'var(--taupe)' }} />
      <Sparkle size={14} className="deco schedule-deco-sp2" style={{ color: 'var(--taupe)' }} />

      <div className="section-head">
        <div className="eyebrow reveal">наш день</div>
      </div>

      <div className="schedule-line">
        {steps.map((s, i) => (
          <div className="step reveal" data-delay={(i % 3) + 1} key={i}>
            <div className="step-icon">{s.icon}</div>
            <div className="step-time">{s.time}</div>
            <div className="step-label">{s.label}</div>
            <div className="step-where">{s.where}</div>
            {s.map && (
              <a className="step-map" href={s.map} target="_blank" rel="noreferrer">
                на мапі <InlineIcon.pin />
              </a>
            )}
          </div>
        ))}
      </div>
    </section>
  );
}

/* ============ Watercolor swatch — hand-painted palette blob ============ */
function WatercolorSwatch({ color, i, name }) {
  const f = `wc-disp-${i}`;
  const g = `wc-grad-${i}`;
  return (
    <div className="swatch" title={name}>
      <svg className="swatch-blob" viewBox="0 0 140 140" aria-hidden="true">
        <defs>
          <filter id={f} x="-30%" y="-30%" width="160%" height="160%">
            <feTurbulence type="fractalNoise" baseFrequency="0.014 0.017"
              numOctaves="5" seed={i * 13 + 7} result="n" />
            <feDisplacementMap in="SourceGraphic" in2="n" scale="18"
              xChannelSelector="R" yChannelSelector="G" />
          </filter>
          <radialGradient id={g} cx="46%" cy="44%" r="58%">
            <stop offset="0%"  stopColor={color} stopOpacity="0.95" />
            <stop offset="58%" stopColor={color} stopOpacity="0.88" />
            <stop offset="82%" stopColor={color} stopOpacity="0.6" />
            <stop offset="100%" stopColor={color} stopOpacity="0.12" />
          </radialGradient>
        </defs>
        <circle cx="70" cy="71" r="51" fill={color} opacity="0.13" filter={`url(#${f})`} />
        <circle cx="70" cy="69" r="47" fill={`url(#${g})`} filter={`url(#${f})`} />
        <circle cx="65" cy="74" r="34" fill={color} opacity="0.34" filter={`url(#${f})`} />
        <circle cx="74" cy="64" r="18" fill={color} opacity="0.26" filter={`url(#${f})`} />
      </svg>
    </div>
  );
}

/* ============ DRESS CODE ============ */
function DressCode({ cfg }) {
  const palette = cfg.dressCode || [];
  if (!palette.length) return null;
  return (
    <section className="dress">
      <div className="dress-inner">
        <div className="dress-eyebrow reveal">дрес-код</div>

        <DancingFigures className="dress-figures reveal" data-delay="1" />

        <div className="dress-swatches reveal" data-delay="2">
          {palette.map((p, i) => (
            <WatercolorSwatch key={i} color={p.color} name={p.name} i={i} />
          ))}
        </div>
        <p className="dress-note reveal" data-delay="3">
        Для нас найголовніше — <em>ваша присутність</em>, але ми будемо раді,<br/>якщо ви захочете дотриматися кольорової палітри нашого весілля.
        </p>

      </div>
    </section>
  );
}

/* ============ COUNTDOWN (inline, used inside Closing) ============ */
function CountdownBlock({ cfg }) {
  const target = useMemo(() => new Date(cfg.weddingDate).getTime(), [cfg.weddingDate]);
  const [now, setNow] = useState(Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, []);
  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);
  const cells = [
    { n: days,  l: 'днів' },
    { n: hours, l: 'годин' },
    { n: mins,  l: 'хвилин' },
    { n: secs,  l: 'секунд' },
  ];
  return (
    <div className="countdown" id="countdown">
      <h2 className="cd-script reveal">до зустрічі</h2>
      <div className="cd-sub reveal" data-delay="1">До нашого святкування залишилось</div>
      <div className="cd-grid reveal" data-delay="2">
        {cells.map((c) => (
          <div className="cd-cell" key={c.l}>
            <div className="cd-num">{String(c.n).padStart(2, '0')}</div>
            <div className="cd-label">{c.l}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ============ QR CODE ============ */
function QRCode({ data, size = 220 }) {
  const canvasRef = useRef(null);
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !data) return;
    const qr = qrcode(0, 'M');
    qr.addData(data);
    qr.make();
    const moduleCount = qr.getModuleCount();
    const cellSize = size / moduleCount;
    const ctx = canvas.getContext('2d');
    canvas.width = size;
    canvas.height = size;
    const paper = getComputedStyle(document.documentElement).getPropertyValue('--paper').trim() || '#fbf6ea';
    const ink   = getComputedStyle(document.documentElement).getPropertyValue('--ink').trim()   || '#3a342a';
    ctx.fillStyle = paper;
    ctx.fillRect(0, 0, size, size);
    ctx.fillStyle = ink;
    for (let r = 0; r < moduleCount; r++) {
      for (let c = 0; c < moduleCount; c++) {
        if (qr.isDark(r, c)) ctx.fillRect(c * cellSize, r * cellSize, cellSize + 0.5, cellSize + 0.5);
      }
    }
  }, [data, size]);
  return <canvas ref={canvasRef} width={size} height={size} style={{ width: size, height: size, display: 'block' }} />;
}

/* ============ DONATION ============ */
function Donation({ cfg }) {
  const d = cfg.donation;
  if (!d || !d.url) return null;
  return (
    <div className="donation" id="donation">
      {/*<BigBouquet className="donation-bouquet" style={{ color: 'var(--accent)' }} />*/}

      {/* hand-drawn vase bouquet + sparkles in the corner */}
      <VaseBouquet className="deco donation-deco-vase" style={{ color: 'var(--ink-2)' }} />
      <Sparkle size={18} className="deco donation-deco-sp1" style={{ color: 'var(--taupe)' }} />

      <div className="donation-grid">
        <div className="donation-text reveal">
          <div className="donation-eyebrow">замість квітів</div>
          <h2 className="donation-title">
            <span className="script">донат</span><br />
            на підтримку армії
          </h2>
          <p className="donation-copy">
            Ваша присутність на нашому весіллі — найкращий подарунок для нас.
            А якщо ви бажаєте привітати нас ще й символічно, будемо вдячні, 
            якщо замість квітів ви зробите донат на підтримку наших захисників.
          </p>
        </div>
        <div className="donation-qr-wrap reveal" data-delay="1">
          <div className="qr-card">
            <QRCode data={d.url} size={220} />
            <div className="qr-caption">скануйте код<br />monobank</div>
            <a className="donation-link" href={d.url} target="_blank" rel="noreferrer">
              {d.displayUrl}
            </a>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============ RSVP ============ */
function RSVP({ cfg, guest }) {
  const maxGuests = guest.maxGuests || 2;
  // Default value shown in the counter. Falls back to maxGuests if not set
  // in guests.js, and is clamped to the valid 1..maxGuests range.
  const defaultGuests = Math.min(
    maxGuests,
    Math.max(1, guest.defaultGuests ?? maxGuests)
  );
  const [step, setStep] = useState('choice');
  const [name, setName] = useState(guest.known ? guest.name : '');
  const [attending, setAttending] = useState(null);
  const [guests, setGuests] = useState(defaultGuests);
  const [message, setMessage] = useState('');
  const [error, setError] = useState('');

  if (!guest.known) return null;

  const submit = useCallback(async (data) => {
    setError('');
    setStep('submitting');
    const payload = {
      code: guest.code || '',
      name: data.name || name,
      attending: data.attending,
      guests: data.guests || 0,
      message: data.message || '',
      submittedAt: new Date().toISOString(),
    };
    if (!cfg.rsvpEndpoint) {
      console.warn('[RSVP] No endpoint. Payload:', payload);
      setTimeout(() => setStep(data.attending ? 'success' : 'declined'), 700);
      return;
    }
    try {
      await fetch(cfg.rsvpEndpoint, {
        method: 'POST', mode: 'no-cors',
        headers: { 'Content-Type': 'text/plain;charset=utf-8' },
        body: JSON.stringify(payload),
      });
      setStep(data.attending ? 'success' : 'declined');
    } catch (err) {
      console.error(err);
      setError('Не вдалося надіслати відповідь. Спробуйте ще раз.');
      setStep('choice');
    }
  }, [cfg.rsvpEndpoint, guest.code, name]);

  const onYes = () => { setAttending(true);  setStep('form'); };
  const onNo  = () => { setAttending(false); setStep('form'); };

  return (
    <section className="rsvp" id="rsvp">
      {/* hand-drawn decorations scattered on sides */}
      <CandleTrio className="deco rsvp-deco-candles" style={{ color: 'var(--ink-2)' }} />
      <DiscoBall className="deco rsvp-deco-disco" style={{ color: 'var(--ink-2)' }} />
      <Sparkle size={20} className="deco rsvp-deco-sp1" style={{ color: 'var(--taupe)' }} />
      <Sparkle size={14} className="deco rsvp-deco-sp2" style={{ color: 'var(--taupe)' }} />


      <div className="reveal" style={{ display: 'flex', justifyContent: 'center', marginBottom: 20 }}>
        <OrnamentDivider width={220} color="var(--taupe)" />
      </div>
      {guest.known && (
       <div className="invite-greet reveal">
          <span className="accent">{guest.name}</span>,
        </div>
      )}

      <p className="rsvp-greeting reveal">просимо</p>

      <p className="rsvp-greeting reveal" style={{ marginTop: 8 }}>підтвердіть свою присутність</p>

      <div className="rsvp-deadline reveal" data-delay="1">
        до {cfg.rsvpDeadline}
      </div>

    

      {step === 'choice' && (
        <div className="rsvp-choices reveal" data-delay="3">
          <button className="rsvp-btn rsvp-btn--yes" onClick={onYes}>
            <span className="check"><InlineIcon.check /></span>
            Підтверджую присутність
          </button>
          <button className="rsvp-btn" onClick={onNo}>
            <span className="check"><InlineIcon.x /></span>
            Нажаль, не зможу
          </button>
        </div>
      )}

      {step === 'form' && (
        <div className="rsvp-card">
          <div className="step-dots">
            <div className="step-dot is-done"></div>
            <div className="step-dot is-active"></div>
            <div className="step-dot"></div>
          </div>
          <form
            className="rsvp-form"
            onSubmit={(e) => {
              e.preventDefault();
              submit({ name, attending, guests: attending ? guests : 0, message });
            }}
          >
            <div className="form-row">
              <label className="form-label">Ваше імʼя</label>
              <input className="form-input" value={name} onChange={(e) => setName(e.target.value)} required autoFocus />
            </div>
            {attending && maxGuests > 1 && (
              <div className="form-row">
                <label className="form-label">Кількість гостей</label>
                <div className="guest-counter">
                  <button type="button" className="counter-btn"
                          onClick={() => setGuests((g) => Math.max(1, g - 1))}
                          disabled={guests <= 1} aria-label="зменшити">−</button>
                  <div className="counter-num">{guests}</div>
                  <button type="button" className="counter-btn"
                          onClick={() => setGuests((g) => Math.min(maxGuests, g + 1))}
                          disabled={guests >= maxGuests} aria-label="збільшити">+</button>
                </div>
              </div>
            )}
            <div className="form-row">
              <label className="form-label">
                {attending ? 'Побажання або алергії' : 'Декілька слів молодятам'}
              </label>
              <input className="form-input" value={message} onChange={(e) => setMessage(e.target.value)}
                     placeholder={attending ? '' : ''} />
            </div>
            <div className="form-actions">
              <button type="button" className="btn-secondary" onClick={() => setStep('choice')}>назад</button>
              <button type="submit" className="btn-primary" disabled={!name.trim()}>надіслати</button>
            </div>
            {error && <div className="rsvp-error">{error}</div>}
          </form>
        </div>
      )}

      {step === 'submitting' && (
        <div className="rsvp-card">
          <div className="rsvp-success">
            <div className="seal" style={{ animation: 'spin 1.2s linear infinite' }}>
              <OrnamentDivider width={40} color="var(--taupe)" />
            </div>
            <h3>Надсилаємо…</h3>
          </div>
        </div>
      )}

      {step === 'success' && (
        <div className="rsvp-card">
          <div className="rsvp-success">
            <div className="seal"><HeartLine size={26} /></div>
            <h3>Дякуємо!</h3>
            <p>
              Ваша відповідь отримана.<br />
              Будемо щасливі бачити Вас на нашому святі.
            </p>
          </div>
        </div>
      )}

      {step === 'declined' && (
        <div className="rsvp-card">
          <div className="rsvp-success">
            <div className="seal"><InlineIcon.x /></div>
            <h3>Будемо сумувати</h3>
            <p>Дякуємо, що дали нам знати.</p>
          </div>
        </div>
      )}
    </section>
  );
}

/* ============ CLOSING — Countdown + Donation + Footer combined ============ */
function Closing({ cfg }) {
  const people = useMemo(() => {
    const acc = {};
    (cfg.socials || []).forEach((s) => { (acc[s.label] = acc[s.label] || []).push(s); });
    return Object.entries(acc);
  }, [cfg.socials]);

  return (
    <section className="closing">
      <Donation cfg={cfg} />
      <CountdownBlock cfg={cfg} />
      <footer className="footer">
        <div className="footer-inner">
          <div className="footer-socials-wrap">
            <div className="footer-socials-label">залишайтесь на зв'язку</div>
            <div className="footer-people">
              {people.map(([name, links]) => (
                <div key={name} className="footer-person">
                  <div className="footer-person-name">{name}</div>
                  <div className="footer-socials">
                    {links.map((s, i) => (
                      <a key={i} className="footer-social" href={s.url} target="_blank" rel="noreferrer" aria-label={`${s.type} · ${name}`}>
                        {s.type === 'instagram' ? <InlineIcon.insta /> : <InlineIcon.telegram />}
                      </a>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </div>
        </div>
      </footer>
    </section>
  );
}

/* ============ TWEAKS — palettes (light pastel + olive) ============ */
const PALETTES = {
  'Olive & Ivory': {
    bg: '#F5F2EB', bg2: '#ECE2D0', paper: '#FDFAF5', paper2: '#F5F2EB',
    ink: '#2e2a22', ink2: '#5c5446', ink3: '#9a9080',
    line: '#d8ceba', line2: '#c8b89e', taupe: '#A3B19B', deep: '#5a6652',
    accent: '#A3B19B', accentDeep: '#7a8f72',
  },
  'Soft Sage': {
    bg: '#eef0e2', bg2: '#e6e9d6', paper: '#f6f8eb', paper2: '#eef1de',
    ink: '#2e3326', ink2: '#586250', ink3: '#8a957f',
    line: '#c5cdaf', line2: '#abb495', taupe: '#84957a', deep: '#4b5a40',
    accent: '#7a8c64', accentDeep: '#566840',
  },
  'Linen & Wheat': {
    bg: '#f6f1e3', bg2: '#ede4ce', paper: '#fcf7eb', paper2: '#f4ecd4',
    ink: '#3b3024', ink2: '#6f5e4a', ink3: '#a2917a',
    line: '#dbc9ac', line2: '#c5b08e', taupe: '#a08568', deep: '#5b432e',
    accent: '#a89c78', accentDeep: '#807454',
  },
};

function applyPalette(name) {
  const p = PALETTES[name] || PALETTES['Olive & Ivory'];
  const r = document.documentElement;
  r.style.setProperty('--bg', p.bg);
  r.style.setProperty('--bg-2', p.bg2);
  r.style.setProperty('--paper', p.paper);
  r.style.setProperty('--paper-2', p.paper2);
  r.style.setProperty('--ink', p.ink);
  r.style.setProperty('--ink-2', p.ink2);
  r.style.setProperty('--ink-3', p.ink3);
  r.style.setProperty('--line', p.line);
  r.style.setProperty('--line-2', p.line2);
  r.style.setProperty('--taupe', p.taupe);
  r.style.setProperty('--deep', p.deep);
  r.style.setProperty('--accent', p.accent);
  r.style.setProperty('--accent-deep', p.accentDeep);
}

/* ============ APP ============ */
function App() {
  useReveal();
  const cfg = window.CONFIG;
  const guest = useGuest();

  const [t, setTweak] = useTweaks({
    palette: 'Olive & Ivory',
  });

  useEffect(() => { applyPalette(t.palette); }, [t.palette]);

  // Music
  const audioRef = useRef(null);
  const [musicPlaying, setMusicPlaying] = useState(false);
  const musicStartedRef = useRef(false);
  const hasMusic = !!cfg.musicSrc;

  const startMusic = useCallback(() => {
    const a = audioRef.current;
    if (!a || musicStartedRef.current) return;
    musicStartedRef.current = true;
    a.load();
    a.play().then(() => setMusicPlaying(true)).catch(() => { musicStartedRef.current = false; });
  }, []);

  useEffect(() => {
    if (!hasMusic) return;
    const events = ['scroll', 'click', 'touchstart', 'keydown'];
    const handler = () => {
      startMusic();
      events.forEach((e) => window.removeEventListener(e, handler));
    };
    events.forEach((e) => window.addEventListener(e, handler, { once: true, passive: true }));
    return () => events.forEach((e) => window.removeEventListener(e, handler));
  }, [hasMusic, startMusic]);

  const toggleMusic = () => {
    const a = audioRef.current; if (!a) return;
    if (a.paused) {
      a.load();
      a.play().then(() => setMusicPlaying(true)).catch(() => {});
    } else { a.pause(); setMusicPlaying(false); }
  };

  return (
    <>
      <Nav onMusicToggle={toggleMusic} musicPlaying={musicPlaying} hasMusic={hasMusic} guest={guest} />
      <main>
        <Hero cfg={cfg} />
        <Invite cfg={cfg} guest={guest} />
        <Schedule cfg={cfg} />
        <DressCode cfg={cfg} />
        <RSVP cfg={cfg} guest={guest} />
      </main>
      <Closing cfg={cfg} />

      {hasMusic && <audio ref={audioRef} src={cfg.musicSrc} loop preload="auto" />}

      <TweaksPanel title="Tweaks">
        <TweakSection title="Палітра">
          <TweakSelect
            label="Палітра"
            value={t.palette}
            options={Object.keys(PALETTES)}
            onChange={(v) => setTweak('palette', v)}
          />
        </TweakSection>
      </TweaksPanel>
    </>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
