/* global React */
const { useState, useEffect, useRef } = React;

// ============================================================
// NAV
// ============================================================
function Nav({ route, navigate, onPitch }) {
  const [scrolled, setScrolled] = useState(false);
  const [menuOpen, setMenuOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 12);
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  useEffect(() => { document.body.classList.toggle('menu-locked', menuOpen); return () => document.body.classList.remove('menu-locked'); }, [menuOpen]);
  const go = (id) => { setMenuOpen(false); navigate(id); };
  return (
    <nav className={'re-nav' + (scrolled ? ' scrolled' : '')}>
      <div className="re-nav-inner">
        <a className="re-nav-brand" onClick={() => navigate('home')}>
          <img src="assets/logo-square.svg" alt="" />
          <div className="re-nav-brand-words">
            <span className="name">Red Empire</span>
            <span className="sub">Productions · EST. 2017</span>
          </div>
        </a>
        <div className="re-nav-links">
          {window.RE_NAV.map(n => (
            n.children ? (
              <div key={n.id} className="re-nav-drop">
                <a className={'re-nav-link' + (n.children.some(c => route.page === c.id) ? ' active' : '')}>
                  {n.label} <span className="re-nav-caret">▾</span>
                </a>
                <div className="re-nav-drop-menu">
                  {n.children.map(c => (
                    <a key={c.id + c.label}
                       className={'re-nav-drop-item' + (route.page === c.id ? ' active' : '')}
                       onClick={() => navigate(c.id)}>{c.label}</a>
                  ))}
                </div>
              </div>
            ) : (
              <a key={n.id}
                 className={'re-nav-link' + (route.page === n.id ? ' active' : '')}
                 onClick={() => navigate(n.id)}>{n.label}</a>
            )
          ))}
        </div>
        <div className="re-nav-right">
          <div className="re-nav-status">
            <span className="re-nav-dot"></span>
            <span>Slate 2026 · Active</span>
          </div>
          <button className="btn btn-blood btn-sm" onClick={() => navigate('contact')}>Contact</button>
          <button className={'re-nav-burger' + (menuOpen ? ' open' : '')} aria-label="Menu" onClick={() => setMenuOpen(o => !o)}>
            <span></span><span></span><span></span>
          </button>
        </div>
      </div>
      {menuOpen && (
        <div className="re-mobile-menu">
          {window.RE_NAV.map(n => (
            n.children ? (
              <div key={n.id} className="re-mobile-group">
                <span className="re-mobile-group-label">{n.label}</span>
                {n.children.map(c => (
                  <a key={c.id + c.label} className={'re-mobile-link sub' + (route.page === c.id ? ' active' : '')} onClick={() => go(c.id)}>{c.label}</a>
                ))}
              </div>
            ) : (
              <a key={n.id} className={'re-mobile-link' + (route.page === n.id ? ' active' : '')} onClick={() => go(n.id)}>{n.label}</a>
            )
          ))}
          <a className="re-mobile-link" onClick={() => go('contact')}>Contact</a>
        </div>
      )}
    </nav>
  );
}

// ============================================================
// HERO — cinematic crossfading sizzle-reel
// ============================================================
function Hero({ projects, navigate, onPitch, motionLevel = 'on', heroVariant = 'reel' }) {
  const [idx, setIdx] = useState(0);
  const [tc, setTc] = useState(0); // fake timecode
  const dur = motionLevel === 'off' ? 99999 : 6200;
  useEffect(() => {
    if (motionLevel === 'off') return;
    const id = setInterval(() => setIdx(i => (i + 1) % projects.length), dur);
    return () => clearInterval(id);
  }, [projects.length, dur, motionLevel]);
  useEffect(() => {
    const id = setInterval(() => setTc(t => t + 1), 1000);
    return () => clearInterval(id);
  }, []);
  const current = projects[idx];
  const tcMin = String(Math.floor(tc / 60) % 60).padStart(2, '0');
  const tcSec = String(tc % 60).padStart(2, '0');

  return (
    <section className="re-hero grain">
      <div className="re-hero-letterbox-top"></div>
      <div className="re-hero-letterbox-bot"></div>

      {/* Base layer — looping showreel video */}
      <video
        className="re-hero-video"
        src="assets/homepage-vid-banner.webm"
        autoPlay
        loop
        muted
        playsInline
        preload="auto"
      ></video>
      <div className="re-hero-video-vignette"></div>

      {/* Per-project colour wash on top of the video */}
      {projects.map((p, i) => (
        <div key={p.id} className={'re-hero-card' + (i === idx ? ' active' : '')}>
          <div className="re-hero-card-wash" style={{
            background: `radial-gradient(ellipse 70% 90% at 30% 60%, ${p.palette[2]}30 0%, transparent 60%), linear-gradient(140deg, ${p.palette[0]}55 0%, ${p.palette[1]}22 80%)`
          }}></div>
        </div>
      ))}

      <div className="re-hero-hud">
        <div className="h-row">
          <span><span className="h-dot"></span>REC • 4K • 24fps</span>
          <span>SLATE 2026.{String(idx + 1).padStart(2, '0')}</span>
        </div>
        <div className="h-row">
          <span>TC {tcMin}:{tcSec}:00</span>
          <span>BNE / GC / AU</span>
        </div>
      </div>

      <div className="re-hero-content">
        <div className="re-hero-textcol">
          <span className="re-hero-eyebrow">
            <span className="live-dot"></span>
            Now featured — {String(idx + 1).padStart(2, '0')} / {String(projects.length).padStart(2, '0')}
          </span>
          <h1 className="re-hero-title" key={current.title}>{current.title}</h1>
          <p className="re-hero-sub">{current.tagline}</p>
          <div className="re-hero-meta">
            <span>{current.year}</span><span className="sep">/</span>
            <span>{current.format}</span><span className="sep">/</span>
            <span>{current.runtime}</span><span className="sep">/</span>
            <span>{current.status}</span>
          </div>
          <div className="re-hero-cta-row">
            <button className="btn btn-blood" onClick={() => navigate('project', { id: current.id })}>
              Enter {current.title} →
            </button>
            <button className="btn btn-ghost" onClick={() => navigate('slate')}>View full slate</button>
          </div>
        </div>
        <div className="re-hero-projectcol">
          <span className="re-hero-num">REEL · 2026</span>
          <div className="re-hero-bars">
            {projects.map((p, i) => (
              <div key={p.id} className={'re-hero-bar' + (i === idx ? ' active' : '')}
                   onClick={() => setIdx(i)}></div>
            ))}
          </div>
          <span style={{ marginTop: 8, color: 'var(--bone)' }}>{current.title}</span>
        </div>
      </div>

    </section>
  );
}

