/* === Shared components === */

const Logo = ({ light = false }) => (
  <a href="/" className={`logo ${light ? 'logo-light' : 'logo-dark'}`} aria-label="Affordable Energy Australia — home">
    <img src="assets/logo.png" alt="Affordable Energy Australia" className="logo-img" />
  </a>
);

// Build a working referral share URL for any surface/channel: lands the friend
// on the petition page, ref in the query (attribution capture reads it).
// Falls back to a ref-less petition link when no code is known.
function referralShareUrl(code) {
  const base = window.location.origin + '/petition';
  return code ? `${base}?ref=${encodeURIComponent(String(code).toUpperCase())}` : base;
}
function storedReferralCode() {
  try { return localStorage.getItem('aea_referral_code') || null; } catch (_) { return null; }
}
window.referralShareUrl = referralShareUrl;
window.storedReferralCode = storedReferralCode;

const NAV = [
  { href: '/', label: 'Home' },
  { href: '/petition', label: 'Sign the Petition' },
  { href: '/the-problem', label: 'The Problem' },
  { href: '/take-action', label: 'Take Action' },
  { href: '/news', label: 'In the News' },
  { href: '/about-us', label: 'About' },
];

// Pages where a signed supporter gets a distraction-free, logo-only header
// (the logo still links home as an escape hatch). Keeps them in the
// donate → share funnel without a nav bar pulling them away.
const SIGNED_FUNNEL_ROUTES = ['/donate', '/share', '/thank-you-donation', '/thank-you-petition'];

const Header = ({ route }) => {
  const [scrolled, setScrolled] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const [signed, setSigned] = React.useState(() => {
    try { return sessionStorage.getItem('aea_signed') === '1'; } catch (_) { return false; }
  });
  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll, { passive: true });
    const onSigned = () => setSigned(true);
    window.addEventListener('petition-signed', onSigned);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('petition-signed', onSigned);
    };
  }, []);
  React.useEffect(() => { setOpen(false); }, [route]);
  const isActive = (href) => route === href;

  // Signed supporter on a funnel page → logo-only header.
  if (signed && SIGNED_FUNNEL_ROUTES.includes(route)) {
    return (
      <header className={`site-header logo-only ${scrolled ? 'scrolled' : ''}`}>
        <div className="container-wide site-header-inner" style={{ justifyContent: 'center' }}>
          <Logo light />
        </div>
      </header>
    );
  }

  return (
    <header className={`site-header ${scrolled ? 'scrolled' : ''}`}>
      <div className="container-wide site-header-inner">
        <Logo light />
        <nav className="site-nav">
          {NAV.map(n => (
            <a key={n.href} href={n.href} className={isActive(n.href) ? 'active' : ''}>
              {n.label}
            </a>
          ))}
        </nav>
        <div className="header-ctas">
          <a href="/petition" className="btn btn-teal"><span className="full-label">Sign the Petition</span><span className="short-label">Sign</span></a>
          <a href="/donate" className="btn btn-amber">Donate</a>
          <button className={`menu-toggle ${open ? 'open' : ''}`} onClick={() => setOpen(!open)} aria-label="Menu"><span /></button>
        </div>
      </div>
      <div className={`mobile-drawer ${open ? 'open' : ''}`}>
        {NAV.map(n => (
          <a key={n.href} href={n.href} className={isActive(n.href) ? 'active' : ''}>{n.label}</a>
        ))}
        <div className="mobile-drawer-ctas">
          <a href="/petition" className="btn btn-amber">Sign the Petition →</a>
          <a href="/donate" className="btn btn-outline-white">Donate</a>
        </div>
      </div>
    </header>
  );
};

