/* ── Komponen bersama: ikon, Header, Footer, UI kecil ── */
const { useState, useEffect } = React;

const IC = {
  menu:   <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>,
  x:      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>,
  heart:  <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-7-4.5-9.5-9.5C.5 7 4 3 7.5 3c1.7 0 3.4 1 4.5 2.5C13.1 4 14.8 3 16.5 3 20 3 23.5 7 21.5 11.5 19 16.5 12 21 12 21z"/></svg>,
  star:   <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>,
  sun:    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="12" cy="12" r="4.5"/><path d="M12 1.5v2M12 20.5v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1.5 12h2M20.5 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4"/></svg>,
  moon:   <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>,
  search: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.5" y2="16.5"/></svg>,
  home:   <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 11l9-8 9 8"/><path d="M5 10v10h14V10"/></svg>,
  doc:    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>,
};

/* ── Toggle Mode Gelap (header) ── */
function ThemeToggle() {
  const [dark, setDark] = useState(() => document.documentElement.dataset.theme === 'dark');
  const toggle = () => {
    const next = !dark;
    document.documentElement.dataset.theme = next ? 'dark' : 'light';
    try { localStorage.setItem('mqa_theme', next ? 'dark' : 'light'); } catch {}
    setDark(next);
  };
  return (
    <button onClick={toggle} aria-label="Ganti mode" title={dark ? 'Mode terang' : 'Mode gelap'}
      style={{ width: 40, height: 40, borderRadius: 10, border: '1.5px solid var(--border)', background: 'var(--surface)', color: 'var(--ink-deep)', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      {dark ? IC.sun : IC.moon}
    </button>
  );
}

const NAV = [
  { key: 'home', label: 'Beranda' },
  { key: 'articles', label: 'Artikel' },
  { key: 'programs', label: 'Program' },
  { key: 'teachers', label: 'Pengajar' },
  { key: 'kegiatan', label: 'Kegiatan' },
  { key: 'gallery', label: 'Fasilitas' },
  { key: 'toko', label: 'Toko' },
  { key: 'donasi', label: 'Donasi' },
  { key: 'about', label: 'Tentang' },
  { key: 'contact', label: 'Kontak' },
];

function Header({ navigate, page, user }) {
  const [mobileOpen, setMobileOpen] = useState(false);
  const [searchOpen, setSearchOpen] = useState(false);
  const go = (k) => { navigate(k); setMobileOpen(false); };
  return (
    <>
      {searchOpen && <SearchOverlay navigate={navigate} onClose={() => setSearchOpen(false)} />}
      <header className="mqa-header">
        <div className="container">
          <div className="mqa-header-inner">
            <div className="mqa-logo" style={{ cursor: 'pointer' }} onClick={() => go('home')}>
              <img src="logo.jpg" alt="Markaz Qurrota A'yun" style={{ height: '50px', width: 'auto', display: 'block' }} /><img src="logo-rq.png" alt="Rumah Qur'an Qurrota A'yun" style={{ height: '46px', width: 'auto', display: 'block', marginLeft: '10px' }} />
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginInlineStart: 'auto' }}>
              <button onClick={() => setSearchOpen(true)} aria-label="Cari" title="Cari artikel" style={{ width: 40, height: 40, borderRadius: 10, border: '1.5px solid var(--border)', background: 'var(--surface)', color: 'var(--ink-deep)', cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>{IC.search}</button>
              <ThemeToggle />
              <button className="mqa-btn-donasi" onClick={() => go('donasi')}>{IC.heart}<span className="label-d">Donasi</span></button>
              <button className="mqa-hamburger" onClick={() => setMobileOpen(true)} aria-label="Menu">{IC.menu}</button>
              {user
                ? <button className="mqa-btn-masuk mqa-hide-sm" onClick={() => go('admin')} style={{ background: 'var(--navy)', color: '#fff', borderColor: 'var(--navy)' }}>Dashboard</button>
                : <button className="mqa-btn-masuk mqa-hide-sm" onClick={() => go('login')}>Masuk</button>}
            </div>
          </div>
        </div>
      </header>
      <nav className="mqa-nav-strip">
        <div className="container">
          <div className="mqa-nav-strip-inner">
            {NAV.map(n => (
              <button key={n.key} className={`mqa-nav-strip-item${page === n.key ? ' active' : ''}`} onClick={() => go(n.key)}>{n.label}</button>
            ))}
          </div>
        </div>
      </nav>
      <div className={`mqa-mobile-menu${mobileOpen ? ' open' : ''}`}>
        <div className="mqa-mobile-menu-head">
          <div className="mqa-mobile-menu-logo">Markaz Qurrota A'yun</div>
          <button className="mqa-mobile-menu-close" onClick={() => setMobileOpen(false)}>{IC.x}</button>
        </div>
        {NAV.map(n => (
          <button key={n.key} className={`mqa-mobile-nav-item${page === n.key ? ' active' : ''}`} onClick={() => go(n.key)}>{n.label}</button>
        ))}
        <div className="mqa-mobile-nav-footer">
          <button className="mqa-btn-daftar" style={{ width: '100%', padding: '13px', fontSize: '14.5px', fontWeight: 800 }} onClick={() => go('register')}>📝 Daftar Santri Baru</button>
          <button onClick={() => go(user ? 'admin' : 'login')} style={{ width: '100%', marginTop: '10px', background: 'none', border: 'none', color: 'rgba(255,255,255,.55)', fontSize: '12.5px', fontWeight: 600, cursor: 'pointer', fontFamily: 'inherit', padding: '6px' }}>{user ? '⚙️ Buka Dashboard' : 'Masuk sebagai Admin'}</button>
        </div>
      </div>
    </>
  );
}

function Footer({ navigate }) {
  const [s, setS] = useState({});
  useEffect(() => { api.get('content.php?action=get_section&section=settings').then(r => { if (r.ok && r.data && typeof r.data === 'object' && !Array.isArray(r.data)) setS(r.data); }).catch(() => {}); }, []);
  const programs = ["Tahfidz Al-Qur'an", 'Bahasa Arab', 'Fiqih Ibadah', 'Akhlaq & Sirah'];
  const navLinks = [['home', 'Beranda'], ['articles', 'Artikel'], ['teachers', 'Pengajar'], ['gallery', 'Fasilitas'], ['contact', 'Kontak']];
  const support = [['donasi', 'Donasi'], ['donasi', 'Wakaf'], ['donasi', 'Beasiswa'], ['donasi', 'Transparansi']];
  const norm = (u) => u ? (/^https?:/.test(u) ? u : 'https://' + u) : null;
  const waLink = s.wa ? 'https://wa.me/' + String(s.wa).replace(/\D/g, '') : (s.phone ? 'https://wa.me/' + String(s.phone).replace(/\D/g, '').replace(/^0/, '62') : 'https://wa.me/628123456789');
  const socials = [
    ['WhatsApp', waLink, '<path d="M12 2a10 10 0 0 0-8.6 15l-1.3 4.9 5-1.3A10 10 0 1 0 12 2zm0 18a8 8 0 0 1-4.1-1.1l-.3-.2-3 .8.8-2.9-.2-.3A8 8 0 1 1 12 20zm4.4-6c-.2-.1-1.4-.7-1.6-.8s-.4-.1-.5.1l-.7.9c-.1.2-.3.2-.5.1a6.5 6.5 0 0 1-3.2-2.8c-.2-.4.2-.4.6-1.2.1-.2 0-.3 0-.5l-.7-1.7c-.2-.4-.4-.4-.5-.4h-.5a1 1 0 0 0-.7.3 3 3 0 0 0-.9 2.2 5.2 5.2 0 0 0 1.1 2.8 11.8 11.8 0 0 0 4.6 4c2.1.8 2.1.6 2.5.5a2.5 2.5 0 0 0 1.7-1.2 2 2 0 0 0 .1-1.2c0-.1-.2-.2-.4-.3z" fill="currentColor"/>', true],
    ['Instagram', norm(s.ig), '<rect x="3" y="3" width="18" height="18" rx="5" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="12" cy="12" r="3.5" fill="none" stroke="currentColor" stroke-width="1.8"/><circle cx="17" cy="7" r="1" fill="currentColor"/>', !!s.ig],
    ['YouTube', norm(s.yt), '<rect x="2.5" y="6" width="19" height="12" rx="3.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M10 9.5l5 2.5-5 2.5z" fill="currentColor"/>', !!s.yt],
    ['Facebook', norm(s.fb), '<path d="M14 9V7c0-.6.4-1 1-1h2V3h-3a4 4 0 0 0-4 4v2H8v3h2v9h3v-9h2.5l.5-3z" fill="currentColor"/>', !!s.fb],
    ['TikTok', norm(s.tiktok), '<path d="M14 3v11.5a3 3 0 1 1-2.2-2.9V9.3a5.5 5.5 0 1 0 4.4 5.4V8.6a6 6 0 0 0 3.3 1V7a3.6 3.6 0 0 1-3.3-2.8A3.4 3.4 0 0 1 16 3z" fill="currentColor"/>', !!s.tiktok],
  ].filter(x => x[3]);
  return (
    <footer className="mqa-footer" style={{ position: 'relative', overflow: 'hidden' }}>
      <div className="container" style={{ position: 'relative', zIndex: 1 }}>
        <div className="mqa-footer-grid">
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
              <img src="logo.jpg" alt="" style={{ height: 42, borderRadius: 8 }} /><img src="logo-rq.png" alt="" style={{ height: 42, marginLeft: 8 }} />
              <div>
                <div className="mqa-footer-brand-name" style={{ marginBottom: 2 }}>Markaz Qurrota A'yun</div>
                <div className="mqa-footer-brand-ar" style={{ marginBottom: 0, fontSize: 13 }}>مركز قرة أعين</div>
              </div>
            </div>
            <div className="mqa-footer-brand-desc">Lembaga pendidikan Al-Qur'an dan ilmu agama untuk segala usia, membentuk generasi Qur'ani yang berakhlak mulia.</div>
            <div className="mqa-footer-social">
              {socials.map(([name, url, path]) => (
                <a key={name} href={url} target="_blank" rel="noopener" title={name} aria-label={name} dangerouslySetInnerHTML={{ __html: '<svg width="18" height="18" viewBox="0 0 24 24">' + path + '</svg>' }} />
              ))}
            </div>
          </div>
          <div>
            <div className="mqa-footer-col-title">Program</div>
            {programs.map(l => <div key={l} className="mqa-footer-link" onClick={() => navigate('programs')}>{l}</div>)}
          </div>
          <div>
            <div className="mqa-footer-col-title">Navigasi</div>
            {navLinks.map(([k, l]) => <div key={k + l} className="mqa-footer-link" onClick={() => navigate(k)}>{l}</div>)}
          </div>
          <div>
            <div className="mqa-footer-col-title">Dukungan</div>
            {support.map(([k, l]) => <div key={k + l} className="mqa-footer-link" onClick={() => navigate(k)}>{l}</div>)}
            <div className="mqa-footer-col-title" style={{ marginTop: 18 }}>Kontak</div>
            <div className="mqa-footer-link" style={{ fontSize: 12 }}>+62 812-3456-7890</div>
            <div className="mqa-footer-link" style={{ fontSize: 12 }}>info@qurrotaayun.com</div>
          </div>
        </div>
        <div className="mqa-footer-bottom">
          <div className="mqa-footer-copy">© 2026 Markaz Qurrota A'yun. Semua hak dilindungi.</div>
          <div className="mqa-footer-copy">qurrotaayun.com</div>
        </div>
      </div>
    </footer>
  );
}

function WAButton() {
  return <button className="mqa-wa-btn" onClick={() => window.open('https://wa.me/628123456789', '_blank')} title="WhatsApp">💬</button>;
}
function DonasiFAB({ navigate }) {
  return (
    <button className="mqa-donasi-fab" onClick={() => navigate('donasi')} aria-label="Donasi">
      <svg className="heart" viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-7-4.5-9.5-9.5C.5 7 4 3 7.5 3c1.7 0 3.4 1 4.5 2.5C13.1 4 14.8 3 16.5 3 20 3 23.5 7 21.5 11.5 19 16.5 12 21 12 21z"/></svg>
      <span>Donasi</span>
    </button>
  );
}

function SectionHeader({ label, title, sub, center }) {
  return (
    <div style={{ marginBottom: '40px', textAlign: center ? 'center' : 'inherit' }}>
      <div className="sec-label" style={{ justifyContent: center ? 'center' : 'flex-start' }}>{label}</div>
      <div className="sec-title">{title}</div>
      {sub && <div className="sec-sub" style={{ margin: center ? '10px auto 0' : undefined }}>{sub}</div>}
    </div>
  );
}

const fmtRp = (n) => 'Rp ' + (n || 0).toLocaleString('id-ID');
/* Format ringkas untuk kartu statistik sempit (mis. Rp 176,7 jt) */
const fmtRpShort = (n) => {
  n = n || 0;
  if (n >= 1e9) return 'Rp ' + (n / 1e9).toLocaleString('id-ID', { maximumFractionDigits: 1 }) + ' M';
  if (n >= 1e6) return 'Rp ' + (n / 1e6).toLocaleString('id-ID', { maximumFractionDigits: 1 }) + ' jt';
  if (n >= 1e3) return 'Rp ' + Math.round(n / 1e3) + ' rb';
  return 'Rp ' + n.toLocaleString('id-ID');
};

/* ── Kategori artikel: warna & label ── */
const CATS = {
  'Tahfidz':      { color: '#1F6B47', bg: 'rgba(31,107,71,.10)' },
  'Bahasa Arab':  { color: '#1B2F6E', bg: 'rgba(27,47,110,.10)' },
  'Fiqih':        { color: '#E8912A', bg: 'rgba(232,145,42,.12)' },
  'Akhlaq':       { color: '#8B5A2B', bg: 'rgba(139,90,43,.12)' },
};
// Palet untuk kategori BARU (otomatis dapat warna konsisten per nama)
const CAT_PALETTE = ['#1F6B47', '#1B2F6E', '#E8912A', '#8B5A2B', '#7A4E8C', '#0E7C86', '#B23A48', '#3A6EA5'];
function hexToRgba(hex, a) { const n = parseInt(hex.slice(1), 16); return `rgba(${(n>>16)&255},${(n>>8)&255},${n&255},${a})`; }
const catStyle = (c) => {
  if (CATS[c]) return CATS[c];
  if (!c) return { color: 'var(--muted)', bg: 'var(--bg2)' };
  // warna deterministik dari nama kategori
  let h = 0; for (let i = 0; i < c.length; i++) h = (h * 31 + c.charCodeAt(i)) >>> 0;
  const color = CAT_PALETTE[h % CAT_PALETTE.length];
  const style = { color, bg: hexToRgba(color, 0.11) };
  CATS[c] = style; // cache agar konsisten
  return style;
};
window.catStyle = catStyle;

/* Ikon garis elegan per kategori (pengganti emoji childish saat cover kosong) */
const _gi = (p) => "<svg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'>" + p + "</svg>";
const GLYPH = {
  book:    "<path d='M4 5a2 2 0 0 1 2-2h12v17H6a2 2 0 0 0-2 2z'/><path d='M18 3v17M8 7h6M8 11h6'/>",
  pen:     "<path d='M12 19l7-7a2.8 2.8 0 0 0-4-4l-7 7-1.5 5z'/><path d='M5 21h14'/>",
  scale:   "<path d='M12 3v18M7 21h10M5 7h14l-3 6a3 3 0 0 1-6 0zM5 7l-2 6a3 3 0 0 0 6 0z' transform='translate(0 1)'/>",
  moon:    "<path d='M20 13a8 8 0 1 1-9-9 6 6 0 0 0 9 9z'/>",
  scroll:  "<path d='M6 4h11a2 2 0 0 1 2 2v10a3 3 0 0 0 3 3H8a2 2 0 0 1-2-2z'/><path d='M6 4a2 2 0 0 0-2 2v2h4M10 9h6M10 13h6'/>",
  cap:     "<path d='M22 9L12 4 2 9l10 5 10-5z'/><path d='M6 11v5c0 1 2.7 3 6 3s6-2 6-3v-5'/>",
  calendar:"<rect x='3' y='5' width='18' height='16' rx='2'/><path d='M3 9h18M8 3v4M16 3v4'/>",
  hands:   "<path d='M12 21s-7-4.5-9.5-9.5C.5 7 4 3 7.5 3c1.7 0 3.4 1 4.5 2.5C13.1 4 14.8 3 16.5 3 20 3 23.5 7 21.5 11.5 19 16.5 12 21 12 21z'/>",
  mosque:  "<path d='M12 2c1.5 2 3 3 3 5a3 3 0 0 1-6 0c0-2 1.5-3 3-5z'/><path d='M4 21V11l8-3 8 3v10'/><path d='M9 21v-5a3 3 0 0 1 6 0v5'/>",
};
const CAT_GLYPH = { 'Tahfidz': 'book', 'Bahasa Arab': 'pen', 'Fiqih': 'scale', 'Akhlaq': 'moon', 'Hadits': 'scroll' };
function glyphURL(name) { return "url(\"data:image/svg+xml," + _gi(GLYPH[name] || GLYPH.book) + "\")"; }
function GlyphMark({ name, size }) {
  return <span style={{ position: 'relative', display: 'inline-block', width: size, height: size, backgroundImage: glyphURL(name), backgroundSize: 'contain', backgroundRepeat: 'no-repeat', backgroundPosition: 'center', opacity: .96, filter: 'drop-shadow(0 5px 12px rgba(0,0,0,.28))' }} />;
}
window.GLYPH = GLYPH; window.glyphURL = glyphURL; window.GlyphMark = GlyphMark; window.CAT_GLYPH = CAT_GLYPH;

/* Ikon default per kategori (PHP asli tak punya kolom icon) */
const CAT_ICON = { 'Tahfidz': '📖', 'Bahasa Arab': '✍️', 'Fiqih': '⚖️', 'Akhlaq': '🌙' };

/* Normalisasi artikel: PHP asli pakai author_name/read_time/published_at,
   mock pakai author/readTime/date. Komponen membaca satu bentuk seragam. */
function normArticle(a) {
  if (!a) return a;
  const author = a.author || a.author_name || 'Tim MQA';
  let date = a.date;
  if (!date && a.published_at) {
    try { date = new Date(a.published_at).toLocaleDateString('id-ID', { day: 'numeric', month: 'short', year: 'numeric' }); }
    catch { date = ''; }
  }
  return {
    ...a,
    author,
    authorInitial: a.authorInitial || (author ? author[0] : '?'),
    readTime: a.readTime || a.read_time || '5 menit',
    date: date || '',
    icon: a.icon || CAT_ICON[a.category] || '📖',
    slug: a.slug || slugify(a.title || ('artikel-' + (a.id || ''))),
  };
}
function slugify(t) { return String(t).toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); }
window.slugify = slugify;
window.normArticle = normArticle;

/* ── Markdown ringan & AMAN (escape dulu, lalu format subset) ──
   Dukungan: ## H2, ### H3, **tebal**, *miring*, - poin, 1. nomor, > kutipan, [teks](url) */
function renderMD(src) {
  if (!src) return '';
  // Jika sudah HTML (dari editor WYSIWYG), tampilkan apa adanya
  if (/<(p|h2|h3|ul|ol|li|blockquote|br|div|strong|em|b|i|a)[\s>]/i.test(src)) return src;
  const esc = (t) => t.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  const inline = (t) => esc(t)
    .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
    .replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>')
    .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
  const lines = String(src).replace(/\r/g, '').split('\n');
  const isAr = (t) => { const ar = (t.match(/[\u0600-\u06ff]/g) || []).length; return ar > 0 && ar / t.replace(/\s/g, '').length > 0.4; };
  const pTag = (t) => isAr(t) ? '<p dir="rtl" class="ar">' + inline(t) + '</p>' : '<p>' + inline(t) + '</p>';
  let html = '', list = null;
  const closeList = () => { if (list) { html += list === 'ul' ? '</ul>' : '</ol>'; list = null; } };
  for (let raw of lines) {
    const line = raw.trim();
    if (!line) { closeList(); continue; }
    let m;
    if ((m = line.match(/^###\s+(.*)/))) { closeList(); html += '<h3>' + inline(m[1]) + '</h3>'; }
    else if ((m = line.match(/^##\s+(.*)/))) { closeList(); html += '<h2>' + inline(m[1]) + '</h2>'; }
    else if ((m = line.match(/^>\s+(.*)/))) { closeList(); const t = m[1]; html += isAr(t) ? '<blockquote dir="rtl" class="ar">' + inline(t) + '</blockquote>' : '<blockquote>' + inline(t) + '</blockquote>'; }
    else if ((m = line.match(/^[-*]\s+(.*)/))) { if (list !== 'ul') { closeList(); html += '<ul>'; list = 'ul'; } html += '<li>' + inline(m[1]) + '</li>'; }
    else if ((m = line.match(/^\d+\.\s+(.*)/))) { if (list !== 'ol') { closeList(); html += '<ol>'; list = 'ol'; } html += '<li>' + inline(m[1]) + '</li>'; }
    else { closeList(); html += pTag(line); }
  }
  closeList();
  return html;
}
window.renderMD = renderMD;

function CatBadge({ cat, small }) {
  const s = catStyle(cat);
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: small ? '3px 9px' : '4px 11px', borderRadius: 999, background: s.bg, color: s.color, fontSize: small ? 11 : 12, fontWeight: 800, letterSpacing: '.01em' }}>{cat}</span>
  );
}

/* ── Cover artikel: foto asli bila ada, atau pola geometris Islami yang elegan ── */
const STAR_TILE = "url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='44'%20height='44'%20viewBox='0%200%2044%2044'%3E%3Cg%20fill='none'%20stroke='%23ffffff'%20stroke-opacity='0.16'%20stroke-width='1.3'%3E%3Crect%20x='10'%20y='10'%20width='24'%20height='24'/%3E%3Crect%20x='10'%20y='10'%20width='24'%20height='24'%20transform='rotate(45%2022%2022)'/%3E%3C/g%3E%3C/svg%3E\")";
function CoverArt({ a, ratio, iconSize, badge }) {
  const s = catStyle(a.category);
  return (
    <div style={{ position: 'relative', aspectRatio: ratio, background: s.color, display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
      {a.cover_url
        ? <img src={a.cover_url} alt="" loading="lazy" decoding="async" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
        : <>
            <div style={{ position: 'absolute', inset: 0, backgroundImage: STAR_TILE, backgroundSize: '44px 44px' }} />
            <GlyphMark name={CAT_GLYPH[a.category] || 'scroll'} size={iconSize} />
          </>}
      {badge && <span style={{ position: 'absolute', top: 14, left: 14, padding: '5px 12px', borderRadius: 999, background: 'rgba(255,255,255,.94)', color: s.color, fontSize: 12, fontWeight: 800, zIndex: 1 }}>{a.category}</span>}
    </div>
  );
}

/* ── Kartu artikel (anchor ke URL SEO; klik = SPA instan) ── */
function ArticleCard({ a: raw, onClick, featured }) {
  const a = normArticle(raw);
  const s = catStyle(a.category);
  const href = '/artikel/' + a.slug;
  const handle = (e) => { if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return; e.preventDefault(); onClick && onClick(); };
  if (featured) {
    return (
      <a href={href} onClick={handle} className="art-card-feat fade-up" style={{ textDecoration: 'none', cursor: 'pointer', display: 'grid', gridTemplateRows: 'auto 1fr', background: 'var(--surface)', borderRadius: 20, overflow: 'hidden', border: '1px solid var(--border-soft)', boxShadow: 'var(--shadow-warm-md)' }}>
        <CoverArt a={a} ratio="16/8" iconSize={84} badge />
        <div style={{ padding: '24px 26px 26px' }}>
          <h2 style={{ fontFamily: 'var(--font-d)', fontSize: 27, fontWeight: 800, lineHeight: 1.18, color: 'var(--ink-deep)', margin: '0 0 12px', textWrap: 'pretty' }}>{a.title}</h2>
          <p style={{ fontSize: 15, lineHeight: 1.6, color: 'var(--muted)', margin: '0 0 18px' }}>{a.excerpt}</p>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 13, color: 'var(--muted)' }}>
            <span style={{ width: 30, height: 30, borderRadius: '50%', background: s.color, color: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontSize: 12 }}>{a.authorInitial || (a.author || '?')[0]}</span>
            <span style={{ fontWeight: 700, color: 'var(--ink-deep)' }}>{a.author}</span>
            <span>·</span><span>{a.date}</span><span>·</span><span>{a.readTime}</span>
          </div>
        </div>
      </a>
    );
  }
  return (
    <a href={href} onClick={handle} className="art-card fade-up" style={{ textDecoration: 'none', cursor: 'pointer', display: 'flex', flexDirection: 'column', background: 'var(--surface)', borderRadius: 16, overflow: 'hidden', border: '1px solid var(--border-soft)', boxShadow: 'var(--shadow-warm-sm, 0 4px 14px rgba(120,80,30,.06))', transition: 'transform .18s, box-shadow .18s' }}>
      <CoverArt a={a} ratio="16/9" iconSize={46} />
      <div style={{ padding: '16px 18px 18px', display: 'flex', flexDirection: 'column', flex: 1 }}>
        <div style={{ marginBottom: 10 }}><CatBadge cat={a.category} small /></div>
        <h3 style={{ fontFamily: 'var(--font-d)', fontSize: 18, fontWeight: 800, lineHeight: 1.25, color: 'var(--ink-deep)', margin: '0 0 8px', textWrap: 'pretty' }}>{a.title}</h3>
        <p style={{ fontSize: 13.5, lineHeight: 1.55, color: 'var(--muted)', margin: '0 0 14px', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{a.excerpt}</p>
        <div style={{ marginTop: 'auto', display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'var(--muted)' }}>
          <span style={{ fontWeight: 700, color: 'var(--ink-deep)' }}>{a.author}</span><span>·</span><span>{a.readTime}</span>
        </div>
      </div>
    </a>
  );
}

Object.assign(window, { IC, NAV, Header, Footer, WAButton, DonasiFAB, SectionHeader, fmtRp, fmtRpShort, CATS, catStyle, CatBadge, ArticleCard });

/* ── YouTube: ambil ID dari berbagai format URL → URL embed ── */
function ytId(url) {
  if (!url) return null;
  const m = String(url).match(/(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|embed\/|shorts\/|live\/))([\w-]{11})/);
  return m ? m[1] : (/^[\w-]{11}$/.test(url) ? url : null);
}
function ytThumb(url) { const id = ytId(url); return id ? `https://i.ytimg.com/vi/${id}/hqdefault.jpg` : null; }

/* Blok media: embed video YouTube bila ada, jika tidak gambar, jika tidak fallback */
function MediaBlock({ youtube, image, fallbackIcon, glyph, ratio = '16/9', bg }) {
  const id = ytId(youtube);
  const [play, setPlay] = useState(false);
  if (id) {
    return (
      <div style={{ position: 'relative', aspectRatio: ratio, background: '#000', overflow: 'hidden' }}>
        {play
          ? <iframe src={`https://www.youtube.com/embed/${id}?autoplay=1&rel=0`} title="Video" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 0 }} />
          : <button onClick={(e) => { e.stopPropagation(); setPlay(true); }} aria-label="Putar video" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', border: 0, padding: 0, cursor: 'pointer', background: `url(${image || ytThumb(youtube)}) center/cover` }}>
              <span style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,.28)' }} />
              <span style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 60, height: 60, borderRadius: '50%', background: 'rgba(220,30,30,.92)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 6px 20px rgba(0,0,0,.4)' }}>
                <span style={{ width: 0, height: 0, borderTop: '11px solid transparent', borderBottom: '11px solid transparent', borderLeft: '18px solid #fff', marginLeft: 4 }} />
              </span>
            </button>}
      </div>
    );
  }
  return (
    <div style={{ position: 'relative', aspectRatio: ratio, background: bg || 'var(--navy)', display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden' }}>
      {image
        ? <img src={image} alt="" loading="lazy" decoding="async" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
        : (glyph ? <GlyphMark name={glyph} size={54} /> : <span style={{ fontSize: 46 }}>{fallbackIcon || ''}</span>)}
    </div>
  );
}

Object.assign(window, { ytId, ytThumb, MediaBlock });

/* ════════════════════════════════════════════════
   HERO SLIDER — banner promosi (artikel/program/kegiatan/donasi)
   slides: [{ tag, title, subtitle, cta, icon, bg, accent, onClick }]
   ════════════════════════════════════════════════ */
function HeroSlider({ slides }) {
  const [i, setI] = useState(0);
  const [paused, setPaused] = useState(false);
  const n = slides.length;

  useEffect(() => {
    if (paused || n <= 1) return;
    const t = setInterval(() => setI(p => (p + 1) % n), 5500);
    return () => clearInterval(t);
  }, [paused, n]);

  if (!n) return null;
  const go = (d) => setI(p => (p + d + n) % n);
  const sw = React.useRef(null);
  const onTouchStart = (e) => { sw.current = e.touches[0].clientX; setPaused(true); };
  const onTouchEnd = (e) => { if (sw.current == null) return; const dx = e.changedTouches[0].clientX - sw.current; if (Math.abs(dx) > 45) go(dx < 0 ? 1 : -1); sw.current = null; setPaused(false); };

  return (
    <div className="hero-slider" onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}
      style={{ position: 'relative', borderRadius: 22, overflow: 'hidden', boxShadow: 'var(--shadow-warm-md)', touchAction: 'pan-y' }}>
      <div className="hero-slider-track" style={{ display: 'flex', transform: `translateX(-${i * 100}%)`, transition: 'transform .6s cubic-bezier(.6,.05,.1,1)' }}>
        {slides.map((s, idx) => {
          const fallbackBg = s.bg || (s.accent ? `linear-gradient(135deg, ${s.accent}, color-mix(in srgb, ${s.accent} 58%, #000))` : 'linear-gradient(135deg, var(--brand-deep), var(--brand-navy))');
          const STAR = "url(\"data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='52'%20height='52'%20viewBox='0%200%2052%2052'%3E%3Cg%20fill='none'%20stroke='%23ffffff'%20stroke-opacity='0.13'%20stroke-width='1.4'%3E%3Crect%20x='13'%20y='13'%20width='26'%20height='26'/%3E%3Crect%20x='13'%20y='13'%20width='26'%20height='26'%20transform='rotate(45%2026%2026)'/%3E%3C/g%3E%3C/svg%3E\")";
          return (
          <div key={idx} className="hero-slide" style={{ flex: '0 0 100%', minWidth: '100%', position: 'relative', background: fallbackBg, color: '#fff', minHeight: 380 }}>
            {s.image
              ? <><img src={s.image} alt="" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover' }} />
                  <div style={{ position: 'absolute', inset: 0, background: `linear-gradient(90deg, ${s.overlay || 'rgba(10,20,45,.82)'} 0%, ${s.overlay2 || 'rgba(10,20,45,.45)'} 100%)` }} /></>
              : <>
                  <div style={{ position: 'absolute', inset: 0, backgroundImage: STAR, backgroundSize: '52px 52px' }} />
                  <div className="hero-slide-deco" style={{ position: 'absolute', right: -30, top: '50%', transform: 'translateY(-50%)', fontSize: 260, opacity: .18, lineHeight: 1, pointerEvents: 'none' }}>{s.icon}</div>
                </>}
            <div className="hero-slide-inner" style={{ position: 'relative', zIndex: 1, padding: '52px 56px', maxWidth: 620, display: 'flex', flexDirection: 'column', justifyContent: 'center', minHeight: 380 }}>
              <span style={{ alignSelf: 'flex-start', padding: '6px 14px', borderRadius: 999, background: 'rgba(255,255,255,.18)', backdropFilter: 'blur(4px)', fontSize: 12.5, fontWeight: 800, letterSpacing: '.04em', textTransform: 'uppercase', marginBottom: 18 }}>{s.tag}</span>
              <h2 style={{ fontFamily: 'var(--font-d)', fontSize: 40, fontWeight: 900, lineHeight: 1.12, margin: '0 0 14px', textWrap: 'balance' }}>{s.title}</h2>
              <p style={{ fontSize: 16.5, lineHeight: 1.55, opacity: .9, margin: '0 0 26px', maxWidth: 480 }}>{s.subtitle}</p>
              <button onClick={s.onClick} className="hero-slide-cta" style={{ alignSelf: 'flex-start', background: '#fff', color: s.accent || 'var(--ink-deep)', border: 'none', borderRadius: 12, padding: '13px 26px', fontSize: 15, fontWeight: 800, cursor: 'pointer', fontFamily: 'inherit', display: 'inline-flex', alignItems: 'center', gap: 8, boxShadow: '0 6px 18px rgba(0,0,0,.18)' }}>{s.cta} →</button>
            </div>
          </div>
          );
        })}
      </div>

      {n > 1 && <>
        <button aria-label="Sebelumnya" onClick={() => go(-1)} className="hero-slider-arrow" style={{ left: 16, position: 'absolute', top: '50%', transform: 'translateY(-50%)', zIndex: 2, width: 42, height: 42, borderRadius: '50%', border: 'none', background: 'rgba(255,255,255,.9)', color: 'var(--ink-deep)', cursor: 'pointer', fontSize: 20, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 3px 10px rgba(0,0,0,.2)' }}>‹</button>
        <button aria-label="Berikutnya" onClick={() => go(1)} className="hero-slider-arrow" style={{ right: 16, position: 'absolute', top: '50%', transform: 'translateY(-50%)', zIndex: 2, width: 42, height: 42, borderRadius: '50%', border: 'none', background: 'rgba(255,255,255,.9)', color: 'var(--ink-deep)', cursor: 'pointer', fontSize: 20, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 3px 10px rgba(0,0,0,.2)' }}>›</button>
        <div style={{ position: 'absolute', bottom: 18, left: 56, zIndex: 2, display: 'flex', gap: 8 }}>
          {slides.map((_, idx) => (
            <button key={idx} aria-label={`Slide ${idx + 1}`} onClick={() => setI(idx)} style={{ width: idx === i ? 28 : 9, height: 9, borderRadius: 999, border: 'none', background: idx === i ? '#fff' : 'rgba(255,255,255,.5)', cursor: 'pointer', transition: 'all .3s', padding: 0 }} />
          ))}
        </div>
      </>}
    </div>
  );
}

window.HeroSlider = HeroSlider;

/* ── Progress bar baca (mengisi saat scroll) ── */
function ReadingProgress() {
  const [p, setP] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      setP(max > 0 ? Math.min(100, (h.scrollTop / max) * 100) : 0);
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return <div style={{ position: 'fixed', top: 0, left: 0, height: 3, width: p + '%', background: 'var(--sage)', zIndex: 200, transition: 'width .1s linear' }} />;
}

/* ── Navigasi bawah (mobile) ── */
function BottomNav({ navigate, page, onSearch }) {
  const items = [
    { key: 'home', label: 'Beranda', icon: IC.home },
    { key: 'articles', label: 'Artikel', icon: IC.doc },
    { key: '__search', label: 'Cari', icon: IC.search },
    { key: 'donasi', label: 'Donasi', icon: IC.heart },
  ];
  return (
    <nav className="bottom-nav">
      {items.map(it => {
        const active = page === it.key;
        const onClick = it.key === '__search' ? onSearch : () => navigate(it.key);
        return (
          <button key={it.key} onClick={onClick} className={`bottom-nav-item${active ? ' active' : ''}`}>
            <span className="bn-icon">{it.icon}</span>
            <span className="bn-label">{it.label}</span>
          </button>
        );
      })}
    </nav>
  );
}

/* ── Hook: animasi fade-up saat elemen masuk viewport (anti-gagal) ── */
function useFadeUp(dep) {
  // Dinonaktifkan: animasi opacity berisiko menyangkut di sebagian konteks.
  // Konten kini selalu terlihat (lihat CSS .fade-up). Fungsi dibiarkan agar pemanggilan di App tetap valid.
}

Object.assign(window, { ThemeToggle, CoverArt, ReadingProgress, BottomNav, useFadeUp });

/* ── Overlay Pencarian Global ── */
function SearchOverlay({ navigate, onClose }) {
  const [q, setQ] = useState('');
  const [articles, setArticles] = useState((window.MQA || {}).articles || []);
  useEffect(() => {
    api.get('articles.php?action=list').then(r => { if (r.ok && Array.isArray(r.articles) && r.articles.length) setArticles(r.articles); });
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);
  const t = q.trim().toLowerCase();
  const results = t ? articles.filter(a => ((a.title || '') + (a.excerpt || '') + (a.category || '') + (a.body || '')).toLowerCase().includes(t)).slice(0, 8) : [];
  const open = (a) => { onClose(); if (window.__openArticle) window.__openArticle(a); else { location.hash = '#/articles'; } };
  return (
    <div onClick={onClose} className="anim-overlay" style={{ position: 'fixed', inset: 0, background: 'rgba(10,20,45,.55)', zIndex: 400, display: 'flex', justifyContent: 'center', alignItems: 'flex-start', padding: '12vh 16px 16px', backdropFilter: 'blur(3px)' }}>
      <div onClick={e => e.stopPropagation()} className="anim-sheet" style={{ width: '100%', maxWidth: 560, background: 'var(--surface)', borderRadius: 16, boxShadow: '0 30px 80px rgba(0,0,0,.4)', overflow: 'hidden' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '16px 18px', borderBottom: '1px solid var(--border-soft)' }}>
          <span style={{ color: 'var(--muted)', display: 'flex' }}>{IC.search}</span>
          <input autoFocus value={q} onChange={e => setQ(e.target.value)} placeholder="Cari artikel… (judul, kategori)" style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontSize: 15.5, fontFamily: 'inherit', color: 'var(--ink-deep)' }} />
          <button onClick={onClose} style={{ background: 'var(--bg2)', border: 'none', borderRadius: 7, width: 28, height: 28, cursor: 'pointer', color: 'var(--muted)', fontSize: 13, fontWeight: 700 }}>Esc</button>
        </div>
        <div style={{ maxHeight: '52vh', overflowY: 'auto' }}>
          {t && results.length === 0 && <div style={{ padding: '30px 18px', textAlign: 'center', color: 'var(--muted)', fontSize: 14 }}>Tidak ada artikel cocok untuk “{q.trim()}”.</div>}
          {!t && <div style={{ padding: '24px 18px', textAlign: 'center', color: 'var(--muted)', fontSize: 13.5 }}>Ketik kata kunci untuk mencari artikel.</div>}
          {results.map(a => {
            const na = normArticle(a); const s = catStyle(a.category);
            return (
              <button key={a.id} onClick={() => open(a)} style={{ display: 'flex', gap: 12, alignItems: 'center', width: '100%', textAlign: 'left', padding: '12px 18px', background: 'none', border: 'none', borderTop: '1px solid var(--border-soft)', cursor: 'pointer', fontFamily: 'inherit' }}>
                <span style={{ flexShrink: 0, width: 42, height: 42, borderRadius: 9, background: s.color, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 18 }}>{na.icon}</span>
                <span style={{ minWidth: 0 }}>
                  <span style={{ display: 'block', fontSize: 11, fontWeight: 700, color: s.color }}>{a.category}</span>
                  <span style={{ display: 'block', fontSize: 14.5, fontWeight: 700, color: 'var(--ink-deep)', lineHeight: 1.3, textWrap: 'pretty' }}>{a.title}</span>
                </span>
              </button>
            );
          })}
        </div>
      </div>
    </div>
  );
}
window.SearchOverlay = SearchOverlay;
