/**
 * AJL40 Digital Series
 * © 2026 sultanmuzaffar. Hak cipta terpelihara / All rights reserved.
 * Penggunaan, penyalinan atau pengedaran tanpa kebenaran bertulis adalah dilarang.
 * Unauthorized use, copying or distribution is prohibited.
 */

// AJL 40 — Web UI Kit components
// Essay publication. Editorial reader for the 16-essay Digital Series.

const { useState, useEffect, useRef } = React;

/* ============== HEADER SHARE POPOVER ============== */
function HeaderShare() {
  const [open, setOpen] = useState(false);
  const wrapRef = useRef(null);

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  }, [open]);

  const shareUrl = typeof window !== 'undefined' ? window.location.href : '';
  const shareText = 'AJL 40 — Dunia Kreatif MM, Antologi Jiwa Lagu';
  const openWin = (href) => { window.open(href, '_blank', 'noopener,noreferrer,width=600,height=500'); setOpen(false); };
  const handleWhatsApp = () => openWin(`https://api.whatsapp.com/send?text=${encodeURIComponent(shareText + '\n' + shareUrl)}`);
  const handleFacebook = () => openWin(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}&quote=${encodeURIComponent(shareText)}`);
  const handleX = () => openWin(`https://x.com/intent/tweet?text=${encodeURIComponent(shareText)}&url=${encodeURIComponent(shareUrl)}`);
  const handleTelegram = () => openWin(`https://t.me/share/url?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareText)}`);
  const handleThreads = () => openWin(`https://www.threads.net/intent/post?text=${encodeURIComponent(shareText + ' ' + shareUrl)}`);
  const handleInstagram = () => {
    const copyText = `${shareText}\n${shareUrl}`;
    if (navigator.clipboard) {
      navigator.clipboard.writeText(copyText).then(() => { if (window.showToast) window.showToast('Disalin untuk Instagram!'); }).catch(() => {});
    } else {
      const ta = document.createElement('textarea'); ta.value = copyText; ta.style.cssText = 'position:fixed;opacity:0';
      document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta);
      if (window.showToast) window.showToast('Disalin untuk Instagram!');
    }
    setOpen(false);
  };

  return (
    <div className="ajl-sharepop" ref={wrapRef}>
      <button className={"ajl-iconbtn" + (open ? " is-active" : "")} aria-label="Kongsi laman" title="Kongsi laman" aria-expanded={open} aria-haspopup="true" onClick={() => setOpen(!open)}>
        <Icon name="share" size={18} />
      </button>
      <div className={"ajl-sharepop__menu" + (open ? " is-open" : "")} role="menu">
        <span className="ajl-sharepop__label t-mono">KONGSI LAMAN</span>
        <div className="ajl-sharepop__row">
          <button className="ajl-sharepop__btn" onClick={handleWhatsApp} aria-label="Kongsi ke WhatsApp" title="WhatsApp">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>
          </button>
          <button className="ajl-sharepop__btn" onClick={handleFacebook} aria-label="Kongsi ke Facebook" title="Facebook">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
          </button>
          <button className="ajl-sharepop__btn" onClick={handleX} aria-label="Kongsi ke X" title="X (Twitter)">
            <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
          </button>
          <button className="ajl-sharepop__btn" onClick={handleThreads} aria-label="Kongsi ke Threads" title="Threads">
            <svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M12.186 24h-.007C5.965 24 2.634 20.155 2.634 14.564V14.5c0-5.531 3.315-9.5 8.02-9.5 3.727 0 6.469 2.223 7.178 5.07l-2.46.56c-.474-1.864-2.15-3.396-4.69-3.396-3.288 0-5.515 2.75-5.515 7.266v.064c0 4.134 1.867 7.202 5.537 7.202 1.97 0 3.382-.68 4.296-1.693.85-.943 1.31-2.218 1.31-3.624v-.02c0-.32-.012-.623-.037-.91-.866.426-1.856.653-2.924.653-3.874 0-6.275-2.282-6.275-5.96 0-3.362 2.258-5.802 5.368-5.802 1.673 0 3.037.617 3.95 1.788.877 1.123 1.321 2.694 1.321 4.667v.204c.005.08.008.16.008.24v.02c0 2.06-.629 3.891-1.81 5.226C14.843 22.69 13.115 24 12.186 24zm.793-14.39c-1.783 0-2.882 1.37-2.882 3.568 0 2.322 1.193 3.724 3.189 3.724.717 0 1.378-.14 1.97-.417a7.106 7.106 0 01-.072-1.01v-.203c0-2.813-.77-5.662-2.205-5.662z"/></svg>
          </button>
          <button className="ajl-sharepop__btn ajl-sharepop__btn--ig" onClick={handleInstagram} aria-label="Salin untuk Instagram" title="Instagram (salin)">
            <svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
          </button>
          <button className="ajl-sharepop__btn" onClick={handleTelegram} aria-label="Kongsi ke Telegram" title="Telegram">
            <svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
          </button>
        </div>
      </div>
    </div>
  );
}

/* ============== HEADER ============== */
function Header() {
  const [menuOpen, setMenuOpen] = useState(false);

  // Close menu on ESC key
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') setMenuOpen(false); };
    if (menuOpen) {
      document.addEventListener('keydown', onKey);
      document.body.style.overflow = 'hidden';
    } else {
      document.body.style.overflow = '';
    }
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, [menuOpen]);

  const handleNavClick = (e, href) => {
    setMenuOpen(false);
    if (href.startsWith('#')) {
      e.preventDefault();
      const el = document.querySelector(href);
      if (el) el.scrollIntoView({ behavior: 'smooth' });
    }
  };

  const navLinks = [
    { label: '17 Esei', href: '#esei' },
    { label: 'Data', href: '/data/' },
    { label: 'Tentang', href: '/tentang/' },
    { label: 'Kredit', href: '/kredit/' },
    { label: 'Arkib Visual', href: '/arkib/' },
  ];

  return (
    <header className="ajl-header">
      <div className="ajl-container ajl-header__row">
        <a href="#" className="ajl-wordmark" aria-label="AJL 40">
          <img className="ajl-wordmark__logo" src="./assets/logo.png" alt="AJL 40" />
        </a>
        <nav className="ajl-nav">
          {navLinks.map((l) => (
            <a key={l.href} className="ajl-nav__link" href={l.href}
              style={l.ruby ? {color: 'var(--ruby-bright)'} : undefined}
              onClick={(e) => handleNavClick(e, l.href)}>{l.label}</a>
          ))}
        </nav>
        <div className="ajl-header__end">
          <HeaderShare />
          {/* Hamburger button — mobile only */}
          <button className="ajl-hamburger" aria-label="Menu" onClick={() => setMenuOpen(!menuOpen)}>
            <span className={"ajl-hamburger__bar" + (menuOpen ? " is-open" : "")} />
            <span className={"ajl-hamburger__bar" + (menuOpen ? " is-open" : "")} />
            <span className={"ajl-hamburger__bar" + (menuOpen ? " is-open" : "")} />
          </button>
        </div>
      </div>

      {/* Mobile drawer */}
      <div className={"ajl-drawer-overlay" + (menuOpen ? " is-visible" : "")} onClick={() => setMenuOpen(false)} />
      <nav className={"ajl-drawer" + (menuOpen ? " is-open" : "")}>
        <div className="ajl-drawer__head">
          <a href="#" className="ajl-wordmark" aria-label="AJL 40" onClick={() => setMenuOpen(false)}>
            <img className="ajl-wordmark__logo" src="./assets/logo.png" alt="AJL 40" />
          </a>
          <button className="ajl-drawer__close" aria-label="Tutup menu" onClick={() => setMenuOpen(false)}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
          </button>
        </div>
        <div className="ajl-drawer__links">
          {navLinks.map((l) => (
            <a key={l.href} className="ajl-drawer__link" href={l.href}
              style={l.ruby ? {color: 'var(--ruby-bright)'} : undefined}
              onClick={(e) => handleNavClick(e, l.href)}>{l.label}</a>
          ))}
        </div>
        <div className="ajl-drawer__foot">
          <span style={{fontFamily:'var(--font-mono)',fontSize:'10px',color:'var(--ivory-40)',letterSpacing:'0.08em'}}>AJL 40 · SIRI DIGITAL · 2026</span>
        </div>
      </nav>
    </header>
  );
}