const Footer = () => {
  const content = useContent();
  const site = content?.site || {};
  return (
  <footer className="site-footer" data-screen-label="Footer">
    <div className="container-wide">
      <div className="footer-grid">
        <div>
          <Logo light />
          <p style={{ marginTop: 20, opacity: 0.75, fontSize: 15, lineHeight: 1.6, maxWidth: '40ch' }}>
            {site.tagline || 'A people-powered, single-issue campaign building a public mandate for affordable and reliable energy for every Australian household.'}
          </p>
          <div className="social-row">
            {[
              ['linkedin', 'LinkedIn', 'M4.98 3.5a2 2 0 11-.02 4 2 2 0 01.02-4zM3 8.5h4V21H3zM10 8.5h3.8v1.7h.05c.53-.95 1.83-1.95 3.77-1.95 4.03 0 4.78 2.5 4.78 5.75V21h-4v-5.3c0-1.27-.02-2.9-1.77-2.9-1.77 0-2.04 1.38-2.04 2.8V21h-4z'],
              ['instagram', 'Instagram', 'M8 3h8a5 5 0 015 5v8a5 5 0 01-5 5H8a5 5 0 01-5-5V8a5 5 0 015-5zm0 2a3 3 0 00-3 3v8a3 3 0 003 3h8a3 3 0 003-3V8a3 3 0 00-3-3zm4 3.5a3.5 3.5 0 110 7 3.5 3.5 0 010-7zm0 2a1.5 1.5 0 100 3 1.5 1.5 0 000-3zM17 6a1 1 0 110 2 1 1 0 010-2z'],
              ['youtube', 'YouTube', 'M22 8.3a3 3 0 00-2.1-2.1C18 5.6 12 5.6 12 5.6s-6 0-7.9.6A3 3 0 002 8.3 31 31 0 002 12a31 31 0 00.1 3.7 3 3 0 002.1 2.1c1.9.6 7.8.6 7.8.6s6 0 7.9-.6a3 3 0 002.1-2.1A31 31 0 0022 12a31 31 0 00-.1-3.7zM10 15V9l5.2 3z'],
              ['facebook', 'Facebook', 'M13.5 21v-8h2.7l.4-3.1h-3.1V7.9c0-.9.25-1.5 1.55-1.5h1.65V3.6c-.8-.1-1.6-.15-2.4-.15-2.4 0-4.05 1.47-4.05 4.16V9.9H7.5V13h2.75v8z'],
              ['tiktok', 'TikTok', 'M16 3c.3 2.1 1.5 3.6 3.5 3.9v2.9c-1.3.1-2.5-.2-3.6-.9v5.6c0 3.1-2.3 5.5-5.3 5.5S5.3 17.6 5.3 14.6c0-2.8 2-5.1 4.8-5.4v3c-1.1.3-1.9 1.2-1.9 2.4 0 1.4 1.1 2.5 2.5 2.5s2.4-1.1 2.4-2.6V3z'],
              ['x', 'X', 'M17.5 3h3l-6.6 7.5L21.8 21h-6.1l-4.3-5.6L6.4 21H3.4l7-8L2.6 3h6.2l3.9 5.2zm-1.1 16h1.7L7.4 4.8H5.6z'],
            ].map(([key, label, d]) => (
              <a key={key} href={(site.social && site.social[key]) || '#'} aria-label={label} target="_blank" rel="noopener noreferrer">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d={d} /></svg>
              </a>
            ))}
          </div>
        </div>
        <div>
          <h5>Campaign</h5>
          <ul>
            <li><a href="/petition">Sign the Petition</a></li>
            <li><a href="/take-action">Take Action</a></li>
            <li><a href="/donate">Donate</a></li>
            <li><a href="/the-problem">The Problem</a></li>
          </ul>
        </div>
        <div>
          <h5>About</h5>
          <ul>
            <li><a href="/about-us">Our Mission</a></li>
            <li><a href="/news">In the News</a></li>
          </ul>
        </div>
        <div>
          <h5>Legal</h5>
          <ul>
            <li><a href="/privacy">Privacy Policy</a></li>
          </ul>
        </div>
      </div>
      <div className="footer-bottom">
        <span>{site.copyright || '© 2026 Affordable Energy Australia'} · ABN {site.abn || '42 645 336 534'}</span>
      </div>
    </div>
  </footer>
  );
};

