/* global React, Icon, WaveBars, AILabel, AlbumCover, LANGS */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// ---------- Shared catalog (used by search, trending, saved) ----------
const CATALOG = [
  { id: 'cassette-light',   t: 'Cassette Light',   a: 'Vela Maren',      g: 'Indie pop',     lang: 'EN', seed: 0, slug: 'vela-maren/cassette-light-meaning',     hot: true,  excerpt: 'Static is just love that didn\'t make it home' },
  { id: 'halo-driver',      t: 'Halo Driver',       a: 'Kestral',         g: 'Synthwave',     lang: 'EN', seed: 1, slug: 'kestral/halo-driver-meaning',            hot: true,  excerpt: 'Headlights argue with the rain' },
  { id: 'bintang-pulang',   t: 'Bintang Pulang',    a: 'Hara Mei',        g: 'Indo R&B',      lang: 'ID', seed: 2, slug: 'hara-mei/bintang-pulang-meaning',        hot: true,  excerpt: 'Pulang itu cuma arah, bukan tempat' },
  { id: 'glass-animal',     t: 'Glass Animal',      a: 'Mott Sun',        g: 'Bedroom pop',   lang: 'EN', seed: 3, slug: 'mott-sun/glass-animal-meaning',          hot: true,  excerpt: 'I am a window someone keeps forgetting' },
  { id: 'slow-tide',        t: 'Slow Tide',         a: 'Nori',            g: 'Ambient pop',   lang: 'EN', seed: 4, slug: 'nori/slow-tide-meaning',                 hot: false, excerpt: 'The sea was never in a hurry to want me' },
  { id: 'hotel-mileage',    t: 'Hotel Mileage',     a: 'June & Olive',    g: 'Folk',          lang: 'EN', seed: 5, slug: 'june-olive/hotel-mileage-meaning',       hot: true,  excerpt: 'We counted floors like they meant something' },
  { id: 'telegrama',        t: 'Telegrama',         a: 'Lía Vera',        g: 'Latin alt',     lang: 'ES', seed: 6, slug: 'lia-vera/telegrama-meaning',             hot: false, excerpt: 'Te escribo en clave de no volver' },
  { id: 'quiet-room',       t: 'Quiet Room',        a: 'The Soft Coast',  g: 'Dream pop',     lang: 'EN', seed: 7, slug: 'the-soft-coast/quiet-room-meaning',      hot: true,  excerpt: 'I rented silence by the hour' },
  { id: 'august-letter',    t: 'August Was a Letter', a: 'Vela Maren',    g: 'Indie pop',     lang: 'EN', seed: 5, slug: 'vela-maren/august-was-a-letter-meaning', hot: false, excerpt: 'I read the weather like it answered me' },
  { id: 'linoleum-bloom',   t: 'Linoleum Bloom',    a: 'Vela Maren',      g: 'Indie pop',     lang: 'EN', seed: 6, slug: 'vela-maren/linoleum-bloom-meaning',      hot: false, excerpt: 'Kitchen light makes saints of anyone' },
  { id: 'indoor-weather',   t: 'Indoor Weather',    a: 'Vela Maren',      g: 'Indie pop',     lang: 'EN', seed: 3, slug: 'vela-maren/indoor-weather-meaning',      hot: false, excerpt: 'The forecast on our ceiling said maybe' },
  { id: 'sayap-pagi',       t: 'Sayap Pagi',        a: 'Anjali Putri',    g: 'Indo folk',     lang: 'ID', seed: 2, slug: 'anjali-putri/sayap-pagi-meaning',        hot: false, excerpt: 'Pagi ini pun masih berani jadi pagi' },
  { id: 'medianoche-club',  t: 'Medianoche Club',   a: 'Río Sastre',      g: 'Latin pop',     lang: 'ES', seed: 6, slug: 'rio-sastre/medianoche-club-meaning',     hot: true,  excerpt: 'Bailamos como si el reloj nos debiera algo' },
  { id: 'paper-radio',      t: 'Paper Radio',       a: 'Albatross Phone', g: 'Indie rock',    lang: 'EN', seed: 4, slug: 'albatross-phone/paper-radio-meaning',    hot: false, excerpt: 'Tune the static, kiss the noise goodnight' },
];

const TRENDING_SEARCHES = [
  'cassette light meaning',
  'arti lirik bintang pulang',
  'medianoche club significado',
  'halo driver meaning',
  '"we ran out of summer"',
];