// ============================================================
// MARQUEE STRIP
// ============================================================
function MarqueeStrip() {
  const items = ['INTERFACE', 'WTFitness', 'TOSKA', 'IN DEVELOPMENT', 'IN DEVELOPMENT', 'OPTIONED', 'IN DEVELOPMENT'];
  const projects = ['INTERFACE', 'The Blood Countess', 'Those Little White Lines', 'Home Away Al.i.Ce', 'Curse of the Seventh Christmas'];
  return (
    <div className="marquee-strip">
      <div className="marquee-track">
        {Array(4).fill(0).flatMap((_, k) => projects.map((p, i) => (
          <React.Fragment key={k + '-' + i}>
            <span>{p}</span>
            <span className="dot">◆</span>
          </React.Fragment>
        )))}
      </div>
    </div>
  );
}

// ============================================================
// SLATE LIST
// ============================================================
function SlateList({ projects, navigate, compact = false }) {
  const [filter, setFilter] = useState(compact ? 'all' : null);
  const filters = [
    { id: 'all', label: 'All' },
    { id: 'Feature', label: 'Features' },
    { id: 'Series', label: 'Series' },
    { id: 'Podcast', label: 'Podcasts' },
    { id: 'Short Film', label: 'Short Films' },
    { id: 'Book', label: 'Books' }
  ];
  const shown = projects.filter(p => {
    if (filter === 'all') return true;
    if (filter === 'In Development' || filter === 'Optioned') return p.status === filter;
    return p.tags.includes(filter) || p.format === filter;
  }).slice().sort((a, b) => {
    if (compact) return 0;
    const last = ['toska'];
    const aL = last.includes(a.id), bL = last.includes(b.id);
    if (aL !== bL) return aL ? 1 : -1;
    if (filter === 'all') {
      const priority = ['little-white-lines', 'interface', 'blood-countess-series', 'home-away-alice', 'blood-countess', 'seventh-christmas', 'baba-yaga', 'wtfitness', 'requiem-broadcast', 'seance-society', 'sirens-beacon', 'creators-champion'];
      const ai = priority.indexOf(a.id), bi = priority.indexOf(b.id);
      const ap = ai === -1 ? priority.length : ai;
      const bp = bi === -1 ? priority.length : bi;
      if (ap !== bp) return ap - bp;
    }
    return (b.year || 0) - (a.year || 0);
  });

  return (
    <>
      {!compact && (
        <div className="slate-filters">
          {filters.map(f => (
            <button key={f.id}
                    className={'slate-filter' + (filter === f.id ? ' active' : '')}
                    onClick={() => setFilter(f.id)}>{f.label}</button>
          ))}
        </div>
      )}
      {!compact && filter === 'Podcast' && (
        <div className="podcast-feature">
          <div className="podcast-feature-text">
            <span className="podcast-feature-eyebrow">● Red Empire Originals</span>
            <h3 className="podcast-feature-title">{window.RE_PODCAST.tagline}</h3>
            <p className="podcast-feature-desc">{window.RE_PODCAST.desc}</p>
          </div>
          <div className="podcast-feature-cta">
            <SpotifyButton url={window.RE_PODCAST.spotifyUrl} label="Listen on Spotify" />
          </div>
        </div>
      )}
      <div className="slate-list">
        {filter === null ? null : shown.map((p, i) => (
          <div key={p.id} className="slate-row" onClick={() => navigate('project', { id: p.id })}>
            <span className="slate-num">{String(i + 1).padStart(2, '0')}</span>
            <div>
              <div className="slate-title">{p.title}</div>
              <div className="slate-meta" style={{ marginTop: 8, flexDirection: 'row' }}>
                {p.tags.slice(0, 3).map((t, j) => (
                  <span key={j} style={j === 0 ? { color: 'var(--blood)' } : null}>{t}{j < Math.min(p.tags.length - 1, 2) ? ' · ' : ''}</span>
                ))}
              </div>
            </div>
            <div className="slate-tagline">{p.tagline}</div>
            <div className="slate-meta">
              <span>{p.year}</span>
              <span>{p.runtime}</span>
            </div>
            <span className={'slate-status ' + p.statusClass}>{p.status}</span>
            <span className="slate-arrow">→</span>
          </div>
        ))}
        {!compact && filter === null && (
          <div className="slate-empty-hint">Select a category above to view the slate.</div>
        )}
      </div>
    </>
  );
}