// Scroll-triggered count-up from 0 → end (gated on `start`, e.g. useInView).
// Used by the homepage StatBand where the reveal should play when scrolled into view.
const useCountUpOnView = (end, duration = 1800, start = false) => {
  const [val, setVal] = React.useState(0);
  React.useEffect(() => {
    if (!start) return;
    let raf, t0;
    const tick = (t) => {
      if (!t0) t0 = t;
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(Math.floor(eased * end));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [end, duration, start]);
  return val;
};

// Animates a displayed number from its previous value to `target` with an
// ease-out cubic curve. Unlike useCountUpOnView it re-runs whenever `target`
// changes, so a live counter tweens smoothly on every update (poll, optimistic
// bump, etc.). First mount tweens 0 → target as a reveal. Returns the current
// animated integer.
const useCountUp = (target, { duration = 1400 } = {}) => {
  const [val, setVal] = React.useState(0);
  const fromRef = React.useRef(0);
  React.useEffect(() => {
    const from = fromRef.current;
    const to = Math.round(Number(target) || 0);
    if (from === to) return;
    let raf, t0 = null;
    const tick = (t) => {
      if (t0 === null) t0 = t;
      const p = Math.min(1, (t - t0) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      const cur = Math.round(from + (to - from) * eased);
      fromRef.current = cur;
      setVal(cur);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, duration]);
  return val;
};

const useInView = (threshold = 0.2) => {
  const ref = React.useRef(null);
  const [seen, setSeen] = React.useState(false);
  React.useEffect(() => {
    if (!ref.current || seen) return;
    const obs = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setSeen(true); obs.disconnect(); }
    }, { threshold });
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [seen, threshold]);
  return [ref, seen];
};

const formatStat = (n, format) => {
  switch (format) {
    case '1-in-X': return <><span>1</span><small> in </small>{n}</>;
    case '$X': return <>${(+n).toLocaleString()}</>;
    case 'X%': return <>{n}<small>%</small></>;
    case 'Xk': return <>{n}<small>k</small></>;
    default: return <>{(+n).toLocaleString()}</>;
  }
};

const StatBand = () => {
  const content = useContent();
  const stats = (content && content.stats) || [];
  const [ref, seen] = useInView(0.3);
  const counts = [
    useCountUpOnView(+(stats[0]?.num || 0), 1400, seen),
    useCountUpOnView(+(stats[1]?.num || 0), 1800, seen),
    useCountUpOnView(+(stats[2]?.num || 0), 1400, seen),
    useCountUpOnView(+(stats[3]?.num || 0), 1800, seen),
  ];
  return (
    <section className="stats-band" ref={ref}>
      <div className="container-wide stats-grid">
        {stats.slice(0, 4).map((s, i) => (
          <div className="stat" key={i}>
            <span className="num">{formatStat(counts[i], s.format)}</span>
            <span className="label">{s.label}</span>
            {s.source && (
              <span className="source">Source: {s.source}</span>
            )}
          </div>
        ))}
      </div>
    </section>
  );
};

// Live petition signature count.
//   - Polls /api/petition-count every 5s while the tab is visible.
//   - Pauses polling when the tab is hidden; resumes (and immediately refetches)
//     when it becomes visible again, so a counter that's been backgrounded for
//     an hour shows current state on the very next render.
//   - Listens for a `petition-count:bump` window event so any submit handler
//     can optimistically tick the counter the moment the signer hits Submit.
//   - Falls back silently to the value passed in (typically the CMS
//     hero.petitionCount) if the endpoint is unreachable — never shows 0.
function usePetitionCount(fallback) {
  const [count, setCount] = React.useState(fallback);

  React.useEffect(() => {
    let alive = true;
    let intervalId = null;
    const load = () => {
      fetch('/api/petition-count', { cache: 'no-store' })
        .then(r => (r.ok ? r.json() : null))
        .then(j => { if (alive && j && typeof j.count === 'number' && j.count > 0) setCount(j.count); })
        .catch(() => {});
    };
    const start = () => {
      if (intervalId) return;
      load();
      intervalId = setInterval(load, 5_000);
    };
    const stop = () => {
      if (intervalId) { clearInterval(intervalId); intervalId = null; }
    };
    const onVis = () => {
      if (document.visibilityState === 'visible') start();
      else stop();
    };
    onVis();
    document.addEventListener('visibilitychange', onVis);
    return () => {
      alive = false;
      stop();
      document.removeEventListener('visibilitychange', onVis);
    };
  }, []);

  React.useEffect(() => {
    const onBump = (e) => {
      const minimum = (e && e.detail && e.detail.minimum);
      setCount(c => Math.max(c, typeof minimum === 'number' ? minimum : c + 1));
    };
    window.addEventListener('petition-count:bump', onBump);
    return () => window.removeEventListener('petition-count:bump', onBump);
  }, []);

  return count;
}

const PetitionCounter = ({ baseClass = "hero-counter" }) => {
  const content = useContent();
  const fallback = +(content?.hero?.petitionCount || 47832);
  const target = usePetitionCount(fallback);
  const n = useCountUp(target);
  return (
    <div className={baseClass}>
      <span className="num">{n.toLocaleString()}</span>
      <span className="label">Australians have signed</span>
    </div>
  );
};

const SocialTicker = () => {
  const content = useContent();
  const fallback = [
    { name: 'Kevin', state: 'NSW' },
    { name: 'Mark', state: 'VIC' },
    { name: 'Nathalia', state: 'NSW' },
    { name: 'Charlotte', state: 'WA' },
    { name: 'Luke', state: 'TAS' },
  ];
  const items = (content?.ticker?.length ? content.ticker : fallback);
  const all = [...items, ...items, ...items];
  return (
    <div className="social-ticker">
      <div className="ticker-inner">
        {all.map((it, i) => (
          <span className="ticker-item" key={i}>
            <span className="dot" />
            <strong>{it.name}</strong> from {it.state} just signed
          </span>
        ))}
      </div>
    </div>
  );
};

const StickyMobileBar = () => {
  const content = useContent();
  const fallback = +(content?.hero?.petitionCount || 47832);
  const target = usePetitionCount(fallback);
  const [visible, setVisible] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => setVisible(window.scrollY > 600 && window.innerWidth < 1024);
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
  }, []);
  return (
    <div className={`sticky-bar ${visible ? 'visible' : ''}`}>
      <div>
        <div className="num">{target.toLocaleString()}</div>
        <div className="lbl">have signed</div>
      </div>
      <a href="/petition" className="btn btn-teal">Sign the Petition</a>
    </div>
  );
};