// ---------- Search combobox (typeahead) ----------
function SearchCombobox({ size = 'md', placeholder, autoFocus = false }) {
  const [q, setQ] = useState('');
  const [open, setOpen] = useState(false);
  const [cursor, setCursor] = useState(-1);
  const [mode, setMode] = useState('auto'); // auto | song | lyric
  const inputRef = useRef(null);
  const rootRef = useRef(null);

  // Recent searches (localStorage)
  const [recent, setRecent] = useState(() => {
    try { return JSON.parse(localStorage.getItem('lyr:recent') || '[]'); } catch { return []; }
  });
  const pushRecent = (s) => {
    if (!s.trim()) return;
    const next = [s, ...recent.filter(x => x.toLowerCase() !== s.toLowerCase())].slice(0, 6);
    setRecent(next);
    try { localStorage.setItem('lyr:recent', JSON.stringify(next)); } catch {}
  };

  // Detect mode (lyric mode if 4+ words or contains quotes/punctuation suggesting a snippet)
  const detectedMode = useMemo(() => {
    if (mode !== 'auto') return mode;
    const trimmed = q.trim();
    if (!trimmed) return 'song';
    if (/["']/.test(trimmed)) return 'lyric';
    const words = trimmed.split(/\s+/).filter(Boolean);
    if (words.length >= 5) return 'lyric';
    return 'song';
  }, [q, mode]);

  // Suggestions
  const suggestions = useMemo(() => {
    const term = q.trim().toLowerCase();
    if (!term) return [];
    const isLyric = detectedMode === 'lyric';
    return CATALOG
      .map(s => {
        let score = 0;
        const hay = `${s.t} ${s.a}`.toLowerCase();
        if (hay.startsWith(term)) score += 6;
        if (s.t.toLowerCase().includes(term)) score += 4;
        if (s.a.toLowerCase().includes(term)) score += 3;
        if (isLyric && s.excerpt.toLowerCase().includes(term)) score += 5;
        else if (s.excerpt.toLowerCase().includes(term)) score += 2;
        return { s, score };
      })
      .filter(x => x.score > 0)
      .sort((a, b) => b.score - a.score)
      .slice(0, 6)
      .map(x => x.s);
  }, [q, detectedMode]);

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

  // ⌘K / Ctrl+K to focus
  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        inputRef.current?.focus();
        setOpen(true);
      } else if (e.key === 'Escape' && open) {
        setOpen(false);
        inputRef.current?.blur();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open]);

  // Keyboard nav within listbox
  const items = q.trim() ? suggestions : null;
  const onKeyDown = (e) => {
    if (!open) setOpen(true);
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      const max = (items ? items.length : (TRENDING_SEARCHES.length + recent.length)) - 1;
      setCursor(c => Math.min(c + 1, max));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setCursor(c => Math.max(c - 1, -1));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      if (items && items[cursor]) {
        go(items[cursor]);
      } else if (q.trim()) {
        pushRecent(q.trim());
        // Fall back: go to first match if any, else stay
        if (suggestions[0]) go(suggestions[0]);
      }
    }
  };
  const go = (song) => {
    pushRecent(`${song.t} — ${song.a}`);
    window.location.hash = `#/song/${song.slug}`;
    setOpen(false);
    setQ('');
  };

  const sizing = size === 'lg'
    ? { wrap: 'rounded-2xl', input: 'text-[17px] md:text-xl py-3 md:py-4', icon: 'w-5 h-5 md:w-6 md:h-6', pad: 'pl-4 md:pl-5 pr-2 py-2', cta: 'h-11 md:h-12 px-4 md:px-5 text-sm md:text-base' }
    : { wrap: 'rounded-full', input: 'text-sm py-0', icon: 'w-4 h-4', pad: 'pl-3.5 pr-2 h-10', cta: 'h-8 px-3 text-xs' };

  return (
    <div ref={rootRef} className={`relative w-full`}>
      <div
        role="combobox"
        aria-expanded={open}
        aria-haspopup="listbox"
        aria-owns="search-listbox"
        className={`relative ${size === 'lg' ? (open ? 'shadow-glow-accent' : 'shadow-soft') : ''} ${sizing.wrap}`}
      >
        <form
          onSubmit={(e) => { e.preventDefault(); if (suggestions[0]) go(suggestions[0]); }}
          className={`relative flex items-center gap-3 bg-ink-850 border ${open ? 'border-accent-500/60' : 'border-ink-700'} ${sizing.wrap} ${sizing.pad}`}
        >
          <Icon.Search className={`${sizing.icon} text-ink-300 shrink-0`} />
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => { setQ(e.target.value); setOpen(true); setCursor(-1); }}
            onFocus={() => setOpen(true)}
            onKeyDown={onKeyDown}
            autoFocus={autoFocus}
            placeholder={placeholder || (detectedMode === 'lyric' ? window.t('common.pasteLyric', 'EN') : window.t('common.search', 'EN'))}
            aria-label="Search lyrics, song, or artist"
            aria-controls="search-listbox"
            aria-activedescendant={cursor >= 0 ? `search-opt-${cursor}` : undefined}
            className={`flex-1 bg-transparent outline-none placeholder:text-ink-400 text-ink-100 min-w-0 ${sizing.input}`}
          />
          {/* mode hint */}
          {q.trim() && (
            <span className="hidden sm:inline-flex items-center gap-1.5 text-[10px] font-mono uppercase tracking-widest text-ink-400 border border-ink-700 rounded-full px-2 py-0.5 mr-1">
              {detectedMode === 'lyric' ? 'lyric mode' : 'song mode'}
            </span>
          )}
          {q && (
            <button type="button" onClick={() => { setQ(''); inputRef.current?.focus(); }} className="text-ink-400 hover:text-ink-100 p-1" aria-label="Clear search">
              <Icon.Close/>
            </button>
          )}
          {size === 'lg' && (
            <button
              type="submit"
              className={`shrink-0 inline-flex items-center gap-1.5 ${sizing.cta} rounded-xl bg-accent-500 hover:bg-accent-600 text-white font-medium transition shadow-glow-accent`}
            >
              <span className="hidden sm:inline">Decode</span>
              <Icon.ArrowRight className="w-4 h-4 md:w-5 md:h-5" />
            </button>
          )}
          {size !== 'lg' && (
            <kbd className="hidden md:inline-flex items-center gap-1 text-[10px] font-mono text-ink-400 border border-ink-700 rounded px-1.5 py-0.5">⌘K</kbd>
          )}
        </form>
      </div>

      {open && (
        <div
          id="search-listbox"
          role="listbox"
          className={`absolute left-0 right-0 mt-2 rounded-2xl border border-ink-700 bg-ink-850/95 backdrop-blur-xl shadow-soft z-50 overflow-hidden`}
        >
          {!q.trim() ? (
            <div className="p-2 max-h-[60vh] overflow-y-auto">
              {recent.length > 0 && (
                <div className="mb-1">
                  <div className="px-3 py-2 text-[10px] font-mono uppercase tracking-[0.22em] text-ink-400 flex items-center justify-between">
                    <span>Recent</span>
                    <button onClick={() => { setRecent([]); try { localStorage.removeItem('lyr:recent'); } catch {} }} className="text-ink-400 hover:text-ink-200 normal-case font-sans">Clear</button>
                  </div>
                  {recent.map((r, i) => (
                    <button
                      key={i}
                      id={`search-opt-${i}`}
                      role="option"
                      aria-selected={cursor === i}
                      onMouseEnter={() => setCursor(i)}
                      onClick={() => { setQ(r); inputRef.current?.focus(); }}
                      className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left text-sm ${cursor === i ? 'bg-ink-800' : 'hover:bg-ink-800/70'}`}
                    >
                      <Icon.Search className="w-3.5 h-3.5 text-ink-400"/>
                      <span className="text-ink-200">{r}</span>
                    </button>
                  ))}
                </div>
              )}
              <div className="px-3 py-2 text-[10px] font-mono uppercase tracking-[0.22em] text-ink-400">Trending searches</div>
              {TRENDING_SEARCHES.map((s, i) => {
                const idx = recent.length + i;
                return (
                  <button
                    key={s}
                    role="option"
                    id={`search-opt-${idx}`}
                    aria-selected={cursor === idx}
                    onMouseEnter={() => setCursor(idx)}
                    onClick={() => { setQ(s); inputRef.current?.focus(); }}
                    className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left text-sm ${cursor === idx ? 'bg-ink-800' : 'hover:bg-ink-800/70'}`}
                  >
                    <span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-accent-500/15 text-accent-300">
                      <WaveBars count={3} className="h-2.5 w-2.5"/>
                    </span>
                    <span className="text-ink-100">{s}</span>
                  </button>
                );
              })}
              <div className="border-t border-ink-800 mt-2 pt-2 px-3 pb-1 text-[11px] text-ink-400 flex items-center justify-between">
                <span>Tip: paste a line from the song</span>
                <span className="flex items-center gap-1">
                  <kbd className="font-mono text-[10px] text-ink-300 border border-ink-700 rounded px-1.5 py-0.5">↵</kbd>
                  <span>to open</span>
                </span>
              </div>
            </div>
          ) : (
            <div className="p-2 max-h-[60vh] overflow-y-auto">
              <div className="px-3 py-2 text-[10px] font-mono uppercase tracking-[0.22em] text-ink-400 flex items-center justify-between">
                <span>{detectedMode === 'lyric' ? 'Matches by lyric' : 'Matches by song'}</span>
                <button
                  onClick={() => setMode(mode === 'lyric' ? 'song' : 'lyric')}
                  className="text-accent-300 hover:text-accent-200 normal-case font-sans"
                >Switch to {detectedMode === 'lyric' ? 'song mode' : 'lyric mode'}</button>
              </div>
              {suggestions.length === 0 && (
                <div className="px-3 py-6 text-sm text-ink-300">
                  No matches yet for <span className="text-ink-100">"{q}"</span>. Try a different line or a partial title.
                </div>
              )}
              {suggestions.map((s, i) => (
                <button
                  key={s.id}
                  role="option"
                  id={`search-opt-${i}`}
                  aria-selected={cursor === i}
                  onMouseEnter={() => setCursor(i)}
                  onClick={() => go(s)}
                  className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left ${cursor === i ? 'bg-ink-800' : 'hover:bg-ink-800/70'}`}
                >
                  <AlbumCover seed={s.seed} className="w-10 h-10 shrink-0" rounded="rounded-md"/>
                  <div className="min-w-0 flex-1">
                    <div className="text-sm font-medium text-ink-100 truncate">{highlight(s.t, q)}</div>
                    <div className="text-xs text-ink-400 truncate">
                      <span>{highlight(s.a, q)}</span>
                      <span className="mx-1.5 text-ink-600">·</span>
                      <span>{s.g}</span>
                      {detectedMode === 'lyric' && (
                        <span className="ml-2 text-ink-300">"{highlight(s.excerpt, q)}"</span>
                      )}
                    </div>
                  </div>
                  <Icon.ArrowRight className="w-4 h-4 text-ink-400 shrink-0"/>
                </button>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function highlight(text, q) {
  const term = q.trim();
  if (!term) return text;
  const idx = text.toLowerCase().indexOf(term.toLowerCase());
  if (idx === -1) return text;
  return (
    <>
      {text.slice(0, idx)}
      <mark className="bg-accent-500/25 text-accent-200 rounded-sm px-0.5">{text.slice(idx, idx + term.length)}</mark>
      {text.slice(idx + term.length)}
    </>
  );
}

// ---------- Depth toggle ----------
function DepthToggle({ value = 'quick', onChange = () => {}, size = 'md', lang }) {
  const code = lang || (typeof localStorage !== 'undefined' && localStorage.getItem('lyr:lang')) || 'EN';
  const opts = [
    { v: 'quick', label: window.t('common.quick', code) },
    { v: 'deep',  label: window.t('common.deep', code) },
  ];
  const h = size === 'sm' ? 'h-8' : 'h-9';
  const t = size === 'sm' ? 'text-xs' : 'text-sm';
  return (
    <div role="radiogroup" aria-label={window.t('common.depth', code)} className={`inline-flex items-center bg-ink-800/80 border border-ink-700 rounded-full p-0.5 ${h}`}>
      {opts.map(o => (
        <button
          key={o.v}
          role="radio"
          aria-checked={value === o.v}
          onClick={() => onChange(o.v)}
          className={`inline-flex items-center gap-1.5 px-3 rounded-full ${h} ${t} transition ${value === o.v ? 'bg-accent-500 text-white shadow-glow-accent' : 'text-ink-200 hover:text-ink-100'}`}
        >
          {o.v === 'deep' ? <Icon.Sparkle className="w-3.5 h-3.5"/> : <Icon.Bolt className="w-3.5 h-3.5"/>}
          <span className="font-medium">{o.label}</span>
        </button>
      ))}
    </div>
  );
}

// ---------- Feedback bar (thumbs + suggest + report) ----------
function FeedbackBar({ id, lang = 'EN' }) {
  const [vote, setVote] = useState(null); // 'up' | 'down' | null
  const [openSuggest, setOpenSuggest] = useState(false);
  const [suggestion, setSuggestion] = useState('');
  const [reported, setReported] = useState(false);
  const [submitted, setSubmitted] = useState(false);
  useEffect(() => { setVote(null); setOpenSuggest(false); setSuggestion(''); setReported(false); setSubmitted(false); }, [id]);

  return (
    <div className="mt-5 border-t border-ink-800 pt-4">
      <div className="flex flex-wrap items-center gap-2">
        <span className="text-xs text-ink-400 mr-1">{window.t('feedback.helpful', lang)}</span>
        <button
          onClick={() => setVote(v => v === 'up' ? null : 'up')}
          aria-pressed={vote === 'up'}
          aria-label={window.t('feedback.up', lang)}
          className={`inline-flex items-center justify-center w-9 h-9 rounded-full border transition ${vote === 'up' ? 'border-accent-500/60 bg-accent-500/15 text-accent-200' : 'border-ink-700 text-ink-300 hover:text-ink-100 hover:border-ink-600'}`}
        >
          <svg viewBox="0 0 24 24" className="w-4 h-4" fill="none"><path d="M7 11v9H4v-9h3Zm3 9h7a2 2 0 0 0 2-1.6l1.2-6A2 2 0 0 0 18.2 10H14V6a2.5 2.5 0 0 0-5 0c0 2.2-2 4.2-2 4.2V20Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/></svg>
        </button>
        <button
          onClick={() => setVote(v => v === 'down' ? null : 'down')}
          aria-pressed={vote === 'down'}
          aria-label={window.t('feedback.down', lang)}
          className={`inline-flex items-center justify-center w-9 h-9 rounded-full border transition ${vote === 'down' ? 'border-accent-500/60 bg-accent-500/15 text-accent-200' : 'border-ink-700 text-ink-300 hover:text-ink-100 hover:border-ink-600'}`}
        >
          <svg viewBox="0 0 24 24" className="w-4 h-4" fill="none"><path d="M7 13V4H4v9h3Zm3-9h7a2 2 0 0 1 2 1.6l1.2 6A2 2 0 0 1 18.2 14H14v4a2.5 2.5 0 0 1-5 0c0-2.2-2-4.2-2-4.2V4Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/></svg>
        </button>
        <button
          onClick={() => setOpenSuggest(s => !s)}
          className="ml-1 inline-flex items-center gap-1.5 h-9 px-3 rounded-full border border-ink-700 hover:border-ink-600 text-xs text-ink-200 transition"
        >
          <Icon.Sparkle className="w-3.5 h-3.5"/>
          {window.t('feedback.suggest', lang)}
        </button>
        <button
          onClick={() => setReported(true)}
          className="inline-flex items-center gap-1.5 h-9 px-3 rounded-full text-xs text-ink-400 hover:text-ink-200 transition"
          aria-label={window.t('feedback.report', lang)}
        >
          <svg viewBox="0 0 24 24" className="w-3.5 h-3.5" fill="none"><path d="M5 21V4h11l-2 4 2 4H5" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/></svg>
          {window.t('feedback.report', lang)}
        </button>
      </div>

      {(vote === 'down' && !openSuggest && !submitted) && (
        <p className="mt-3 text-xs text-ink-400">{window.t('feedback.thanksDown', lang)}</p>
      )}
      {(vote === 'up' && !openSuggest && !submitted) && (
        <p className="mt-3 text-xs text-emerald-400/90">{window.t('feedback.thanksUp', lang)}</p>
      )}
      {reported && (
        <p className="mt-3 text-xs text-ink-400">{window.t('feedback.reported', lang)}</p>
      )}

      {openSuggest && !submitted && (
        <form
          onSubmit={(e) => { e.preventDefault(); if (suggestion.trim()) setSubmitted(true); }}
          className="mt-3 rounded-xl border border-ink-700 bg-ink-900/60 p-3"
        >
          <label className="block text-[11px] font-mono uppercase tracking-widest text-ink-400 mb-2">
            {window.t('feedback.yourReading', lang)}
          </label>
          <textarea
            value={suggestion}
            onChange={(e) => setSuggestion(e.target.value)}
            rows={3}
            placeholder={window.t('feedback.placeholder', lang)}
            className="w-full bg-ink-850 border border-ink-700 rounded-lg p-3 text-sm text-ink-100 placeholder:text-ink-400 outline-none focus:border-accent-500/60 resize-y"
          />
          <div className="mt-2 flex items-center justify-between gap-2">
            <span className="text-[11px] text-ink-400">{window.t('feedback.private', lang)}</span>
            <div className="flex items-center gap-2">
              <button type="button" onClick={() => setOpenSuggest(false)} className="h-8 px-3 rounded-full text-xs text-ink-300 hover:text-ink-100">{window.t('feedback.cancel', lang)}</button>
              <button
                type="submit"
                disabled={!suggestion.trim()}
                className="h-8 px-3.5 rounded-full text-xs font-medium bg-accent-500 hover:bg-accent-600 text-white disabled:opacity-40 disabled:cursor-not-allowed"
              >{window.t('feedback.send', lang)}</button>
            </div>
          </div>
        </form>
      )}
      {submitted && (
        <p className="mt-3 text-xs text-accent-300">{window.t('feedback.sent', lang)}</p>
      )}
    </div>
  );
}

// ---------- Reading mode toggle ----------
function ReadingModeToggle({ value, onChange, lang = 'EN' }) {
  return (
    <button
      onClick={() => onChange(!value)}
      aria-pressed={value}
      className={`inline-flex items-center gap-2 h-9 px-3.5 rounded-full border text-sm transition ${value ? 'border-accent-500/60 bg-accent-500/10 text-ink-100' : 'border-ink-700 text-ink-200 hover:border-ink-600'}`}
      title={value ? window.t('common.exitReading', lang) : window.t('common.readingMode', lang)}
    >
      <svg viewBox="0 0 24 24" className="w-4 h-4" fill="none" aria-hidden="true">
        <path d="M4 5h7v14H4zM20 5h-7v14h7" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/>
      </svg>
      {value ? window.t('common.exitReading', lang) : window.t('common.readingMode', lang)}
    </button>
  );
}

// ---------- Bookmark store ----------
function useSaved() {
  const [saved, setSaved] = useState(() => {
    try { return JSON.parse(localStorage.getItem('lyr:saved') || '[]'); } catch { return []; }
  });
  useEffect(() => {
    const onStorage = () => {
      try { setSaved(JSON.parse(localStorage.getItem('lyr:saved') || '[]')); } catch {}
    };
    window.addEventListener('storage', onStorage);
    window.addEventListener('lyr:saved-changed', onStorage);
    return () => {
      window.removeEventListener('storage', onStorage);
      window.removeEventListener('lyr:saved-changed', onStorage);
    };
  }, []);
  const isSaved = (id) => saved.some(s => s.id === id);
  const toggle = (song) => {
    let next;
    if (isSaved(song.id)) {
      next = saved.filter(s => s.id !== song.id);
    } else {
      next = [{ id: song.id, t: song.t, a: song.a, seed: song.seed, slug: song.slug, savedAt: Date.now() }, ...saved];
    }
    setSaved(next);
    try { localStorage.setItem('lyr:saved', JSON.stringify(next)); } catch {}
    window.dispatchEvent(new CustomEvent('lyr:saved-changed'));
  };
  return { saved, isSaved, toggle };
}

function BookmarkButton({ song, size = 'md', label = false, lang = 'EN' }) {
  const { isSaved, toggle } = useSaved();
  const on = isSaved(song.id);
  const h = size === 'sm' ? 'w-8 h-8' : 'w-9 h-9';
  return (
    <button
      onClick={(e) => { e.preventDefault(); e.stopPropagation(); toggle(song); }}
      aria-pressed={on}
      aria-label={on ? window.t('common.saved', lang) : window.t('common.save', lang)}
      title={on ? window.t('common.saved', lang) : window.t('common.save', lang)}
      className={`inline-flex items-center justify-center gap-1.5 ${label ? 'h-9 px-3.5 rounded-full' : `${h} rounded-full`} border transition ${on ? 'border-accent-500/60 bg-accent-500/10 text-accent-200' : 'border-ink-700 text-ink-300 hover:text-ink-100 hover:border-ink-600'}`}
    >
      <svg viewBox="0 0 24 24" className="w-4 h-4" fill={on ? 'currentColor' : 'none'} aria-hidden="true">
        <path d="M6 4h12v17l-6-4-6 4V4Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/>
      </svg>
      {label && <span className="text-sm">{on ? window.t('common.saved', lang) : window.t('common.save', lang)}</span>}
    </button>
  );
}

// ---------- Trending page ----------
function TrendingPage({ lang = 'EN' }) {
  const [filter, setFilter] = useState('all'); // all | EN | ID | ES
  const list = useMemo(() => {
    const base = [...CATALOG].sort((a, b) => Number(b.hot) - Number(a.hot));
    if (filter === 'all') return base;
    return base.filter(s => s.lang === filter);
  }, [filter]);
  const top = list.slice(0, 3);
  const rest = list.slice(3);
  const T = window.t;

  return (
    <main id="main">
      <section className="relative pt-12 md:pt-20 pb-10 md:pb-14 overflow-hidden">
        <div aria-hidden="true" className="absolute inset-x-0 top-0 h-[70%] bg-[radial-gradient(ellipse_at_top,rgba(124,92,255,0.16),transparent_60%)]"/>
        <div className="relative max-w-7xl mx-auto px-5 md:px-8">
          <div className="text-[11px] font-mono uppercase tracking-[0.22em] text-accent-300">{T('trendingPage.eyebrow', lang)}</div>
          <h1 className="mt-3 font-display text-4xl md:text-6xl font-semibold tracking-tight text-ink-100 text-balance">{T('trendingPage.h1', lang)}</h1>
          <p className="mt-4 text-ink-300 max-w-2xl text-base md:text-lg">{T('trendingPage.sub', lang)}</p>
          <div className="mt-7 flex flex-wrap items-center gap-2">
            {[
              { v: 'all', label: T('trendingPage.all', lang) },
              { v: 'EN',  label: 'English' },
              { v: 'ID',  label: 'Bahasa Indonesia' },
              { v: 'ES',  label: 'Español' },
            ].map(f => (
              <button
                key={f.v}
                onClick={() => setFilter(f.v)}
                className={`inline-flex items-center gap-2 h-9 px-3.5 rounded-full border text-sm transition ${filter === f.v ? 'border-accent-500/60 bg-accent-500/10 text-ink-100' : 'border-ink-700 text-ink-200 hover:border-ink-600'}`}
                aria-pressed={filter === f.v}
              >{f.label}</button>
            ))}
          </div>
        </div>
      </section>

      {/* Top 3 podium */}
      <section className="max-w-7xl mx-auto px-5 md:px-8">
        <div className="grid md:grid-cols-3 gap-4 md:gap-6">
          {top.map((s, i) => (
            <a key={s.id} href={`#/song/${s.slug}`} className="group relative rounded-3xl border border-ink-700 bg-ink-850 overflow-hidden hover:border-accent-500/40 transition">
              <div className="relative aspect-[5/4]">
                <AlbumCover seed={s.seed} className="w-full h-full" rounded="rounded-none"/>
                <span className="absolute top-3 left-3 font-display text-[44px] leading-none font-semibold text-white/90 drop-shadow-lg">{String(i + 1).padStart(2, '0')}</span>
                <span className="absolute top-3 right-3 inline-flex items-center gap-1.5 text-[10px] font-mono uppercase tracking-widest text-white/90 bg-black/40 backdrop-blur-md rounded-full px-2 py-0.5">
                  <WaveBars count={3} className="h-2.5 w-2.5"/> {T('trendingPage.rising', lang)}
                </span>
              </div>
              <div className="p-5 flex items-center justify-between gap-3">
                <div className="min-w-0">
                  <div className="font-display text-lg font-semibold text-ink-100 truncate">{s.t}</div>
                  <div className="text-xs text-ink-400 truncate">{s.a} · {s.g}</div>
                </div>
                <BookmarkButton song={s} lang={lang}/>
              </div>
            </a>
          ))}
        </div>
      </section>

      {/* Rest as table-ish list */}
      <section className="max-w-7xl mx-auto px-5 md:px-8 mt-12 md:mt-16 mb-16">
        <div className="rounded-2xl border border-ink-700 bg-ink-850 overflow-hidden">
          <div className="hidden md:grid grid-cols-[64px_minmax(0,1.4fr)_minmax(0,1fr)_minmax(0,0.8fr)_minmax(0,1.4fr)_120px] gap-4 px-5 py-3 text-[10px] font-mono uppercase tracking-[0.22em] text-ink-400 border-b border-ink-800">
            <div>{T('trendingPage.cols.rank', lang)}</div><div>{T('trendingPage.cols.song', lang)}</div><div>{T('trendingPage.cols.artist', lang)}</div><div>{T('trendingPage.cols.lang', lang)}</div><div>{T('trendingPage.cols.topLine', lang)}</div><div></div>
          </div>
          {rest.map((s, i) => (
            <a key={s.id} href={`#/song/${s.slug}`} className="grid grid-cols-[40px_minmax(0,1fr)] md:grid-cols-[64px_minmax(0,1.4fr)_minmax(0,1fr)_minmax(0,0.8fr)_minmax(0,1.4fr)_120px] gap-4 items-center px-5 py-3.5 border-t border-ink-800 hover:bg-ink-800/60 transition group">
              <div className="font-mono text-sm text-ink-400">{String(i + 4).padStart(2,'0')}</div>
              <div className="flex items-center gap-3 min-w-0">
                <AlbumCover seed={s.seed} className="w-10 h-10 shrink-0" rounded="rounded-md"/>
                <div className="min-w-0">
                  <div className="text-sm font-medium text-ink-100 truncate">{s.t}</div>
                  <div className="md:hidden text-xs text-ink-400 truncate">{s.a} · {s.lang}</div>
                </div>
              </div>
              <div className="hidden md:block text-sm text-ink-300 truncate">{s.a}</div>
              <div className="hidden md:block"><span className="font-mono text-[11px] text-ink-300 border border-ink-700 rounded px-1.5 py-0.5">{s.lang}</span></div>
              <div className="hidden md:block text-sm text-ink-400 truncate italic">"{s.excerpt}"</div>
              <div className="hidden md:flex items-center justify-end gap-2">
                <BookmarkButton song={s} size="sm" lang={lang}/>
                <Icon.ArrowRight className="w-4 h-4 text-ink-400 group-hover:text-accent-300 transition"/>
              </div>
            </a>
          ))}
        </div>
      </section>
    </main>
  );
}

// ---------- Saved page ----------
function SavedPage({ lang = 'EN' }) {
  const { saved, toggle } = useSaved();
  const T = window.t;

  return (
    <main id="main">
      <section className="relative pt-12 md:pt-20 pb-10 md:pb-14 overflow-hidden">
        <div aria-hidden="true" className="absolute inset-x-0 top-0 h-[70%] bg-[radial-gradient(ellipse_at_top,rgba(124,92,255,0.16),transparent_60%)]"/>
        <div className="relative max-w-7xl mx-auto px-5 md:px-8">
          <div className="text-[11px] font-mono uppercase tracking-[0.22em] text-accent-300">{T('saved.eyebrow', lang)}</div>
          <h1 className="mt-3 font-display text-4xl md:text-6xl font-semibold tracking-tight text-ink-100 text-balance">{T('saved.h1', lang)}</h1>
          <p className="mt-4 text-ink-300 max-w-xl text-base md:text-lg">{T('saved.sub', lang)}</p>
        </div>
      </section>
      <section className="max-w-5xl mx-auto px-5 md:px-8 mb-20">
        {saved.length === 0 ? (
          <div className="rounded-3xl border border-dashed border-ink-700 bg-ink-850/40 p-10 md:p-16 text-center">
            <div className="mx-auto inline-flex items-center justify-center w-12 h-12 rounded-2xl bg-accent-500/10 border border-accent-500/25 text-accent-300">
              <svg viewBox="0 0 24 24" className="w-5 h-5" fill="none"><path d="M6 4h12v17l-6-4-6 4V4Z" stroke="currentColor" strokeWidth="1.5"/></svg>
            </div>
            <h2 className="mt-5 font-display text-2xl md:text-3xl font-semibold text-ink-100">{T('saved.emptyH', lang)}</h2>
            <p className="mt-3 text-ink-300 text-sm md:text-base">{T('saved.emptyBody', lang)}</p>
            <a href="#/trending" className="mt-6 inline-flex items-center gap-1.5 h-10 px-4 rounded-full bg-accent-500 hover:bg-accent-600 text-white text-sm font-medium shadow-glow-accent">
              {T('saved.browseTrending', lang)}
              <Icon.ArrowRight className="w-4 h-4"/>
            </a>
          </div>
        ) : (
          <div className="rounded-2xl border border-ink-700 bg-ink-850 overflow-hidden">
            <div className="px-5 py-3 border-b border-ink-800 flex items-center justify-between">
              <span className="text-sm text-ink-300">{saved.length} {T('saved.countSuffix', lang)}</span>
              <button
                onClick={() => { if (confirm(T('saved.confirmClear', lang))) { saved.forEach(s => toggle(s)); } }}
                className="text-xs text-ink-400 hover:text-ink-100"
              >{T('saved.clearAll', lang)}</button>
            </div>
            {saved.map((s) => (
              <a key={s.id} href={`#/song/${s.slug}`} className="flex items-center gap-3 px-5 py-3.5 border-t border-ink-800 hover:bg-ink-800/60 transition group">
                <AlbumCover seed={s.seed} className="w-11 h-11 shrink-0" rounded="rounded-md"/>
                <div className="min-w-0 flex-1">
                  <div className="text-sm font-medium text-ink-100 truncate">{s.t}</div>
                  <div className="text-xs text-ink-400 truncate">{s.a}</div>
                </div>
                <BookmarkButton song={s} size="sm" lang={lang}/>
                <Icon.ArrowRight className="w-4 h-4 text-ink-400 group-hover:text-accent-300 transition"/>
              </a>
            ))}
          </div>
        )}
      </section>
    </main>
  );
}

// ---------- Quiet ad slot (reusable) ----------
function QuietAd({ note = 'One quiet sponsor slot — never inside lyrics, never autoplay.' }) {
  return (
    <section className="max-w-7xl mx-auto px-5 md:px-8 mt-16 md:mt-24" aria-label="Sponsor">
      <div className="rounded-2xl border border-dashed border-ink-700 bg-ink-850/40 px-5 py-4 flex items-center gap-4">
        <div className="text-[10px] font-mono uppercase tracking-widest text-ink-400 shrink-0">Ad</div>
        <div className="flex-1 text-sm text-ink-300">{note}</div>
        <span className="hidden sm:inline text-[10px] font-mono text-ink-500">1 of 1 on this page</span>
      </div>
    </section>
  );
}

Object.assign(window, {
  CATALOG, TRENDING_SEARCHES,
  SearchCombobox, DepthToggle, FeedbackBar, ReadingModeToggle,
  useSaved, BookmarkButton, TrendingPage, SavedPage, QuietAd,
});