// ============================================================
// AI SECTION
// ============================================================
function AISection({ navigate, compact }) {
  const [active, setActive] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setActive(a => (a + 1) % window.RE_AI_PIPELINE.length), 2200);
    return () => clearInterval(id);
  }, []);
  return (
    <section className="section ai-section" id="ai">
      <div className="ai-section-divider" aria-hidden="true"></div>
      <div className="container">
        <div className="ai-grid">
          <div className="ai-text-col">
            <span className="ai-tag">Red Empire × AI</span>
            <h2 className="ai-headline">
              Tools, never authors.<br />
              <span className="accent">Cinema, still made by humans.</span>
            </h2>
            <p className="ai-body">
              We embrace AI across the creative and technical pipeline to <strong>move faster, explore further</strong>
              {' '}and unlock new possibilities in storytelling. What once took <strong>months</strong> can now happen in <strong>days</strong>,
              allowing our teams to spend less time fighting process and more time creating.
            </p>
            <p className="ai-body" style={{ marginTop: 18 }}>
              The technology is <strong>powerful</strong>. The creative vision <strong>remains human</strong>.
            </p>
            <p className="ai-body" style={{ marginTop: 18 }}>
              Every frame, every performance and every creative decision is <strong>shaped and driven by people</strong>.
              Every project is approved by the storytellers behind it. <strong>No exceptions.</strong>
            </p>
            <div className="re-hero-cta-row" style={{ marginTop: 32 }}>
              {navigate && (
                <button className="btn btn-blood" onClick={() => navigate('ai')}>Read our manifesto →</button>
              )}
            </div>
            <div className="reel-inline">
              <div className="reel-inline-frame">
                <iframe
                  src="https://www.youtube.com/embed/YLqRz8GtVHk?rel=0"
                  title="Red Empire process reel"
                  frameBorder="0"
                  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
                  referrerPolicy="strict-origin-when-cross-origin"
                  allowFullScreen
                ></iframe>
              </div>
            </div>
            {!compact && (
            <div className="ai-philosophy">
              <div className="section-eyebrow" style={{ marginBottom: 24 }}>
                <span className="bar"></span>
                <span>Our Philosophy · The Red Empire Approach</span>
              </div>
              <h2 className="ai-headline" style={{ marginBottom: 24 }}>
                Human imagination.<br />
                <span className="accent">Intelligently amplified.</span>
              </h2>
              <p className="ai-body">
                We don't view AI as a replacement for writers, actors, directors or artists.
              </p>
              <p className="ai-philo-beat">
                We view it as a <span className="accent">creative accelerator.</span>
              </p>
              <p className="ai-body">
                Our approach combines traditional filmmaking disciplines with next-generation technologies,
                allowing creators to iterate faster, visualise earlier and push beyond the limitations of
                conventional production pipelines.
              </p>
              <p className="ai-philo-beat">
                Every project begins with people.<br />
                <span className="accent">The technology follows.</span>
              </p>
            </div>
            )}
          </div>
          <div className="ai-pipeline">
            <div className="ai-pipeline-head">
              <span>RE / HYBRID PIPELINE</span>
              <span className="ai-pipeline-ver">v2.6</span>
            </div>
            {window.RE_AI_PIPELINE.map((row, i) => (
              <div key={i} className={'ai-pipeline-row' + (i === active ? ' active' : '')}>
                <span className="idx">{String(i + 1).padStart(2, '0')}</span>
                <div className="ai-pipeline-text">
                  <span className="phase">{row.phase}</span>
                  <span className="tagline">{row.tagline}</span>
                  <span className="desc">{row.desc}</span>
                </div>
              </div>
            ))}
          </div>
        </div>

        {!compact && (
        <div className="ai-performance">
          <div className="section-eyebrow" style={{ marginBottom: 24 }}>
            <span className="bar"></span>
            <span>Performance-Driven Creation</span>
          </div>
          <h2 className="ai-perf-title">
            Any character.<br />Any location.<br /><span className="accent">Any story.</span>
          </h2>
          <div className="ai-perf-body">
            <p className="ai-body">
              One of the biggest misconceptions about AI-generated content is that it removes the human element.
            </p>
            <p className="ai-philo-beat">
              Our process does the <span className="accent">opposite.</span>
            </p>
            <p className="ai-body">
              For many hybrid productions, performances are captured first — through actors, voice artists,
              directors and creators. AI is then used to translate, enhance or transform those performances
              into new visual forms while preserving the emotional authenticity at their core.
            </p>
            <p className="ai-body">
              The result is not artificial storytelling.
            </p>
            <p className="ai-philo-beat">
              It's <span className="accent">human storytelling</span> expressed through new mediums.
            </p>
          </div>
        </div>
        )}

        {!compact && (
        <div className="ai-performance">
          <div className="section-eyebrow" style={{ marginBottom: 24 }}>
            <span className="bar"></span>
            <span>What We Believe</span>
          </div>
          <div className="ai-beliefs">
            <div className="ai-belief-col dont">
              <h3 className="ai-belief-head">What we don't believe</h3>
              <ul className="ai-belief-list">
                <li>AI replaces writers.</li>
                <li>AI replaces actors.</li>
                <li>AI replaces directors.</li>
                <li>AI replaces creativity.</li>
              </ul>
            </div>
            <div className="ai-belief-col do">
              <h3 className="ai-belief-head">What we do believe</h3>
              <ul className="ai-belief-list">
                <li>AI expands creative possibilities.</li>
                <li>AI accelerates production.</li>
                <li>AI lowers barriers.</li>
                <li>AI enables new forms of storytelling.</li>
              </ul>
            </div>
          </div>
          <p className="ai-belief-closer">
            Human creativity remains the <span className="accent">driving force.</span>
          </p>
        </div>
        )}
      </div>
    </section>
  );
}

// ============================================================
// FOOTER
// ============================================================
function Footer({ navigate, onPitch }) {
  const B = window.RE_BRAND;
  return (
    <footer className="re-footer grain">
      <div className="re-footer-grid">
        <div className="re-footer-col re-footer-brandcol">
          <img className="re-footer-mark" src="assets/dragon-mark-clean.png" alt="" />
        </div>
        <div className="re-footer-col">
          <h5>Studio</h5>
          <a onClick={() => navigate('home')}>Home</a>
          <a onClick={() => navigate('slate')}>Slate</a>
          <a onClick={() => navigate('studio')}>About</a>
          <a onClick={() => navigate('ai')}>AI · Process</a>
        </div>
        <div className="re-footer-col">
          <h5>Connect</h5>
          <a onClick={onPitch}>Pitch us</a>
          <a href={'mailto:' + B.email}>{B.email}</a>
          <a href={'tel:' + B.phone}>{B.phone}</a>
        </div>
        <div className="re-footer-col">
          <h5>Follow</h5>
          <a href={B.socials.instagram} target="_blank" rel="noreferrer">Instagram ↗</a>
          <a href={B.socials.facebook} target="_blank" rel="noreferrer">Facebook ↗</a>
          <a href={B.socials.imdb} target="_blank" rel="noreferrer">IMDb ↗</a>
          <a href={B.socials.youtube} target="_blank" rel="noreferrer">YouTube ↗</a>
        </div>
      </div>
      <p className="re-footer-tagline">
        Content that claims <span style={{ color: 'var(--blood)' }}>territory</span> in <span style={{ color: 'var(--blood)' }}>culture</span>.
      </p>
      <div className="re-footer-bottom">
        <span>© 2017 Red Empire Productions Pty Ltd</span>
        <span>{B.location}</span>
      </div>
    </footer>
  );
}