/* Hero placeholder illustrations */
const HERO_ICONS = {
  petition: (
    <svg viewBox="0 0 200 160" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <rect x="40" y="20" width="100" height="130" rx="4" fill="rgba(255,255,255,.04)" />
      <path d="M55 50h70M55 70h70M55 90h50M55 110h60" />
      <path d="M150 95l16 16 30-32" stroke="#F5A623" strokeWidth="5" />
    </svg>
  ),
  megaphone: (
    <svg viewBox="0 0 200 160" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <path d="M30 70v20a10 10 0 0010 10h10v-40H40a10 10 0 00-10 10z" fill="rgba(255,255,255,.04)" />
      <path d="M50 60l90-30v100l-90-30z" fill="rgba(245,166,35,.18)" />
      <path d="M70 100c0 10 5 20 15 20s10-15 5-25" />
      <path d="M155 60l20-8M160 80h25M155 100l20 8" stroke="#F5A623" strokeWidth="4" />
    </svg>
  ),
  newspaper: (
    <svg viewBox="0 0 200 160" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <rect x="30" y="30" width="140" height="100" rx="3" fill="rgba(255,255,255,.04)" />
      <rect x="44" y="44" width="56" height="36" fill="rgba(245,166,35,.25)" />
      <path d="M110 44h50M110 56h50M110 68h36M44 92h120M44 104h120M44 116h80" />
    </svg>
  ),
  community: (
    <svg viewBox="0 0 200 160" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <circle cx="60" cy="60" r="18" fill="rgba(245,166,35,.2)" />
      <circle cx="100" cy="50" r="20" fill="rgba(255,255,255,.06)" />
      <circle cx="140" cy="60" r="18" fill="rgba(245,166,35,.2)" />
      <path d="M30 130c0-18 14-30 30-30s30 12 30 30M70 130c0-22 14-36 30-36s30 14 30 36M110 130c0-18 14-30 30-30s30 12 30 30" />
    </svg>
  ),
  donate: (
    <svg viewBox="0 0 200 160" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <path d="M100 130l-32-30c-12-12-12-30 0-40 10-10 26-8 32 4 6-12 22-14 32-4 12 10 12 28 0 40z" fill="rgba(245,166,35,.22)" stroke="#F5A623" />
      <path d="M70 60h-26M156 60h-26M58 40l-16-12M142 40l16-12" />
    </svg>
  ),
  check: (
    <svg viewBox="0 0 100 100" fill="none" stroke="currentColor" strokeWidth="6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <circle cx="50" cy="50" r="42" fill="rgba(61,189,168,.12)" />
      <path d="M30 52l14 14 28-32" />
    </svg>
  ),
  heart: (
    <svg viewBox="0 0 100 100" fill="currentColor" aria-hidden="true">
      <path d="M50 86 L20 56c-8-8-8-22 0-30 8-8 21-6 24 4 3-10 16-12 24-4 8 8 8 22 0 30z" />
    </svg>
  ),
  user: (
    <svg viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      <circle cx="32" cy="22" r="10" />
      <path d="M14 54c0-10 8-16 18-16s18 6 18 16" />
    </svg>
  ),
};