/* ============== HERO ============== */
function Hero({ data, onRead }) {
  return (
    <section className="ajl-hero">
      <div className="ajl-hero__photo">
        <img src="./assets/herovisual01.jpg" alt="Visual AJL — trofi emas dalam lanskap bunyi dan arkib" />
        <div className="ajl-hero__grain" />
        <div className="ajl-hero__protect" />
      </div>

      <div className="ajl-container ajl-hero__inner">
        <div className="ajl-eyebrow ajl-hero__eyebrow">{data.eyebrow}</div>

        <h1 className="ajl-hero__title">
          <span className="ajl-hero__title-line">{data.title}</span>
          {data.titleSub ? <span className="ajl-hero__title-line">{data.titleSub}</span> : null}
          <span className="ajl-hero__title-line ajl-hero__title-em"><em>{data.titleSub2}</em></span>
        </h1>

        <p className="ajl-hero__body">{data.body}</p>
        {data.note ? <p className="ajl-hero__body" style={{ fontSize: '0.8rem', lineHeight: 1.65, opacity: 0.6, marginTop: '0.85rem', maxWidth: '52ch' }}>{data.note}</p> : null}

        <div className="ajl-hero__ctas">
          <button className="ajl-btn ajl-btn--primary" onClick={onRead}>
            {data.ctaPrimary}
            <Icon name="arrow-right" size={14} />
          </button>
          <button className="ajl-btn ajl-btn--ghost" onClick={() => { const el = document.getElementById('esei'); if(el) el.scrollIntoView({behavior:'smooth'}); }}>
            {data.ctaSecondary}
            <span className="ajl-btn__ul" />
          </button>
        </div>

        <div className="ajl-hero__stats">
          {data.stats.map((s, i) => (
            <div className="ajl-stat" key={s.lbl}>
              <div className="ajl-stat__n">{s.n}</div>
              <div className="ajl-stat__lbl t-mono">{s.lbl}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============== DARI KACAMATA — editorial intro band ============== */
function DariKacamata() {
  const scrollToEsei = () => {
    const el = document.getElementById('esei');
    if (el) el.scrollIntoView({ behavior: 'smooth' });
  };
  return (
    <section className="ajl-kacamata">
      <div className="ajl-container">
        <div className="ajl-kacamata__divider" />
        <div className="ajl-kacamata__grid">
          {/* Left: pull-quote style title */}
          <div>
            <div className="ajl-kacamata__eyebrow">Dari Kacamata</div>
            <h2 className="ajl-kacamata__title">
              Mengapa siri ini<br /><em>wujud.</em>
            </h2>
          </div>
          {/* Right: body + CTA */}
          <div>
            <div className="ajl-kacamata__body">
              <p>Dunia Kreatif MM, Antologi Jiwa Lagu lahir sebagai usaha merakam semula makna Anugerah Juara Lagu yang bukan sekadar pertandingan tahunan, tetapi juga sebagai cermin panjang perjalanan muzik, budaya dan ingatan rakyat Malaysia.</p>
              <p>Melalui empat pintu masuk naratif: Seni &amp; Jiwa, Ekosistem, Inovasi dan Warisan — siri ini mengajak pembaca menelusuri AJL sebagai arkib emosi negara: tempat lagu bukan sahaja dinyanyikan, tetapi juga hidup dalam ruang tamu, ingatan keluarga, perdebatan peminat, kegelisahan industri dan harapan generasi akan datang.</p>
              <p>Inilah usaha untuk mendengar semula budaya sebuah negara; bukan dengan nostalgia yang pegun, tetapi dengan kesedaran baharu tentang nilai karya, masa depan muzik tempatan dan tanggungjawab memastikan lagu Malaysia terus bernyawa melangkaui pentas, platform dan zaman.</p>
            </div>
            <div className="ajl-kacamata__cta">
              <button className="ajl-btn ajl-btn--outline" onClick={scrollToEsei}>
                Teroka Empat Pintu Naratif
              </button>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ============== VIDEO WALL (12 panels + Penutup) ==============
   The home-page hero module per v2 spec: 12 clickable music-video-style panels
   with filter chips for decade + theme. Essay 16 sits below as Penutup. */
function VideoWall({ essays, filters, onSelect }) {
  const [naratif, setNaratif] = useState(null);
  const [theme, setTheme] = useState(null);

  // Split into main 12 + Penutup + Pembukaan
  const main = essays.filter((e) => !e.isPenutup && !e.isPembukaan);
  const penutup = essays.find((e) => e.isPenutup);
  const pembukaan = essays.find((e) => e.isPembukaan);

  const matches = (e) => {
    if (naratif && (!e.naratif || !e.naratif.includes(naratif))) return false;
    if (theme && e.theme !== theme) return false;
    return true;
  };
  const activeCount = main.filter(matches).length;
  const hasFilter = naratif || theme;

  return (
    <section className="ajl-wall" id="esei">
      <div className="ajl-container">
        <div className="ajl-wall__head">
          <div className="ajl-eyebrow">17 Esei · Dinding Video Muzik</div>
          <h2 className="ajl-wall__title">
            <span>Tujuh belas pintu masuk.</span><br />
            <em className="ajl-wall__title-em">Naratif muzik kita.</em>
          </h2>
          <p className="ajl-wall__lede">
            Setiap panel ialah satu bilik dalam memori budaya AJL. Tapis ikut dekad atau tema,
            atau buka mana-mana panel untuk membaca esei penuh.
          </p>
        </div>

        {/* Filter chips */}
        <FilterBar
          filters={filters}
          naratif={naratif}
          theme={theme}
          onNaratif={setNaratif}
          onTheme={setTheme}
          activeCount={activeCount}
          totalCount={main.length}
        />

        {/* Pembukaan — opening essay, horizontal panel (above the grid) */}
        {pembukaan && (
          <div className="ajl-wall__pembukaan" id="pembukaan">
            <PembukaanPanel essay={pembukaan} onSelect={onSelect} />
          </div>
        )}

        {/* 4×3 panel grid */}
        <div className="ajl-wall__grid">
          {main.map((e) => (
            <Panel
              key={e.no}
              essay={e}
              dimmed={hasFilter && !matches(e)}
              onSelect={onSelect}
            />
          ))}
        </div>

        {/* Penutup — closing essay, horizontal panel */}
        {penutup && (
          <div className="ajl-wall__penutup" id="penutup">
            <PenutupPanel essay={penutup} onSelect={onSelect} />
          </div>
        )}
      </div>
    </section>
  );
}

/* ============== FILTER BAR (naratif + tema chips) ============== */
function FilterBar({ filters, naratif, theme, onNaratif, onTheme, activeCount, totalCount }) {
  const clearAll = () => { onNaratif(null); onTheme(null); };
  const hasFilter = naratif || theme;
  return (
    <div className="ajl-filterbar">
      <div className="ajl-filterbar__row">
        <div className="ajl-filterbar__label t-mono">NARATIF</div>
        <div className="ajl-filterbar__chips">
          {filters.naratif.map((n) => (
            <button
              key={n}
              className={`ajl-chip ${naratif === n ? "is-on" : ""}`}
              onClick={() => onNaratif(naratif === n ? null : n)}
            >
              {n}
            </button>
          ))}
        </div>
      </div>
      <div className="ajl-filterbar__row">
        <div className="ajl-filterbar__label t-mono">TEMA</div>
        <div className="ajl-filterbar__chips">
          {filters.themes.map((t) => (
            <button
              key={t}
              className={`ajl-chip ${theme === t ? "is-on" : ""}`}
              onClick={() => onTheme(theme === t ? null : t)}
            >
              {t}
            </button>
          ))}
        </div>
      </div>
      <div className="ajl-filterbar__meta">
        <span className="t-mono">{activeCount} / {totalCount} ESEI</span>
        {hasFilter && (
          <button className="ajl-filterbar__clear" onClick={clearAll}>
            Bersihkan tapisan
            <Icon name="close" size={12} />
          </button>
        )}
      </div>
    </div>
  );
}

/* ============== PANEL (one essay in the wall) ============== */
function Panel({ essay, dimmed, onSelect }) {
  return (
    <article
      className={`ajl-panel ${dimmed ? "is-dim" : ""}`}
      onClick={() => onSelect(essay.no, essay)}
      role="button"
      tabIndex={0}
    >
      {/* Background photograph */}
      <div className="ajl-panel__bg" aria-hidden="true">
        {essay.bgImage && <img src={essay.bgImage} alt="" />}
        <div className="ajl-panel__bg-grain" />
        <div className="ajl-panel__bg-protect" />
      </div>

      {/* Top metadata: tema + naratif pills */}
      <div className="ajl-panel__top">
        <span className="ajl-panel__theme">{essay.theme}</span>
        <span className="ajl-panel__decade t-mono">{essay.naratif ? essay.naratif[0] : ""}</span>
      </div>

      {/* Number, title, subtitle */}
      <div className="ajl-panel__body">
        <div className="ajl-panel__no">{essay.no}</div>
        <h3 className="ajl-panel__title">{essay.title}</h3>
        <p className="ajl-panel__sub">{essay.subtitle}</p>
      </div>

      {/* Hover-revealed quote */}
      {essay.hoverQuote && (
        <div className="ajl-panel__hover">
          <blockquote className="ajl-panel__quote"><em>“{essay.hoverQuote}”</em></blockquote>
          <div className="ajl-panel__cta">
            <span>Baca Esei</span>
            <Icon name="arrow-right" size={14} />
          </div>
        </div>
      )}

      {/* Waveform line at bottom */}
      <Waveform />
    </article>
  );
}



/* ============== WAVEFORM line (subtle, animated on hover) ============== */
function Waveform() {
  // 32 bars, varying heights — animated by CSS on parent hover
  const bars = React.useMemo(
    () => Array.from({ length: 36 }, (_, i) => 1.0 + Math.sin(i * 0.7) * 0.5 + Math.cos(i * 1.3) * 0.4),
    []
  );
  return (
    <div className="ajl-wave" aria-hidden="true">
      {bars.map((h, i) => (
        <span
          key={i}
          className="ajl-wave__bar"
          style={{
            height: `${Math.max(0.2, h) * 30}%`,
            animationDelay: `${i * 40}ms`,
          }}
        />
      ))}
    </div>
  );
}

/* ============== PEMBUKAAN — opening essay, horizontal hero strip ============== */
function PembukaanPanel({ essay, onSelect }) {
  return (
    <article
      className="ajl-penutup"
      onClick={() => onSelect(essay.no, essay)}
      role="button"
      tabIndex={0}
    >
      <div className="ajl-penutup__bg" aria-hidden="true">
        {essay.bgImage && <img src={essay.bgImage} alt="" />}
        <div className="ajl-penutup__bg-grain" />
        <div className="ajl-penutup__bg-protect" />
      </div>
      <div className="ajl-penutup__inner">
        <div className="ajl-penutup__left">
          <div className="ajl-eyebrow ajl-penutup__eyebrow">Pembukaan · Bacaan Pembukaan</div>
          <h3 className="ajl-penutup__title">{essay.title}</h3>
          <p className="ajl-penutup__sub">{essay.subtitle}</p>
        </div>
        <div className="ajl-penutup__right">
          <div className="ajl-penutup__meta t-mono">
            {essay.readMin} MIN · BACAAN PEMBUKAAN
          </div>
          <button className="ajl-btn ajl-btn--ruby" onClick={(ev) => { ev.stopPropagation(); onSelect(essay.no, essay); }}>
            Baca Pembukaan<Icon name="arrow-right" size={14} />
          </button>
        </div>
      </div>
      <Waveform />
    </article>
  );
}

/* ============== PENUTUP — closing essay, horizontal hero strip ============== */
function PenutupPanel({ essay, onSelect }) {
  return (
    <article
      className="ajl-penutup"
      onClick={() => onSelect(essay.no, essay)}
      role="button"
      tabIndex={0}
    >
      <div className="ajl-penutup__bg" aria-hidden="true">
        {essay.bgImage && <img src={essay.bgImage} alt="" />}
        <div className="ajl-penutup__bg-grain" />
        <div className="ajl-penutup__bg-protect" />
      </div>
      <div className="ajl-penutup__inner">
        <div className="ajl-penutup__left">
          <div className="ajl-eyebrow ajl-penutup__eyebrow">Penutup · Esei {essay.no}</div>
          <h3 className="ajl-penutup__title">{essay.title}</h3>
          <p className="ajl-penutup__sub">{essay.subtitle}</p>
        </div>
        <div className="ajl-penutup__right">
          <div className="ajl-penutup__meta t-mono">
            {essay.readMin} MIN · BACAAN PENUTUP
          </div>
          <button className="ajl-btn ajl-btn--ruby" onClick={(ev) => { ev.stopPropagation(); onSelect(essay.no, essay); }}>
            Baca Penutup<Icon name="arrow-right" size={14} />
          </button>
        </div>
      </div>
      <Waveform />
    </article>
  );
}

/* ============== ESSAY INDEX (legacy: vertical list) ============== */
function EssayIndex({ essays, activeNo, onSelect }) {
  return (
    <section className="ajl-section" id="esei">
      <div className="ajl-container">
        <div className="ajl-section__head">
          <div className="ajl-eyebrow">Senarai Esei · 13 Cluster Naratif</div>
          <h2 className="ajl-section__title">
            <span>Enam belas esei.</span><br />
            <em className="ajl-section__title-em">Naratif muzik kita.</em>
          </h2>
          <p className="ajl-section__lede">
            Dari ruang tamu pada malam Sabtu, ke fanbase digital. Dari trofi pertama 1986, ke
            soalan tentang AI dan masa depan. Setiap esei membaca satu sudut AJL — dan, melalui AJL,
            satu sudut Malaysia.
          </p>
        </div>

        <ol className="ajl-essay-list">
          {essays.map((e) => (
            <li
              key={e.no}
              className={`ajl-essay-row ${activeNo === e.no ? "is-active" : ""} ${e.bgImage ? "has-bg" : ""}`}
              onClick={() => onSelect(e.no, e)}
              role="button"
              tabIndex={0}
            >
              {e.bgImage && (
                <div className="ajl-essay-row__bg" aria-hidden="true">
                  <img src={e.bgImage} alt="" />
                  <div className="ajl-essay-row__bg-grain" />
                  <div className="ajl-essay-row__bg-protect" />
                </div>
              )}
              <div className="ajl-essay-row__no">{e.no}</div>
              <div className="ajl-essay-row__body">
                <div className="ajl-essay-row__cluster">{e.cluster}</div>
                <h3 className="ajl-essay-row__title">{e.title}</h3>
                <p className="ajl-essay-row__sub">{e.subtitle}</p>
              </div>
              <div className="ajl-essay-row__meta">
                <span className="t-mono">{e.readMin} min</span>
                <span className="ajl-essay-row__level">{e.level}</span>
              </div>
              <div className="ajl-essay-row__cta">
                <Icon name="arrow-right" size={18} />
              </div>
            </li>
          ))}
        </ol>
      </div>
    </section>
  );
}

/* ============== PULL QUOTE BAND (3 stacked) ============== */
function PullQuoteBand({ quotes }) {
  return (
    <section className="ajl-quote-band" id="petikan" data-surface="bone">
      <div className="ajl-container ajl-quote-band__inner">
        <div className="ajl-eyebrow ajl-quote-band__eyebrow" style={{color: "var(--gold-deep)"}}>
          Petikan · Daripada Esei 01
        </div>
        <div className="ajl-quote-band__big">
          <span className="ajl-quote-band__mark">"</span>
          <blockquote className="ajl-quote-band__body">{quotes[0].body}</blockquote>
          <div className="ajl-quote-band__attr">{quotes[0].attribution}</div>
        </div>

        <div className="ajl-quote-band__pair">
          {quotes.slice(1).map((q, i) => (
            <figure className="ajl-quote-band__small" key={i}>
              <blockquote className="ajl-quote-band__sbody">{q.body}</blockquote>
              <figcaption className="ajl-quote-band__sattr">{q.attribution}</figcaption>
            </figure>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============== READER EXCERPT (bone, long-form sample) ============== */
function ReaderExcerpt({ data, onContinue }) {
  return (
    <section className="ajl-reader" id="bacaan" data-surface="bone">
      <div className="ajl-reader__inner">
        <div className="ajl-eyebrow ajl-reader__eyebrow">{data.eyebrow}</div>
        <div className="ajl-reader__cluster">{data.cluster}</div>
        <h2 className="ajl-reader__title">{data.title}</h2>

        <div className="ajl-reader__byline t-mono">
          <Icon name="clock" size={12} /> {data.readingTime}
        </div>

        <hr className="ajl-reader__rule" />

        <p className="ajl-reader__standfirst">{data.standfirst}</p>

        {data.paragraphs.map((p, i) => (
          <p className="ajl-reader__p" key={i}>
            {i === 1 ? <strong className="ajl-reader__pullout">{p}</strong> : p}
          </p>
        ))}

        <div className="ajl-reader__continue">
          <button className="ajl-btn ajl-btn--primary ajl-btn--on-bone" onClick={onContinue}>
            Teruskan Bacaan
            <Icon name="arrow-right" size={14} />
          </button>
          <span className="ajl-reader__progress t-mono">3 / 18 minit</span>
        </div>
      </div>
    </section>
  );
}

/* ============== ERA GALLERY (archive photographs) ============== */
function EraGallery() {
  const eras = [
    { src: "./assets/era/decade-1-perintis.jpg", year: "1986–1995", label: "Dekad Perintis, Legenda Dicipta", caption: "The birth of Malaysian pop identity and cultural optimism where melody met nationhood." },
    { src: "./assets/era/decade-2-prasasti.jpg", year: "1996–2005", label: "Prasasti Emas, Budaya Diteroka", caption: "The rise of pop royalty; the peak of Irama Malaysia and cultural fusion; and adapting to Reality TV trend." },
    { src: "./assets/era/decade-3-revolusi.jpg", year: "2006–2015", label: "Mencetus Revolusi, Gelombang Digital", caption: "The disruption of the mainstream, the rise of indie authenticity and digital platforms, and the diversification of genres." },
    { src: "./assets/era/decade-4-global.jpg", year: "2016–2025", label: "Melakar Visual, Menggapai Global", caption: "The redefinition of Malaysian pop on the global stage, where performance art meets digital expression." },
  ];
  return (
    <section className="ajl-eragallery" id="arkib">
      <div className="ajl-container">
        <div className="ajl-section__head">
          <div className="ajl-eyebrow">Arkib Visual · Empat Babak</div>
          <h2 className="ajl-section__title">
            <span>Dunia kreatif,</span><br />
            <em className="ajl-section__title-em">empat rupa pentas.</em>
          </h2>
          <p className="ajl-section__lede">
            Pakaian, prop, lampu, gaya rambut, cara pegang mikrofon — setiap dekad AJL
            membawa estetika sendiri. Imejan arkib ini menjadi tulang belakang visual
            siri digital.
          </p>
        </div>
        <div className="ajl-eragrid">
          {eras.map((e) => (
            <figure className="ajl-erafig" key={e.year}>
              <div className="ajl-erafig__photo">
                <img src={e.src} alt={`Arkib AJL ${e.year}`} />
                <div className="ajl-erafig__grain" />
              </div>
              <figcaption className="ajl-erafig__cap">
                <div className="ajl-erafig__head">
                  <span className="ajl-erafig__year">{e.year}</span>
                </div>
                <div className="ajl-erafig__label">{e.label}</div>
                <p className="ajl-erafig__caption">{e.caption}</p>
              </figcaption>
            </figure>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============== RADAR KONSENSUS — Data & Visualisasi ============== */
function RadarConsensus() {
  const llms = ['Perplexity','Claude','Gemini','Z.ai','Grok'];
  const ranges = {Perplexity:{min:39.4,max:64.6},Claude:{min:61.8,max:71.5},Gemini:{min:63,max:72.9},'Z.ai':{min:67.4,max:77.5},Grok:{min:68.3,max:75.1}};
  const songs = [
    {id:'cindai',year:1998,title:'Cindai',artist:'Siti Nurhaliza',genre:'Traditional Malay',models:{Perplexity:{rank:1,score:64.6},Claude:{rank:2,score:65.3},Gemini:{rank:2,score:71},
      'Z.ai':{rank:1,score:77.5},Grok:{rank:1,score:75.1}},mentions:5,first_votes:3,hybrid:89.69,rank_avg:96,norm_avg:83.38,raw_avg:70.7},
    {id:'tanya',year:1994,title:'Tanya Sama Itu Hud Hud',artist:'M. Nasir',genre:'World Fusion',models:{Perplexity:{rank:2,score:58.4},Claude:{rank:1,score:71.5},Gemini:{rank:1,score:72.9},
      'Z.ai':{rank:2,score:77},Grok:{rank:3,score:71.6}},mentions:5,first_votes:2,hybrid:87.9,rank_avg:92,norm_avg:83.8,raw_avg:70.28},
    {id:'ghazal',year:1997,title:'Ghazal Untuk Rabiah',artist:'M. Nasir & Jamal Abdillah',genre:'World Fusion',models:{Claude:{rank:3.5,score:62.2},
      'Z.ai':{rank:3,score:74.1}},mentions:2,first_votes:0,hybrid:22.55,rank_avg:31,norm_avg:14.09,raw_avg:68.15},
    {id:'isabella',year:1989,title:'Isabella',artist:'Search',genre:'Rock Ballad',models:{Claude:{rank:5,score:62},
      'Z.ai':{rank:5,score:67.4},Grok:{rank:4,score:69.5}},mentions:3,first_votes:0,hybrid:20.97,rank_avg:38,norm_avg:3.94,raw_avg:66.3},
    {id:'terlalu',year:2006,title:'Terlalu Istimewa',artist:'Adibah Noor',genre:'Pop Ballad',models:{Perplexity:{rank:3,score:47.1},Gemini:{rank:3,score:64.5}},mentions:2,first_votes:0,hybrid:20.57,rank_avg:32,norm_avg:9.14,raw_avg:55.8},
    {id:'terushidup',year:2022,title:'Terus Hidup',artist:'Aina Abdul',genre:'Power Ballad',models:{Grok:{rank:2,score:74.1}},mentions:1,first_votes:0,hybrid:17.53,rank_avg:18,norm_avg:17.06,raw_avg:74.1},
    {id:'gemilang',year:2005,title:'Gemilang',artist:'Jaclyn Victor',genre:'Power Ballad',models:{Perplexity:{rank:5,score:39.4},
      'Z.ai':{rank:4,score:71.4}},mentions:2,first_votes:0,hybrid:16.96,rank_avg:26,norm_avg:7.92,raw_avg:55.4},
    {id:'pelukan',year:2021,title:'Pelukan Angkasa',artist:'SOG feat. Shila Amzah',genre:'Modern Rock',models:{Perplexity:{rank:4,score:47}},mentions:1,first_votes:0,hybrid:10.02,rank_avg:14,norm_avg:6.03,raw_avg:47},
    {id:'gila',year:2020,title:'Gila',artist:'Kaka Azraff, Noki, Loca B',genre:'Hip-Hop',models:{Gemini:{rank:4,score:64.2}},mentions:1,first_votes:0,hybrid:8.21,rank_avg:14,norm_avg:2.42,raw_avg:64.2},
    {id:'andalusia',year:1999,title:'Andalusia',artist:'M. Nasir',genre:'World Fusion',models:{Claude:{rank:3.5,score:62.2}},mentions:1,first_votes:0,hybrid:7.91,rank_avg:15,norm_avg:0.82,raw_avg:62.2},
    {id:'terukir',year:2012,title:'Terukir Di Bintang',artist:'Yuna',genre:'Indie/Alt',models:{Grok:{rank:5,score:68.3}},mentions:1,first_votes:0,hybrid:6,rank_avg:12,norm_avg:0,raw_avg:68.3},
    {id:'awannano',year:2011,title:'Awan Nano',artist:'Hafiz Suip',genre:'Pop Ballad',models:{Gemini:{rank:5,score:63}},mentions:1,first_votes:0,hybrid:6,rank_avg:12,norm_avg:0,raw_avg:63},
    {id:'hasrat',year:2023,title:'Hasrat',artist:'Amir Jahari',genre:'Folk',models:{Claude:{rank:6,score:61.8}},mentions:1,first_votes:0,hybrid:5,rank_avg:10,norm_avg:0,raw_avg:61.8},
  ];
  const palette = ['#C9A84C','#C0392B','#7bdff2','#b2f7ef','#f7d6e0','#cdb4db','#a0c4ff','#fdffb6','#caffbf','#ffd6a5','#ffadad','#9bf6ff','#bdb2ff'];

  const [selected, setSelected] = useState(() => new Set(['cindai','tanya']));
  const [mode, setMode] = useState('hybrid');
  const [hoveredSong, setHoveredSong] = useState(null);

  const rankSupport = (rank) => { if(rank==null) return 0; return Math.max(0,110-(10*Number(rank))); };
  const normScore = (model, score) => {
    const r = ranges[model]; if(score==null||!r) return 0;
    if(r.max===r.min) return 100;
    return 100*(Number(score)-r.min)/(r.max-r.min);
  };
  const valueFor = (song, model) => {
    const item = song.models[model]; if(!item) return 0;
    if(mode==='rank') return rankSupport(item.rank);
    if(mode==='raw') return item.score||0;
    if(mode==='norm') return normScore(model, item.score);
    return 0.5*rankSupport(item.rank)+0.5*normScore(model, item.score);
  };
  const fmt = (n,dp) => n==null||isNaN(n) ? '—' : Number(n).toFixed(dp||1);

  const toggleSong = (id) => {
    setSelected(prev => { const s = new Set(prev); if(s.has(id)) s.delete(id); else s.add(id); return s; });
  };
  const selectPreset = (type) => {
    const s = new Set();
    songs.forEach((song,i) => {
      if(type==='leaders' && i<2) s.add(song.id);
      if(type==='top5' && i<5) s.add(song.id);
      if(type==='multi' && song.mentions>=2) s.add(song.id);
      if(type==='all') s.add(song.id);
    });
    setSelected(s);
  };

  // SVG radar drawing
  const cx=200, cy=200, radius=150, maxVal=100, n=llms.length;
  const polar = (r,i) => {
    const angle = -Math.PI/2+(2*Math.PI*i/n);
    return [cx+r*Math.cos(angle), cy+r*Math.sin(angle)];
  };
  const polyPoints = (values) => values.map((v,i) => polar(radius*Math.max(0,Math.min(maxVal,v))/maxVal,i).join(',')).join(' ');

  const gridTicks = [20,40,60,80,100];

  const cardBg = { backgroundColor: '#161616' };
  const border = '1px solid rgba(255,255,255,0.08)';

  return (
    <div style={{ ...cardBg, borderRadius: '0.375rem', border, overflow: 'hidden', marginBottom: '1.5rem' }}>
      {/* Card Header */}
      <div style={{ padding: '1.5rem 1.5rem 1rem', borderBottom: border }}>
        <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '0.75rem', letterSpacing: '0.15em', textTransform: 'uppercase', color: '#C9A84C', marginBottom: '0.25rem' }}>Radar Konsensus AI</p>
        <p style={{ fontFamily: 'var(--f-body,serif)', fontSize: '0.75rem', color: 'rgba(255,255,255,0.45)', lineHeight: 1.5 }}>
          Lima model AI diminta memilih Juara Lagu Sepanjang Zaman. Radar ini menunjukkan konsensus merentasi <span style={{ color: '#F5F0E8' }}>13 lagu</span> · 5 LLM.
        </p>
      </div>

      <div style={{ padding: '1.5rem' }}>
        {/* Callout metrics */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: '0.75rem', marginBottom: '1.5rem' }}>
          {[{label:'Pemenang Konsensus',value:songs[0].title,sub:'Hybrid '+fmt(songs[0].hybrid)+' · '+songs[0].first_votes+' undi #1',highlight:true},
            {label:'Pesaing Terdekat',value:songs[1].title,sub:'Hybrid '+fmt(songs[1].hybrid)+' · '+songs[1].first_votes+' undi #1'},
            {label:'Perplexity #1',value:'Cindai',sub:'Skor 64.6 · Kemas kini terkini'}].map((m,i) => (
            <div key={i} style={{ padding: '0.875rem', borderRadius: '0.75rem', border: m.highlight ? '1px solid rgba(201,168,76,0.4)' : border,
              background: m.highlight ? 'linear-gradient(135deg,rgba(201,168,76,0.12),rgba(192,57,43,0.08))' : 'rgba(255,255,255,0.025)' }}>
              <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.12em', textTransform: 'uppercase', color: '#666', margin: '0 0 0.375rem' }}>{m.label}</p>
              <p style={{ fontFamily: 'var(--f-heading,serif)', fontSize: '1rem', fontWeight: 700, color: '#F5F0E8', margin: '0 0 0.25rem' }}>{m.value}</p>
              <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', color: 'rgba(255,255,255,0.4)', margin: 0 }}>{m.sub}</p>
            </div>
          ))}
        </div>

        {/* Controls row */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem', marginBottom: '1rem' }}>
          <div>
            <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.12em', textTransform: 'uppercase', color: '#666', margin: '0 0 0.375rem' }}>Mod carta</p>
            <select value={mode} onChange={e=>setMode(e.target.value)} style={{
              width: '100%', fontFamily: 'var(--f-label,monospace)', fontSize: '0.7rem', color: '#F5F0E8',
              background: 'rgba(255,255,255,0.05)', border, borderRadius: '0.5rem', padding: '0.5rem 0.625rem', outline: 'none' }}>
              <option value="hybrid">Hybrid Consensus</option>
              <option value="norm">Normalised Score</option>
              <option value="rank">Rank Support</option>
              <option value="raw">Raw Score</option>
            </select>
          </div>
          <div>
            <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.12em', textTransform: 'uppercase', color: '#666', margin: '0 0 0.375rem' }}>Pilihan pantas</p>
            <div style={{ display: 'flex', gap: '0.375rem', flexWrap: 'wrap' }}>
              {[{k:'leaders',l:'2 Teratas'},{k:'top5',l:'Top 5'},{k:'multi',l:'2+ LLM'},{k:'all',l:'Semua'}].map(p => (
                <button key={p.k} onClick={() => selectPreset(p.k)} style={{
                  fontFamily: 'var(--f-label,monospace)', fontSize: '10px', color: '#F5F0E8', letterSpacing: '0.05em',
                  background: 'rgba(255,255,255,0.05)', border, borderRadius: '0.375rem', padding: '0.375rem 0.625rem', cursor: 'pointer' }}>{p.l}</button>
              ))}
            </div>
          </div>
        </div>

        {/* Main layout: radar + song list */}
        <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr', gap: '1rem', alignItems: 'start' }}>
          {/* Radar SVG */}
          <div style={{ position: 'relative', borderRadius: '0.75rem', background: 'radial-gradient(circle at 50% 50%, rgba(201,168,76,0.06), transparent 14rem)', padding: '0.5rem' }}>
            <svg viewBox="0 0 400 400" style={{ width: '100%', height: 'auto', display: 'block' }}>
              {/* Grid rings */}
              {gridTicks.map(tick => (
                <polygon key={tick}
                  points={llms.map((_,i)=>polar(radius*tick/maxVal,i).join(',')).join(' ')}
                  fill="none" stroke="rgba(255,255,255,0.1)" strokeWidth="1" />
              ))}
              {/* Axis lines + labels */}
              {llms.map((m,i) => {
                const [ex,ey] = polar(radius,i);
                const [lx,ly] = polar(radius+28,i);
                return (
                  <g key={m}>
                    <line x1={cx} y1={cy} x2={ex} y2={ey} stroke="rgba(255,255,255,0.12)" strokeWidth="1" />
                    <text x={lx} y={ly} textAnchor={Math.abs(lx-cx)<15?'middle':(lx>cx?'start':'end')}
                      dominantBaseline="middle" fill="#F5F0E8" fontFamily="var(--f-label,monospace)" fontSize="10" fontWeight="700">{m}</text>
                  </g>
                );
              })}
              {/* Tick labels */}
              {gridTicks.map(tick => {
                const [tx,ty] = polar(radius*tick/maxVal,0);
                return <text key={tick} x={tx+6} y={ty+3} fill="rgba(255,255,255,0.35)" fontFamily="var(--f-label,monospace)" fontSize="8">{tick}</text>;
              })}
              {/* Song polygons */}
              {songs.map((s,idx) => {
                if(!selected.has(s.id)) return null;
                const values = llms.map(m => valueFor(s,m));
                const color = palette[idx % palette.length];
                return (
                  <g key={s.id} onMouseEnter={() => setHoveredSong(s)} onMouseLeave={() => setHoveredSong(null)}>
                    <polygon points={polyPoints(values)} fill={color} fillOpacity={hoveredSong && hoveredSong.id===s.id ? 0.28 : 0.14}
                      stroke={color} strokeWidth={hoveredSong && hoveredSong.id===s.id ? 3 : 2} style={{ transition: 'all 0.2s', cursor: 'pointer' }} />
                    {values.map((v,i) => {
                      const [px,py] = polar(radius*Math.max(0,Math.min(maxVal,v))/maxVal,i);
                      return <circle key={i} cx={px} cy={py} r={4} fill={color} stroke="#0D0D0D" strokeWidth="1.5" style={{ cursor: 'pointer' }} />;
                    })}
                  </g>
                );
              })}
            </svg>
            {/* Tooltip */}
            {hoveredSong && (
              <div style={{ position: 'absolute', top: '1rem', left: '1rem', padding: '0.75rem', borderRadius: '0.75rem', border: '1px solid rgba(201,168,76,0.3)',
                background: 'rgba(13,13,13,0.95)', fontSize: '0.7rem', lineHeight: 1.5, pointerEvents: 'none', maxWidth: '220px', zIndex: 10 }}>
                <p style={{ fontFamily: 'var(--f-heading,serif)', fontWeight: 700, color: '#C9A84C', margin: '0 0 0.25rem' }}>{hoveredSong.title}</p>
                <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', color: 'rgba(255,255,255,0.5)', margin: '0 0 0.375rem' }}>{hoveredSong.year} · {hoveredSong.artist}</p>
                <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', color: '#F5F0E8', margin: 0 }}>
                  Hybrid: {fmt(hoveredSong.hybrid)} · Rank: {fmt(hoveredSong.rank_avg)} · Norm: {fmt(hoveredSong.norm_avg)}
                </p>
              </div>
            )}
          </div>

          {/* Song list */}
          <div>
            <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', letterSpacing: '0.1em', textTransform: 'uppercase', color: '#666', margin: '0 0 0.75rem' }}>Siri Lagu · Pilih untuk bandingkan</p>
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem', maxHeight: '360px', overflowY: 'auto', paddingRight: '0.25rem' }}>
              {songs.map((s,idx) => {
                const color = palette[idx % palette.length];
                const isOn = selected.has(s.id);
                return (
                  <div key={s.id} onClick={() => toggleSong(s.id)} style={{
                    display: 'grid', gridTemplateColumns: '10px 1fr auto', gap: '0.5rem', alignItems: 'center',
                    padding: '0.5rem 0.625rem', borderRadius: '0.625rem', cursor: 'pointer',
                    border: isOn ? '1px solid '+color+'66' : '1px solid rgba(255,255,255,0.05)',
                    background: isOn ? color+'0D' : 'rgba(255,255,255,0.02)', transition: 'all 0.2s' }}>
                    <div style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: isOn ? color : 'rgba(255,255,255,0.15)' }} />
                    <div>
                      <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '11px', fontWeight: 600, color: '#F5F0E8', margin: 0 }}>{s.title}</p>
                      <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', color: 'rgba(255,255,255,0.35)', margin: 0 }}>{s.year} · {s.artist}</p>
                    </div>
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', fontWeight: 800, color: '#C9A84C',
                      border: '1px solid rgba(201,168,76,0.25)', borderRadius: '999px', padding: '0.25rem 0.5rem', minWidth: '40px', textAlign: 'center' }}>{fmt(s.hybrid)}</span>
                  </div>
                );
              })}
            </div>
          </div>
        </div>

        {/* Bottom note */}
        <div style={{ marginTop: '1rem', borderLeft: '3px solid #C9A84C', background: 'rgba(201,168,76,0.06)', padding: '0.75rem 1rem', borderRadius: '0.75rem' }}>
          <p style={{ fontFamily: 'var(--f-body,serif)', fontSize: '0.7rem', color: 'rgba(255,255,255,0.45)', lineHeight: 1.55, margin: 0 }}>
            <strong style={{ color: '#C9A84C' }}>Cindai</strong> muncul sebagai pemenang konsensus — #1 dalam Perplexity, Z.ai dan Grok, #2 dalam Claude dan Gemini. <strong style={{ color: '#C9A84C' }}>Tanya Sama Itu Hud Hud</strong> kekal kuat (#1 Claude & Gemini) tetapi profil radar Cindai lebih stabil dan luas.
          </p>
        </div>
      </div>
    </div>
  );
}

/* ============== GENRE FLOW — Data & Visualisasi ============== */
function GenreFlow() {
  const sectionStyle = { backgroundColor: '#0D0D0D', borderTop: '1px solid rgba(255,255,255,0.08)', borderBottom: '1px solid rgba(255,255,255,0.08)' };
  return (
    <section style={{ ...sectionStyle, padding: 'clamp(4rem,8vw,8rem) 0' }}>
      <div className="ajl-container">
        {/* Section Header */}
        <div className="ajl-section__head" style={{ marginBottom: '3.5rem' }}>
          <div className="ajl-eyebrow">Data & Visualisasi</div>
          <h2 className="ajl-section__title">
            <span>AJL dalam angka.</span><br />
            <em className="ajl-section__title-em">Empat dekad, satu peta.</em>
          </h2>
          <p className="ajl-section__lede" style={{ maxWidth: '32rem' }}>
            Data editorial tentang evolusi genre, tren pemenang, dan perjalanan budaya AJL selama empat dekad.
          </p>
        </div>

        {/* Radar Konsensus Card */}
        <RadarConsensus />

        {/* Genre Flow Card */}
        <GenreFlowCard />
      </div>
    </section>
  );
}

function GenreFlowCard() {
  const [activeDekad, setActiveDekad] = useState(null);
  const [activeGenre, setActiveGenre] = useState(null);

  const genres = [
    { key: 'ballad', label: 'Ballad', color: '#C9A84C', sub: 'Ballad, Power Ballad, Pop Ballad' },
    { key: 'rock',   label: 'Rock',   color: '#C0392B', sub: 'Rock, Ballad, Pop, Modern, Alternative, Slow' },
    { key: 'tradisi',label: 'Tradisi / Fusion', color: '#2D8A7A', sub: 'Traditional Malay, World Fusion, Folk' },
    { key: 'hiphop', label: 'Hip Hop', color: '#4A7B9D', sub: 'Hip Hop' },
  ];

  const decades = [
    { id: 1, label: 'Dekad 1', years: '1986–1995', data: { ballad: 6, rock: 3, tradisi: 1, hiphop: 0 }, total: 10 },
    { id: 2, label: 'Dekad 2', years: '1996–2005', data: { ballad: 3, rock: 2, tradisi: 5, hiphop: 0 }, total: 10 },
    { id: 3, label: 'Dekad 3', years: '2006–2015', data: { ballad: 3, rock: 3, tradisi: 3, hiphop: 1 }, total: 10 },
    { id: 4, label: 'Dekad 4', years: '2016–2024', data: { ballad: 4, rock: 3, tradisi: 1, hiphop: 1 }, total: 9 },
  ];

  const genreTotals = {};
  genres.forEach(g => {
    genreTotals[g.key] = decades.reduce((s, d) => s + d.data[g.key], 0);
  });

  const toggleDekad = (id) => { setActiveGenre(null); setActiveDekad(activeDekad === id ? null : id); };
  const toggleGenre = (key) => { setActiveDekad(null); setActiveGenre(activeGenre === key ? null : key); };

  const isDimmedRow = (dekadId) => activeDekad !== null && activeDekad !== dekadId;
  const isDimmedSeg = (dekadId, genreKey) => {
    if (activeDekad !== null && activeDekad !== dekadId) return true;
    if (activeGenre !== null && activeGenre !== genreKey) return true;
    return false;
  };

  const cardBg = { backgroundColor: '#161616' };

  return (
        <div style={{ ...cardBg, borderRadius: '0.375rem', border: '1px solid rgba(255,255,255,0.08)', overflow: 'hidden', marginBottom: '1.5rem' }}>
          {/* Card Header */}
          <div style={{ padding: '1.5rem 1.5rem 1rem', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
            <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '0.75rem', letterSpacing: '0.15em', textTransform: 'uppercase', color: '#C9A84C', marginBottom: '0.25rem' }}>Aliran Genre Pemenang</p>
            <p style={{ fontFamily: 'var(--f-body,serif)', fontSize: '0.75rem', color: 'rgba(255,255,255,0.45)', lineHeight: 1.5 }}>
              Klik dekad atau genre untuk menyerlahkan data. Jumlah: <span style={{ color: '#F5F0E8' }}>39 pemenang</span> · 4 dekad · 4 genre utama.
            </p>
          </div>

          <div style={{ padding: '1.5rem' }}>
            {/* Decade Buttons */}
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '1.5rem' }}>
              {decades.map(d => {
                const isActive = activeDekad === d.id;
                return (
                  <button key={d.id} onClick={() => toggleDekad(d.id)}
                    style={{
                      display: 'flex', flexDirection: 'column', alignItems: 'flex-start',
                      padding: '0.5rem 0.75rem', borderRadius: '0.375rem',
                      border: isActive ? '1px solid #C9A84C' : '1px solid rgba(255,255,255,0.1)',
                      backgroundColor: isActive ? 'rgba(201,168,76,0.08)' : 'transparent',
                      cursor: 'pointer', transition: 'all 0.2s'
                    }}>
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.2em', textTransform: 'uppercase', color: isActive ? '#C9A84C' : '#666' }}>{d.label}</span>
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '0.75rem', color: '#F5F0E8' }}>{d.years}</span>
                  </button>
                );
              })}
            </div>

            {/* Stacked Bar Rows */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '1.5rem' }}>
              {decades.map(d => (
                <div key={d.id} style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', cursor: 'pointer', opacity: isDimmedRow(d.id) ? 0.25 : 1, transition: 'opacity 0.3s' }}
                  onClick={() => toggleDekad(d.id)}>
                  <div style={{ width: '4rem', flexShrink: 0 }}>
                    <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.1em', textTransform: 'uppercase', color: '#666', margin: 0 }}>{d.label}</p>
                    <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', color: 'rgba(255,255,255,0.45)', margin: 0 }}>{d.years}</p>
                  </div>
                  <div style={{ flex: 1, display: 'flex', gap: '2px', height: '2rem', borderRadius: '0.375rem', overflow: 'hidden' }}>
                    {genres.map(g => {
                      const val = d.data[g.key];
                      if (val === 0) return null;
                      const pct = (val / d.total) * 100;
                      const dimmed = isDimmedSeg(d.id, g.key);
                      return (
                        <div key={g.key}
                          title={g.label + ': ' + val}
                          style={{
                            width: pct + '%', backgroundColor: g.color,
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            opacity: dimmed ? 0.15 : 1,
                            transition: 'opacity 0.3s'
                          }}>
                          {val >= 2 && <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', fontWeight: 700, color: 'rgba(255,255,255,0.8)', userSelect: 'none' }}>{val}</span>}
                        </div>
                      );
                    })}
                  </div>
                  <div style={{ width: '2rem', flexShrink: 0, textAlign: 'right' }}>
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '0.75rem', color: 'rgba(255,255,255,0.45)' }}>{d.total}</span>
                  </div>
                </div>
              ))}
            </div>

            {/* Genre Legend Buttons */}
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '1.5rem' }}>
              {genres.map(g => {
                const isActive = activeGenre === g.key;
                return (
                  <button key={g.key} onClick={() => toggleGenre(g.key)}
                    style={{
                      display: 'flex', alignItems: 'center', gap: '0.5rem',
                      padding: '0.375rem 0.75rem', borderRadius: '0.375rem',
                      border: isActive ? '1px solid ' + g.color : '1px solid rgba(255,255,255,0.1)',
                      backgroundColor: isActive ? g.color + '14' : 'transparent',
                      cursor: 'pointer', transition: 'all 0.2s'
                    }}>
                    <div style={{ width: '0.625rem', height: '0.625rem', borderRadius: '2px', flexShrink: 0, backgroundColor: g.color }} />
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', letterSpacing: '0.05em', color: 'rgba(255,255,255,0.45)' }}>{g.label}</span>
                    <span style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '10px', fontWeight: 700, color: g.color }}>{genreTotals[g.key]}</span>
                  </button>
                );
              })}
            </div>

            {/* Summary Stats */}
            <div style={{ borderTop: '1px solid rgba(255,255,255,0.08)', paddingTop: '1rem' }}>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '1rem' }}>
                {genres.map(g => (
                  <div key={g.key} style={{ textAlign: 'center' }}>
                    <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', letterSpacing: '0.15em', textTransform: 'uppercase', color: '#666', margin: '0 0 0.25rem' }}>{g.label}</p>
                    <p style={{ fontFamily: 'var(--f-heading,serif)', fontSize: '1.5rem', fontWeight: 700, color: g.color, margin: '0 0 0.125rem' }}>{genreTotals[g.key]}</p>
                    <p style={{ fontFamily: 'var(--f-label,monospace)', fontSize: '9px', color: 'rgba(255,255,255,0.35)', margin: 0 }}>pemenang</p>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
  );
}

/* ============== VIDEO REEL (looping archive footage) ============== */
function VideoReel() {
  return (
    <section className="ajl-reel">
      <div className="ajl-reel__video">
        <video src="./assets/era/loop.mp4" autoPlay muted loop playsInline />
        <div className="ajl-reel__grain" />
        <div className="ajl-reel__protect" />
      </div>
      <div className="ajl-container ajl-reel__inner">
        <div className="ajl-eyebrow">Bahan Arkib Dunia Kreatif</div>
        <h2 className="ajl-reel__title">
          <em>Pentas ini masih bersuara.</em>
        </h2>
        <p className="ajl-reel__body">
          Setiap arkib membawa kita kembali ke malam tertentu, ke ruang tamu tertentu
          dan ke ingatan tertentu.
        </p>
        <button className="ajl-btn ajl-btn--outline" onClick={() => { window.location.href = '/archive/'; }}>
          LIHAT ARKIB PENUH
          <Icon name="arrow-right" size={14} />
        </button>
      </div>
    </section>
  );
}

/* ============== FOOTER ============== */
function Footer() {
  return (
    <footer className="ajl-footer" id="tentang">
      <div className="ajl-container ajl-footer__inner">
        <div className="ajl-footer__col">
          <a href="/" className="ajl-wordmark ajl-footer__mark" aria-label="AJL 40">
            <img className="ajl-wordmark__logo" src="./assets/logo.png" alt="AJL 40" />
          </a>
          <p className="ajl-footer__brand">
            <em>Dunia Kreatif<br />Budaya Sebuah Negara</em>
          </p>
          <div className="ajl-footer__credits t-mono">
            SIRI DIGITAL · 2026<br />
            EDISI KE-40 ANUGERAH JUARA LAGU
          </div>
        </div>

        <div className="ajl-footer__col">
          <div className="ajl-eyebrow">Nota Editorial</div>
          <p className="ajl-footer__body" style={{fontSize:'0.85rem',lineHeight:1.6,opacity:0.85,maxWidth:'38ch'}}>AJL40 Digital Series ialah inisiatif editorial bebas oleh sultanmuzaffar. Portal ini tidak mewakili penganjur Anugerah Juara Lagu, TV3 atau Media Prima. Semua rujukan digunakan dalam konteks ulasan budaya, penyelidikan terbuka dan penghargaan terhadap sejarah muzik popular Malaysia.</p>
          <div className="ajl-social-row" style={{marginTop:'16px'}}>
            <a className="ajl-social-row__btn" href="https://www.x.com/sultanmuzaffar" target="_blank" rel="noopener noreferrer" aria-label="X (Twitter)" title="X (Twitter)">
              <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
            </a>
            <a className="ajl-social-row__btn" href="https://www.instagram.com/sultanmuzaffar" target="_blank" rel="noopener noreferrer" aria-label="Instagram" title="Instagram">
              <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
            </a>
            <a className="ajl-social-row__btn" href="https://www.facebook.com/muzaffar.sm.mustapa" target="_blank" rel="noopener noreferrer" aria-label="Facebook" title="Facebook">
              <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
            </a>
            <a className="ajl-social-row__btn" href="https://www.linkedin.com/in/sultanmuzaffar" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn" title="LinkedIn">
              <svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
            </a>
          </div>
        </div>

        <div className="ajl-footer__col">
          <div className="ajl-eyebrow">Layari</div>
          <div style={{marginTop:'0.5rem',display:'flex',flexDirection:'column',gap:'8px'}}>
            <a className="ajl-footer__link" href="/esei/" style={{color:'var(--ivory-80)',textDecoration:'none',fontSize:'0.9rem'}}>17 Esei Editorial</a>
            <a className="ajl-footer__link" href="/arkib/" style={{color:'var(--ivory-80)',textDecoration:'none',fontSize:'0.9rem'}}>Arkib Editorial</a>
            <a className="ajl-footer__link" href="/archive/" style={{color:'var(--ivory-80)',textDecoration:'none',fontSize:'0.9rem'}}>Arkib Digital</a>
          </div>
        </div>

        <div className="ajl-footer__col">
          <div className="ajl-eyebrow">Projek</div>
          <div style={{marginTop:'0.5rem',display:'flex',flexDirection:'column',gap:'8px'}}>
            <a className="ajl-footer__link" href="/tentang/" style={{color:'var(--ivory-80)',textDecoration:'none',fontSize:'0.9rem'}}>Tentang</a>
            <a className="ajl-footer__link" href="/kredit/" style={{color:'var(--ivory-80)',textDecoration:'none',fontSize:'0.9rem'}}>Kredit</a>
          </div>
        </div>
      </div>

      <div className="ajl-footer__bar">
        <div className="ajl-container ajl-footer__legal">
          <span className="t-mono">© 2026 SULTANMUZAFFAR · HAK CIPTA TERPELIHARA</span>
          <span className="t-mono" style={{opacity:0.6,fontSize:'0.7rem'}}>Semua kandungan, kod sumber dan reka bentuk dilindungi hak cipta. Penggunaan tanpa kebenaran adalah dilarang.</span>
        </div>
      </div>
    </footer>
  );
}

/* ============== BACK TO TOP ============== */
function BackToTop() {
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    const onScroll = () => setVisible(window.scrollY > 600);
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  if (!visible) return null;
  return (
    <button className="ajl-btt" aria-label="Kembali ke atas" title="Kembali ke atas"
      onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}>
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
        <polyline points="18 15 12 9 6 15" />
      </svg>
    </button>
  );
}

Object.assign(window, {
  Header, Hero, DariKacamata, VideoWall, FilterBar, Panel, PenutupPanel, Waveform,
  EssayIndex, EraGallery, RadarConsensus, GenreFlow, GenreFlowCard, PullQuoteBand, ReaderExcerpt, VideoReel, Footer, BackToTop
});