// ============================================================
// PITCH MODAL
// ============================================================
function PitchModal({ open, onClose }) {
  const [step, setStep] = useState('form');
  useEffect(() => { if (open) setStep('form'); }, [open]);
  if (!open) return null;
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal-card" onClick={e => e.stopPropagation()}>
        <button className="modal-close" onClick={onClose}>×</button>
        {step === 'form' && (
          <>
            <span className="eyebrow" style={{ display: 'block', marginBottom: 12 }}>● The intake</span>
            <h2>Pitch us<br />something dangerous.</h2>
            <p className="sub">One paragraph. One line. One word. Whatever the work needs.</p>
            <div className="field">
              <label>Your name</label>
              <input className="input" placeholder="First Last" />
            </div>
            <div className="field">
              <label>Email</label>
              <input className="input" placeholder="you@studio.com" />
            </div>
            <div className="field">
              <label>Project title</label>
              <input className="input" placeholder="Working title" />
            </div>
            <div className="field">
              <label>The pitch</label>
              <textarea className="input" placeholder="A logline, a paragraph, a fully formed manifesto. Up to you." />
            </div>
            <button className="btn btn-blood btn-lg" onClick={() => setStep('sent')} style={{ width: '100%', marginTop: 8 }}>
              Send to the dragon →
            </button>
          </>
        )}
        {step === 'sent' && (
          <div style={{ textAlign: 'center', padding: '32px 0' }}>
            <img src="assets/logo-mark-dragon.png" alt="" style={{ width: 88, marginBottom: 24, opacity: 0.95 }} />
            <h2>Received.</h2>
            <p className="sub">Amie reads everything. Expect a reply within 14 days.</p>
            <button className="btn btn-ghost" onClick={onClose}>Close</button>
          </div>
        )}
      </div>
    </div>
  );
}

// ============================================================
// SPOTIFY BUTTON
// ============================================================
function SpotifyButton({ url, label = 'Listen on Spotify', size = 'lg' }) {
  if (!url) return null;
  const SpotifyMark = (
    <svg width="20" height="20" viewBox="0 0 168 168" aria-hidden="true">
      <circle cx="84" cy="84" r="84" fill="#1DB954"/>
      <path fill="#0A0606" d="M122.667 121.292c-1.575 2.583-4.95 3.4-7.533 1.825-20.633-12.6-46.617-15.45-77.225-8.467-2.95.675-5.9-1.175-6.575-4.125-.675-2.95 1.167-5.9 4.125-6.575 33.5-7.65 62.225-4.358 85.383 9.808 2.583 1.575 3.4 4.95 1.825 7.534zm10.317-22.85c-1.975 3.225-6.183 4.241-9.4 2.275-23.625-14.525-59.642-18.733-87.583-10.258-3.625 1.092-7.45-.942-8.55-4.55-1.092-3.625.942-7.45 4.567-8.55 31.917-9.683 71.575-4.967 98.692 11.683 3.217 1.967 4.233 6.175 2.275 9.4zm.892-23.792c-28.342-16.825-75.05-18.367-102.092-10.158-4.342 1.317-8.933-1.142-10.25-5.483-1.317-4.342 1.142-8.933 5.492-10.258 31.025-9.417 82.625-7.6 115.25 11.766 3.908 2.317 5.192 7.367 2.875 11.275-2.317 3.9-7.367 5.183-11.275 2.858z"/>
    </svg>
  );
  return (
    <a href={url} target="_blank" rel="noreferrer"
       className={'spotify-btn' + (size === 'sm' ? ' spotify-btn-sm' : '')}>
      {SpotifyMark}
      <span>{label}</span>
      <span className="spotify-btn-arrow">↗</span>
    </a>
  );
}

Object.assign(window, { Nav, Hero, MarqueeStrip, SlateList, AISection, Footer, PitchModal, SpotifyButton, PreorderButton, PosterFX });