const HeroPlaceholder = ({ icon = 'petition', tag = 'Image' }) => (
  <div className="hero-placeholder" role="img" aria-label={`${tag} placeholder`}>
    <span className="ph-tag">{tag}</span>
    {HERO_ICONS[icon]}
  </div>
);

/* === Social proof toast ===
 * Ephemeral bottom-right toast, mounted sitewide from App (suppressed on
 * /donate and /share). Prefers real events (petition-signed / donation-completed
 * ≥ $50) and falls back to a curated idle pool. */
const SOCIAL_PROOF_POOL = [
  { first: 'Kevin',    state: 'NSW' },
  { first: 'Margaret', state: 'QLD' },
  { first: 'Luke',     state: 'TAS' },
  { first: 'Sharon',   state: 'VIC' },
  { first: 'Nathan',   state: 'WA'  },
  { first: 'Chloe',    state: 'SA'  },
  { first: 'Barry',    state: 'QLD' },
  { first: 'Amelia',   state: 'NSW' },
  { first: 'Trevor',   state: 'VIC' },
  { first: 'Janet',    state: 'WA'  },
  { first: 'Darren',   state: 'SA'  },
  { first: 'Olivia',   state: 'NSW' },
  { first: 'Wayne',    state: 'QLD' },
  { first: 'Bronwyn',  state: 'TAS' },
  { first: 'Craig',    state: 'VIC' },
  { first: 'Hannah',   state: 'WA'  },
];
const SOCIAL_PROOF_DONATIONS = [35, 65, 135];

const socialProofText = (t) => {
  const where = t.state ? ` from ${t.state}` : '';
  if (t.type === 'donation') return `${t.first}${where} chipped in $${t.amount}`;
  return `${t.first}${where} just signed the petition`;
};

const SocialProofPopup = ({ route }) => {
  const suppressed = route === '/donate' || route === '/share';
  const [toast, setToast] = React.useState(null);
  const [visible, setVisible] = React.useState(false);
  const dismissTimer = React.useRef(null);
  const idleTimer = React.useRef(null);
  const seq = React.useRef(0);

  const dismiss = (e) => {
    if (e) e.stopPropagation();
    if (dismissTimer.current) { clearTimeout(dismissTimer.current); dismissTimer.current = null; }
    setVisible(false);
    setTimeout(() => setToast(null), 320);
  };

  const show = (t) => {
    if (dismissTimer.current) clearTimeout(dismissTimer.current);
    setToast({ ...t, id: ++seq.current });
    setVisible(false);
    requestAnimationFrame(() => requestAnimationFrame(() => setVisible(true)));
    dismissTimer.current = setTimeout(() => dismiss(), 9000);
  };

  const makeIdleToast = () => {
    const p = SOCIAL_PROOF_POOL[Math.floor(Math.random() * SOCIAL_PROOF_POOL.length)];
    if (Math.random() < 0.25) {
      const amount = SOCIAL_PROOF_DONATIONS[Math.floor(Math.random() * SOCIAL_PROOF_DONATIONS.length)];
      return { type: 'donation', first: p.first, state: p.state, amount };
    }
    return { type: 'petition', first: p.first, state: p.state };
  };

  const scheduleIdle = (delay) => {
    if (idleTimer.current) clearTimeout(idleTimer.current);
    idleTimer.current = setTimeout(() => {
      if (document.visibilityState === 'visible') show(makeIdleToast());
      scheduleIdle(60000);
    }, delay);
  };

  // First idle toast ~8s after mount, then one every 60s.
  React.useEffect(() => {
    scheduleIdle(8000);
    return () => {
      if (idleTimer.current) clearTimeout(idleTimer.current);
      if (dismissTimer.current) clearTimeout(dismissTimer.current);
    };
  }, []);

  // Real events take priority: show immediately and reset the idle cadence.
  React.useEffect(() => {
    const onSigned = (e) => {
      const first = (e && e.detail && e.detail.first) || 'Someone';
      show({ type: 'petition', first });
      scheduleIdle(60000);
    };
    const onDonation = (e) => {
      const amount = e && e.detail && e.detail.amount;
      if (!(amount >= 50)) return;
      const first = (e.detail && e.detail.first) || 'Someone';
      show({ type: 'donation', first, amount });
      scheduleIdle(60000);
    };
    window.addEventListener('petition-signed', onSigned);
    window.addEventListener('donation-completed', onDonation);
    return () => {
      window.removeEventListener('petition-signed', onSigned);
      window.removeEventListener('donation-completed', onDonation);
    };
  }, []);

  if (suppressed || !toast) return null;

  const isMobile = typeof window !== 'undefined' && window.innerWidth <= 640;
  const goTo = () => { window.navigate(toast.type === 'donation' ? '/donate' : '/petition'); };

  return (
    <div
      onClick={goTo}
      role="status"
      style={{
        position: 'fixed',
        right: 18,
        bottom: isMobile ? 74 : 18,
        zIndex: 900,
        display: 'flex',
        alignItems: 'center',
        gap: 12,
        background: '#fff',
        color: 'var(--ink)',
        borderLeft: '4px solid var(--teal)',
        borderRadius: 6,
        boxShadow: '0 8px 30px rgba(13,31,28,.18)',
        padding: '12px 16px',
        fontSize: 14,
        lineHeight: 1.35,
        maxWidth: 300,
        cursor: 'pointer',
        transform: visible ? 'translateY(0)' : 'translateY(16px)',
        opacity: visible ? 1 : 0,
        transition: 'opacity .3s ease, transform .3s ease',
      }}
    >
      <span style={{ flex: 1 }}>
        <strong>{toast.first}</strong>{socialProofText(toast).slice(toast.first.length)}
      </span>
      <button
        type="button"
        onClick={dismiss}
        aria-label="Dismiss"
        style={{
          background: 'none', border: 'none', cursor: 'pointer',
          color: 'var(--grey)', fontSize: 18, lineHeight: 1, padding: 0, flexShrink: 0,
        }}
      >×</button>
    </div>
  );
};

// Browser-side CAPI/Pixel helper. Fires the browser Pixel (deduped by event_id)
// then POSTs the same event to /api/meta-capi. All errors swallowed.
window.sendCAPI = (event_name, custom_data) => {
  try {
    let attribution = {};
    try { attribution = JSON.parse(sessionStorage.getItem('aea_attribution') || '{}'); } catch (_) {}
    const event_id = 'web_' + event_name + '_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);
    const payload = {
      event_name,
      event_id,
      event_source_url: location.href,
      user: { fbp: attribution._fbp, fbclid: attribution.fbclid },
      custom_data,
    };
    if (typeof window.fbq === 'function') {
      try { window.fbq('track', event_name, custom_data || {}, { eventID: event_id }); } catch (_) {}
    }
    fetch('/api/meta-capi', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
      keepalive: true,
    }).catch(() => {});
  } catch (_) {}
};

Object.assign(window, { Logo, Header, Footer, StatBand, PetitionCounter, SocialTicker, StickyMobileBar, SocialProofPopup, useCountUp, useCountUpOnView, useInView, usePetitionCount, NAV, HeroPlaceholder, HERO_ICONS });