// ============================================================
// POSTER FX — living neon particles + code rain over a banner
// ============================================================
function PosterFX({ variant = 'cyber' }) {
  const canvasRef = useRef(null);
  const wrapRef = useRef(null);
  useEffect(() => {
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const canvas = canvasRef.current, wrap = wrapRef.current;
    if (!canvas || !wrap) return;
    const ctx = canvas.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const PINK = [255, 45, 126], CYAN = [41, 211, 255];
    const chars = '01<>/\\{}[]=+*#01\u30a2\u30a4\u30a6\u30a8\u30aa0110';
    let W = 0, H = 0, sparks = [], columns = [], raf = 0, last = performance.now(), cap = 120, baseline = 24, burst = 0;
    let flakes = [], drips = [], dripAt = 0, dripGap = 3;
    const gothic = variant === 'gothic';
    const mech = variant === 'mechanica';
    const broadcast = variant === 'broadcast';
    const bandY = 0.455, bandX0 = 0.22, bandX1 = 0.78, bandAmp = 0.089;
    let phases = [], barCount = 120, mist = [];
    const mkMist = () => ({ x: Math.random() * W, y: (0.77 + Math.random() * 0.20) * H, r: (0.12 + Math.random() * 0.16) * W, sp: (Math.random() < 0.5 ? -1 : 1) * (20 + Math.random() * 26), ph: Math.random() * Math.PI * 2, a: 0.13 + Math.random() * 0.13 });
    const lip = { x: 0.83, y: 0.52 };
    const actors = [
      { x: 0.16, y: 0.40, w: 0.15, h: 0.42 },
      { x: 0.29, y: 0.10, w: 0.18, h: 0.70 },
      { x: 0.54, y: 0.12, w: 0.17, h: 0.66 },
      { x: 0.64, y: 0.42, w: 0.15, h: 0.38 },
      { x: 0.75, y: 0.40, w: 0.15, h: 0.34 }
    ];
    let glT = 0, glIdx = 0;
    const title = { x: 0.15, y: 0.75, w: 0.72, h: 0.15 };
    let tTimer = 0, tGap = 1.5, tBurst = 0;
    const poster = wrap.parentElement ? wrap.parentElement.querySelector('img') : null;

    const siren = variant === 'siren';
    const goggles = variant === 'goggles';
    const eyeXL = { x: 0.195, y: 0.265, w: 0.075, h: 0.075 };
    const eyeXR = { x: 0.27, y: 0.255, w: 0.075, h: 0.075 };
    let gogT = 0, gogIdx = 0;
    const gogRegions = [eyeXL, eyeXR];
    const sloganRegion = { x: 0.235, y: 0.75, w: 0.555, h: 0.155 };
    const waveL = { x: 0.427, y: 0.316, w: 0.089, h: 0.045 };
    const waveR = { x: 0.793, y: 0.316, w: 0.069, h: 0.045 };
    const navCenter = { x: 0.932, y: 0.875 };
    const navRadiusXFrac = 0.045, navRadiusYFrac = 0.080;
    let navAngle = 0;
    let wavePhases = null;
    let rain = [];
    const rainSlant = 0.55; // heavy right-lean matching the poster's rain
    const mkRain = () => ({ x: Math.random() * W * 1.4 - W * 0.2, y: Math.random() * H, len: 16 + Math.random() * 26, speed: 300 + Math.random() * 220, a: 0.10 + Math.random() * 0.16 });
    let boltTimer = 0, boltGap = 2.6, boltFlash = 0;
    const boltFlashMax = 0.32;
    const boltOrigin = { x: 0.12, y: 0.015 };
    const beacon = { x: 0.635, y: 0.095 };
    const beamUpperFar = { x: 1.06, y: -0.03 };
    const beamLowerFar = { x: 1.06, y: 0.175 };
    const beamLeftUpperFar = { x: 0.30, y: 0.01 };
    const beamLeftLowerFar = { x: 0.30, y: 0.14 };
    const eyeL = { x: 0.235, y: 0.21 };
    const eyeR = { x: 0.365, y: 0.205 };
    let boltPath = [], boltBranches = [];
    function genBoltPath() {
      boltPath = [];
      boltBranches = [];
      const endX = 0.19, endY = 0.72;
      const steps = 11;
      let x = boltOrigin.x, y = boltOrigin.y;
      for (let i = 0; i <= steps; i++) {
        const t = i / steps;
        y = boltOrigin.y + t * (endY - boltOrigin.y);
        x = boltOrigin.x + t * (endX - boltOrigin.x) + (Math.random() - 0.5) * 0.11 * (1 - t * 0.2);
        boltPath.push({ x, y });
        if (Math.random() < 0.55 && i > 1 && i < steps - 1) {
          const dir = Math.random() < 0.5 ? -1 : 1;
          const bx = x + dir * (0.05 + Math.random() * 0.09);
          const by = y + 0.05 + Math.random() * 0.06;
          boltBranches.push([{ x, y }, { x: bx, y: by }]);
        }
      }
    }
    const mkFlake = () => ({ x: Math.random() * W, y: Math.random() * H, r: 0.6 + Math.random() * 2.2, sp: 8 + Math.random() * 24, sway: 6 + Math.random() * 14, ph: Math.random() * Math.PI * 2, a: 0.12 + Math.random() * 0.4 });
    const mkDrip = () => ({ x: lip.x * W + (Math.random() - 0.5) * 12, y0: lip.y * H, y: lip.y * H, vy: 6 + Math.random() * 6, r: 1.3 + Math.random() * 1.2, life: 0, max: 3.5 + Math.random() * 2 });
    const pointer = { x: -9999, y: -9999, active: false };

    const rc = () => (Math.random() < 0.5 ? CYAN : PINK);
    const mkSpark = (x, y, ang, spd) => {
      const a = (ang === undefined) ? (-Math.PI / 2 + (Math.random() - 0.5) * 1.7) : ang;
      const s = (spd === undefined) ? (220 + Math.random() * 420) : spd;
      const fromBottom = (x === undefined);
      return {
        x: fromBottom ? Math.random() * W : x,
        y: fromBottom ? H + 8 + Math.random() * 30 : y,
        vx: Math.cos(a) * s, vy: Math.sin(a) * s,
        life: 0, max: 0.5 + Math.random() * 1.0,
        col: rc(), w: 0.8 + Math.random() * 1.3
      };
    };
    function resize() {
      const r = canvas.getBoundingClientRect();
      W = r.width; H = r.height;
      if (W < 2 || H < 2) return;
      canvas.width = Math.floor(W * dpr); canvas.height = Math.floor(H * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
      if (gothic) {
        flakes = Array.from({ length: Math.round((W * H) / 6500) }, mkFlake);
        drips = []; dripAt = 0; dripGap = 2.5;
      } else if (broadcast) {
        barCount = Math.max(70, Math.round((bandX1 - bandX0) * W / 5));
        phases = Array.from({ length: barCount }, () => Math.random() * Math.PI * 2);
        mist = Array.from({ length: Math.max(14, Math.round(W / 65)) }, mkMist);
      } else if (siren) {
        rain = Array.from({ length: Math.max(70, Math.round((W * H) / 9000)) }, mkRain);
        genBoltPath();
      } else {
        const cols = Math.max(6, Math.round(W / 34));
        columns = Array.from({ length: cols }, (_, i) => ({
          x: (i + 0.5) * (W / cols), y: Math.random() * H,
          speed: 30 + Math.random() * 70, len: 6 + Math.floor(Math.random() * 10), col: (mech ? [90, 200, 255] : rc())
        }));
      }
    }
    function frame(now) {
      const dt = Math.min(0.05, (now - last) / 1000); last = now;
      const br = canvas.getBoundingClientRect();
      if (Math.abs(br.width - W) > 1 || Math.abs(br.height - H) > 1) resize();
      if (W < 2 || H < 2) { raf = requestAnimationFrame(frame); return; }
      ctx.clearRect(0, 0, W, H);
      if (gothic) {
        ctx.globalCompositeOperation = 'source-over';
        for (const f of flakes) {
          f.y += f.sp * dt; f.ph += dt * 0.8;
          const fx = f.x + Math.sin(f.ph) * f.sway;
          if (f.y - f.r > H) { f.y = -f.r; f.x = Math.random() * W; }
          const g = ctx.createRadialGradient(fx, f.y, 0, fx, f.y, f.r * 3);
          g.addColorStop(0, 'rgba(255,255,255,' + f.a + ')');
          g.addColorStop(1, 'rgba(255,255,255,0)');
          ctx.fillStyle = g; ctx.beginPath(); ctx.arc(fx, f.y, f.r * 3, 0, Math.PI * 2); ctx.fill();
        }
        raf = requestAnimationFrame(frame);
        return;
      }
      if (broadcast) {
        ctx.globalCompositeOperation = 'lighter';
        for (const m of mist) {
          m.x += m.sp * dt; m.ph += dt * 0.7;
          if (m.x - m.r > W) m.x = -m.r; else if (m.x + m.r < 0) m.x = W + m.r;
          const my = m.y + Math.sin(m.ph) * 50;
          const mg = ctx.createRadialGradient(m.x, my, 0, m.x, my, m.r);
          mg.addColorStop(0, 'rgba(84,98,93,' + m.a + ')');
          mg.addColorStop(1, 'rgba(84,98,93,0)');
          ctx.fillStyle = mg; ctx.beginPath(); ctx.arc(m.x, my, m.r, 0, Math.PI * 2); ctx.fill();
        }
        const tt = now / 1000;
        const yc = bandY * H, x0 = bandX0 * W, x1 = bandX1 * W, span = x1 - x0, amp = bandAmp * H;
        const loud = 0.55 + 0.45 * Math.sin(tt * 1.9 + Math.sin(tt * 0.6) * 2.5);
        for (let i = 0; i < barCount; i++) {
          const xn = i / (barCount - 1);
          const bx = x0 + xn * span;
          const taper = Math.pow(Math.sin(xn * Math.PI), 0.6);
          const n = Math.abs(Math.sin(xn * 55 + tt * 9 + phases[i]) * 0.6 + Math.sin(xn * 21 - tt * 6.5) * 0.4);
          const h = amp * (0.05 + n * loud * taper);
          ctx.fillStyle = 'rgba(205,238,232,' + (0.35 + 0.4 * (h / amp)) + ')';
          ctx.fillRect(bx - 1, yc - h, 2, h * 2);
        }
        raf = requestAnimationFrame(frame);
        return;
      }
      if (goggles) {
        ctx.globalCompositeOperation = 'source-over';
        if (poster && poster.complete && poster.naturalWidth) {
          gogT += dt;
          const onDur = 0.3, offDur = 0.55;
          if (gogT > onDur + offDur) { gogT = 0; }
          if (gogT < onDur) {
            const inten = 0.6 + 0.4 * Math.sin((gogT / onDur) * Math.PI);
            const nW = poster.naturalWidth, nH = poster.naturalHeight;
            for (const r of gogRegions) {
              const rx = r.x * W, ry = r.y * H, rw = r.w * W, rh = r.h * H;
              const sx = r.x * nW, sy = r.y * nH, sw = r.w * nW, sh = r.h * nH;
              const bands = 10;
              for (let b = 0; b < bands; b++) {
                const t = b / bands;
                const off = Math.random() < 0.6 ? (Math.random() - 0.5) * 18 * inten : 0;
                ctx.drawImage(poster, sx, sy + sh * t, sw, sh / bands + 1, rx + off, ry + rh * t, rw, rh / bands + 1);
              }
              ctx.globalCompositeOperation = 'lighter';
              for (let n = 0; n < 2; n++) {
                const by = ry + Math.random() * rh, bh = 1.5 + Math.random() * 4;
                ctx.fillStyle = Math.random() < 0.5 ? 'rgba(90,200,255,' + (0.32 * inten) + ')' : 'rgba(255,60,140,' + (0.24 * inten) + ')';
                ctx.fillRect(rx, by, rw, bh);
              }
              ctx.globalCompositeOperation = 'source-over';
            }
          }
        }
        const tt = now / 1000;
        const barsPer = 11;
        if (!wavePhases) wavePhases = Array.from({ length: barsPer }, () => Math.random() * Math.PI * 2);
        ctx.globalCompositeOperation = 'lighter';
        for (const reg of [waveL, waveR]) {
          const rx = reg.x * W, ry = reg.y * H, rw = reg.w * W, rh = reg.h * H;
          for (let i = 0; i < barsPer; i++) {
            const xn = i / (barsPer - 1);
            const bx = rx + xn * rw;
            const n = Math.abs(Math.sin(tt * 6 + wavePhases[i]) * 0.6 + Math.sin(tt * 2.7 + xn * 8) * 0.4);
            const taper = Math.pow(Math.sin(xn * Math.PI), 0.7);
            const h = rh * (0.08 + n * taper * 0.92);
            ctx.fillStyle = 'rgba(110,225,255,' + (0.5 + 0.4 * n) + ')';
            ctx.fillRect(bx - 1, ry + rh / 2 - h / 2, 2, h);
          }
        }
        navAngle += dt * 1.6;
        const ncx = navCenter.x * W, ncy = navCenter.y * H;
        const nrx = navRadiusXFrac * W, nry = navRadiusYFrac * H;
        const scaleY = nrx !== 0 ? (nry / nrx) : 1;
        ctx.save();
        ctx.translate(ncx, ncy);
        ctx.scale(1, scaleY);
        ctx.rotate(navAngle);
        const sweep = ctx.createConicGradient ? null : null;
        const wedgeSpread = 0.55;
        const grad2 = ctx.createRadialGradient(0, 0, 0, 0, 0, nrx);
        grad2.addColorStop(0, 'rgba(120,225,255,0.55)');
        grad2.addColorStop(1, 'rgba(120,225,255,0)');
        ctx.fillStyle = grad2;
        ctx.beginPath();
        ctx.moveTo(0, 0);
        ctx.arc(0, 0, nrx, -wedgeSpread / 2, wedgeSpread / 2);
        ctx.closePath();
        ctx.fill();
        ctx.strokeStyle = 'rgba(190,240,255,0.85)';
        ctx.lineWidth = 1.6;
        ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(nrx, 0); ctx.stroke();
        ctx.restore();
        ctx.globalCompositeOperation = 'source-over';
        raf = requestAnimationFrame(frame);
        return;
      }
      if (siren) {
        ctx.globalCompositeOperation = 'lighter';
        const tt = now / 1000;
        const cyc = tt % 3.4;
        let pulse;
        if (cyc < 0.3) pulse = 0.55 + 0.45 * Math.sin((cyc / 0.3) * Math.PI);
        else pulse = 0.22 + 0.06 * Math.sin(tt * 3.3) + 0.04 * Math.sin(tt * 7.1);
        const flicker = 0.9 + Math.random() * 0.1;
        const bx = beacon.x * W, by = beacon.y * H;
        const ux = beamUpperFar.x * W, uy = beamUpperFar.y * H;
        const lx = beamLowerFar.x * W, ly = beamLowerFar.y * H;
        const beamAlpha = pulse * flicker;
        const grad = ctx.createLinearGradient(bx, by, (ux + lx) / 2, (uy + ly) / 2);
        grad.addColorStop(0, 'rgba(225,247,240,' + (0.55 * beamAlpha) + ')');
        grad.addColorStop(0.4, 'rgba(200,240,232,' + (0.28 * beamAlpha) + ')');
        grad.addColorStop(1, 'rgba(200,240,232,0)');
        ctx.fillStyle = grad;
        ctx.beginPath();
        ctx.moveTo(bx, by);
        ctx.lineTo(ux, uy);
        ctx.lineTo(lx, ly);
        ctx.closePath();
        ctx.fill();
        const lux = beamLeftUpperFar.x * W, luy = beamLeftUpperFar.y * H;
        const llx = beamLeftLowerFar.x * W, lly = beamLeftLowerFar.y * H;
        const gradL = ctx.createLinearGradient(bx, by, (lux + llx) / 2, (luy + lly) / 2);
        gradL.addColorStop(0, 'rgba(225,247,240,' + (0.4 * beamAlpha) + ')');
        gradL.addColorStop(1, 'rgba(200,240,232,0)');
        ctx.fillStyle = gradL;
        ctx.beginPath();
        ctx.moveTo(bx, by);
        ctx.lineTo(lux, luy);
        ctx.lineTo(llx, lly);
        ctx.closePath();
        ctx.fill();
        const core = ctx.createRadialGradient(bx, by, 0, bx, by, H * 0.09);
        core.addColorStop(0, 'rgba(255,255,255,' + (0.7 * beamAlpha) + ')');
        core.addColorStop(1, 'rgba(255,255,255,0)');
        ctx.fillStyle = core;
        ctx.beginPath(); ctx.arc(bx, by, H * 0.09, 0, Math.PI * 2); ctx.fill();
        const eyeCyc = (tt + 1.6) % 3.4;
        let eyePulse;
        if (eyeCyc < 0.3) eyePulse = 0.55 + 0.45 * Math.sin((eyeCyc / 0.3) * Math.PI);
        else eyePulse = 0.35 + 0.08 * Math.sin(tt * 4.1) + 0.05 * Math.sin(tt * 6.7);
        const eyeAlpha = eyePulse * (0.92 + Math.random() * 0.08);
        const eyeR_px = H * 0.028;
        for (const eye of [eyeL, eyeR]) {
          const exx = eye.x * W, eyy = eye.y * H;
          const eg = ctx.createRadialGradient(exx, eyy, 0, exx, eyy, eyeR_px * 3.2);
          eg.addColorStop(0, 'rgba(215,247,255,' + (0.85 * eyeAlpha) + ')');
          eg.addColorStop(0.45, 'rgba(160,225,240,' + (0.4 * eyeAlpha) + ')');
          eg.addColorStop(1, 'rgba(160,225,240,0)');
          ctx.fillStyle = eg;
          ctx.beginPath(); ctx.arc(exx, eyy, eyeR_px * 3.2, 0, Math.PI * 2); ctx.fill();
        }
        for (const r of rain) {
          r.x += rainSlant * r.speed * dt; r.y += r.speed * dt;
          if (r.y - r.len > H) { r.y = -r.len - Math.random() * 120; r.x = Math.random() * W * 1.4 - W * 0.2; }
          const tx = r.x - rainSlant * r.len, ty = r.y - r.len;
          ctx.strokeStyle = 'rgba(205,228,224,' + r.a + ')';
          ctx.lineWidth = 1;
          ctx.beginPath(); ctx.moveTo(r.x, r.y); ctx.lineTo(tx, ty); ctx.stroke();
        }
        boltTimer += dt;
        if (boltTimer > boltGap && boltFlash <= 0) {
          boltTimer = 0; boltGap = 2.8 + Math.random() * 4.2; boltFlash = boltFlashMax; genBoltPath();
        }
        if (boltFlash > 0) {
          boltFlash -= dt;
          const life = Math.max(0, boltFlash / boltFlashMax);
          const flicker = (Math.random() < 0.72 ? 1 : 0.3) * life;
          const gx = boltOrigin.x * W, gy = boltOrigin.y * H;
          const wash = ctx.createRadialGradient(gx, gy, 0, gx, gy, W * 0.55);
          wash.addColorStop(0, 'rgba(215,242,255,' + (0.34 * flicker) + ')');
          wash.addColorStop(1, 'rgba(215,242,255,0)');
          ctx.fillStyle = wash; ctx.fillRect(0, 0, W, H * 0.85);
          ctx.lineCap = 'round'; ctx.lineJoin = 'round';
          ctx.shadowColor = 'rgba(190,232,255,0.95)';
          ctx.shadowBlur = 22;
          ctx.strokeStyle = 'rgba(150,220,255,' + (0.55 * flicker) + ')';
          ctx.lineWidth = 9;
          ctx.beginPath();
          boltPath.forEach((p, i) => { const px = p.x * W, py = p.y * H; if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); });
          ctx.stroke();
          ctx.strokeStyle = 'rgba(255,255,255,' + (0.97 * flicker) + ')';
          ctx.lineWidth = 3.4;
          ctx.beginPath();
          boltPath.forEach((p, i) => { const px = p.x * W, py = p.y * H; if (i === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); });
          ctx.stroke();
          ctx.lineWidth = 1.8;
          ctx.strokeStyle = 'rgba(255,255,255,' + (0.8 * flicker) + ')';
          for (const br of boltBranches) {
            ctx.beginPath();
            ctx.moveTo(br[0].x * W, br[0].y * H);
            ctx.lineTo(br[1].x * W, br[1].y * H);
            ctx.stroke();
          }
          ctx.shadowBlur = 0;
        }
        raf = requestAnimationFrame(frame);
        return;
      }
      ctx.globalCompositeOperation = 'lighter';
      ctx.font = '12px "IBM Plex Mono", ui-monospace, monospace';
      for (const c of columns) {
        c.y += c.speed * dt;
        if (c.y - c.len * 14 > H) { c.y = -Math.random() * 40; c.speed = 30 + Math.random() * 70; c.col = rc(); }
        for (let k = 0; k < c.len; k++) {
          const yy = c.y - k * 14;
          if (yy < 0 || yy > H) continue;
          const a = (1 - k / c.len) * 0.45;
          ctx.fillStyle = 'rgba(' + c.col[0] + ',' + c.col[1] + ',' + c.col[2] + ',' + a + ')';
          ctx.fillText(chars[(Math.floor(yy / 14) + Math.floor(c.x)) % chars.length], c.x, yy);
        }
      }
      if (mech) {
        if (pointer.active) {
          const pg = ctx.createRadialGradient(pointer.x, pointer.y, 0, pointer.x, pointer.y, 110);
          pg.addColorStop(0, 'rgba(120,215,255,0.20)'); pg.addColorStop(1, 'rgba(120,215,255,0)');
          ctx.fillStyle = pg; ctx.beginPath(); ctx.arc(pointer.x, pointer.y, 110, 0, Math.PI * 2); ctx.fill();
        }
        if (poster && poster.complete && poster.naturalWidth) {
          glT += dt;
          const onDur = 0.42, offDur = 0.5;
          if (glT > onDur + offDur) { glT = 0; glIdx = (glIdx + 1) % actors.length; }
          if (glT < onDur) {
            const r = actors[glIdx];
            const inten = 0.55 + 0.45 * Math.sin((glT / onDur) * Math.PI);
            const nW = poster.naturalWidth, nH = poster.naturalHeight;
            const rx = r.x * W, ry = r.y * H, rw = r.w * W, rh = r.h * H;
            const sx = r.x * nW, sy = r.y * nH, sw = r.w * nW, sh = r.h * nH;
            const bands = 14;
            ctx.globalCompositeOperation = 'source-over';
            for (let b = 0; b < bands; b++) {
              const t = b / bands;
              const off = Math.random() < 0.6 ? (Math.random() - 0.5) * 34 * inten : 0;
              ctx.drawImage(poster, sx, sy + sh * t, sw, sh / bands + 1, rx + off, ry + rh * t, rw, rh / bands + 1);
            }
            ctx.globalCompositeOperation = 'lighter';
            for (let n = 0; n < 3; n++) {
              const by = ry + Math.random() * rh, bh = 2 + Math.random() * 6;
              ctx.fillStyle = Math.random() < 0.5 ? 'rgba(90,200,255,' + (0.28 * inten) + ')' : 'rgba(255,60,120,' + (0.20 * inten) + ')';
              ctx.fillRect(rx, by, rw, bh);
            }
            ctx.globalCompositeOperation = 'source-over';
          }
          tTimer += dt;
          if (tTimer > tGap) { tTimer = 0; tGap = 1.2 + Math.random() * 2.5; tBurst = 0.16 + Math.random() * 0.12; }
          if (tBurst > 0) {
            tBurst -= dt;
            const nW = poster.naturalWidth, nH = poster.naturalHeight;
            const rx = title.x * W, ry = title.y * H, rw = title.w * W, rh = title.h * H;
            const sx = title.x * nW, sy = title.y * nH, sw = title.w * nW, sh = title.h * nH;
            const bands = 8;
            ctx.globalCompositeOperation = 'source-over';
            for (let b = 0; b < bands; b++) {
              const t = b / bands;
              const off = Math.random() < 0.5 ? (Math.random() - 0.5) * 12 : 0;
              ctx.drawImage(poster, sx, sy + sh * t, sw, sh / bands + 1, rx + off, ry + rh * t, rw, rh / bands + 1);
            }
            ctx.globalCompositeOperation = 'lighter';
            const by = ry + Math.random() * rh;
            ctx.fillStyle = Math.random() < 0.5 ? 'rgba(90,200,255,0.18)' : 'rgba(255,60,120,0.12)';
            ctx.fillRect(rx, by, rw, 2 + Math.random() * 4);
            ctx.globalCompositeOperation = 'source-over';
          }
        }
      }
      ctx.globalCompositeOperation = 'source-over';
      raf = requestAnimationFrame(frame);
    }
    resize();
    raf = requestAnimationFrame(frame);
    const ro = new ResizeObserver(resize); ro.observe(canvas);
    window.addEventListener('resize', resize);
    if (poster) poster.addEventListener('load', resize);
    const move = (e) => { const r = canvas.getBoundingClientRect(); pointer.x = e.clientX - r.left; pointer.y = e.clientY - r.top; pointer.active = true; };
    const leave = () => { pointer.active = false; pointer.x = -9999; pointer.y = -9999; };
    wrap.addEventListener('pointermove', move);
    wrap.addEventListener('pointerleave', leave);
    return () => { cancelAnimationFrame(raf); ro.disconnect(); window.removeEventListener('resize', resize); if (poster) poster.removeEventListener('load', resize); wrap.removeEventListener('pointermove', move); wrap.removeEventListener('pointerleave', leave); };
  }, [variant]);
  return (
    <div ref={wrapRef} className="poster-fx" data-variant={variant}>
      <canvas ref={canvasRef} className="poster-fx-canvas"></canvas>
    </div>
  );
}

// ============================================================
// PREORDER — captures name + email locally
// ============================================================
function PreorderButton({ title, label = 'Preorder the book' }) {
  const STORAGE_KEY = 're_preorders';
  const [open, setOpen] = useState(false);
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');
  const [done, setDone] = useState(false);

  const submit = (e) => {
    e.preventDefault();
    if (!name.trim()) { setError('Please enter your name.'); return; }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) { setError('Please enter a valid email.'); return; }
    try {
      const list = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
      list.push({ title: title || '', name: name.trim(), email: email.trim(), at: new Date().toISOString() });
      localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
    } catch (err) { /* storage unavailable — still confirm */ }
    setDone(true);
  };

  if (done) {
    return (
      <div className="preorder-confirm">
        <span className="preorder-confirm-mark">✓</span>
        <div className="preorder-confirm-text">
          <strong>You’re on the list, {name.trim().split(' ')[0]}.</strong>
          <span>We’ll email {email.trim()} the moment preorders open.</span>
        </div>
      </div>
    );
  }

  if (!open) {
    return <button className="btn btn-blood" onClick={() => setOpen(true)}>{label} →</button>;
  }

  return (
    <form className="preorder-form" onSubmit={submit}>
      <div className="preorder-form-head">
        <span>Preorder · reserve your copy</span>
        <button type="button" className="preorder-form-close" onClick={() => setOpen(false)} aria-label="Cancel">✕</button>
      </div>
      <div className="preorder-fields">
        <input type="text" placeholder="Full name" value={name}
               onChange={e => { setName(e.target.value); setError(''); }} />
        <input type="email" placeholder="Email address" value={email}
               onChange={e => { setEmail(e.target.value); setError(''); }} />
      </div>
      {error && <p className="preorder-error">{error}</p>}
      <button type="submit" className="btn btn-blood preorder-submit">Confirm preorder →</button>
    </form>
  );
}
