/* global React, BIBLE_BOOKS, BIBLE_TEXT, BIBLE_CURRENT, TranslationLoader */
const { useState: useStateR, useEffect: useEffectR, useMemo: useMemoR, useCallback: useCallbackR, useRef: useRefR } = React;

/* ---------- Offline-availability toggle ----------
 *
 * Per-translation control in the Reader meta sidebar. When tapped, fetches
 * every book of the translation (`data/<slug>/<book>.json` × N, typically
 * 27-80 books, ~50-200 KB each) sequentially, with combined byte progress.
 * The service-worker cacheFirst path on each fetch deposits the response in
 * DATA_CACHE; on completion we set localStorage `ob.offline.<slug>=true` so
 * subsequent visits show "Cached" without re-checking N individual cache
 * entries.
 *
 * Status state machine
 *   unknown     → component mounting; reading localStorage
 *   absent      → no flag set; show "Download for offline (N MB)"
 *   downloading → in flight; show "K / N books · X / Y MB" + cancel
 *   available   → flag set; show "Cached" + "Remove"
 *   error       → fetch failed mid-stream; show retry
 *
 * Why localStorage instead of querying caches.match() for every book on mount:
 * cleaner semantics ("did the user *intend* this to be offline?") and avoids
 * N cache lookups on every Reader mount.
 *
 * On "Remove": we walk the books list and delete each cache entry, then drop
 * the localStorage flag. If the user later reads a chapter, the runtime
 * cache will re-populate that one book naturally — no different from a
 * never-downloaded translation.
 *
 * Persistence across deploys: DATA_CACHE is intentionally NOT versioned in
 * sw.js — a user who deliberately downloaded a translation doesn't lose it
 * when we ship a shell update.
 *
 * SSR / non-browser safety: `caches` and `fetch` are guarded; if absent the
 * component renders unknown state without crashing.
 */
function OfflineToggle({ slug }) {
  const [status, setStatus] = useStateR('unknown');
  const [bookProgress, setBookProgress] = useStateR({ done: 0, total: 0 });
  const [byteProgress, setByteProgress] = useStateR({ received: 0, total: 0 });
  const [errorMsg, setErrorMsg] = useStateR('');
  const abortRef = useRefR(null);

  // Read intent flag from localStorage on mount + slug change.
  useEffectR(() => {
    if (!slug) { setStatus('unknown'); return; }
    try {
      const flag = localStorage.getItem(`ob.offline.${slug}`);
      setStatus(flag === 'true' ? 'available' : 'absent');
    } catch {
      setStatus('absent');
    }
  }, [slug]);

  async function downloadForOffline() {
    if (!slug) return;
    setStatus('downloading');
    setBookProgress({ done: 0, total: 0 });
    setByteProgress({ received: 0, total: 0 });
    setErrorMsg('');
    const controller = new AbortController();
    abortRef.current = controller;
    try {
      // Need the books list — fetch the per-translation index first.
      const idx = await window.TranslationLoader.loadTranslationIndex
        ? await window.TranslationLoader.loadTranslationIndex(slug)
        : await fetch(`/data/${slug}/index.json`).then(r => r.json());
      const books = idx.books || [];
      const totalBooks = books.length;
      const totalBytes = books.reduce((s, b) => s + (b.sizeBytes || 0), 0);
      setBookProgress({ done: 0, total: totalBooks });
      setByteProgress({ received: 0, total: totalBytes });
      let bytesSoFar = 0;
      for (let i = 0; i < books.length; i++) {
        if (controller.signal.aborted) throw new DOMException('aborted', 'AbortError');
        const b = books[i];
        const url = `/data/${slug}/${b.id}.json`;
        const res = await fetch(url, { signal: controller.signal });
        if (!res.ok) throw new Error(`book ${b.id}: HTTP ${res.status}`);
        // Stream the body so progress feels live within a single book too.
        // Without streaming, each ~200 KB book would jump in one chunk;
        // bytes-as-they-arrive is smoother on slow connections.
        const reader = res.body.getReader();
        while (true) {
          const { done, value } = await reader.read();
          if (done) break;
          bytesSoFar += value.length;
          // Cap visible "received" at the known total (when total is known)
          // to avoid the bar jumping past 100% if Content-Length sums are
          // slightly different from on-disk size.
          setByteProgress({ received: bytesSoFar, total: totalBytes });
        }
        setBookProgress({ done: i + 1, total: totalBooks });
      }
      try { localStorage.setItem(`ob.offline.${slug}`, 'true'); } catch {}
      setStatus('available');
    } catch (e) {
      if (e.name === 'AbortError') {
        setStatus('absent');
      } else {
        setStatus('error');
        setErrorMsg(e.message || 'download failed');
      }
    } finally {
      abortRef.current = null;
    }
  }

  function cancel() {
    if (abortRef.current) abortRef.current.abort();
  }

  async function remove() {
    if (!slug) return;
    try {
      // Best-effort: walk the books list and delete each cache entry. If
      // caches API isn't available (private window), we still drop the
      // intent flag.
      if (typeof caches !== 'undefined') {
        const idx = await fetch(`/data/${slug}/index.json`).then(r => r.json()).catch(() => null);
        const books = (idx && idx.books) || [];
        const names = await caches.keys();
        for (const name of names) {
          const cache = await caches.open(name);
          for (const b of books) {
            await cache.delete(`/data/${slug}/${b.id}.json`);
          }
          // Also drop the per-translation index so a fresh re-download
          // re-fetches it (in case books-list changed since cache was written).
          await cache.delete(`/data/${slug}/index.json`);
        }
      }
      try { localStorage.removeItem(`ob.offline.${slug}`); } catch {}
      setStatus('absent');
    } catch (e) {
      setStatus('error');
      setErrorMsg(e.message || 'remove failed');
    }
  }

  const fmtMB = (b) => (b / (1024 * 1024)).toFixed(1);
  const pct = byteProgress.total
    ? Math.min(100, Math.round((byteProgress.received / byteProgress.total) * 100))
    : (bookProgress.total ? Math.round((bookProgress.done / bookProgress.total) * 100) : 0);

  let body;
  if (status === 'unknown') {
    body = <span style={{color:'var(--ink-3)'}}>…</span>;
  } else if (status === 'available') {
    body = (
      <>
        <span style={{color:'var(--ink)'}}>✓ Cached for offline</span>
        <button
          onClick={remove}
          style={{display:'block', marginTop:6, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:'var(--ink-3)', borderBottom:'1px dotted var(--ink-3)'}}
        >Remove</button>
      </>
    );
  } else if (status === 'downloading') {
    body = (
      <>
        <span style={{fontFamily:'"JetBrains Mono", monospace', fontSize:12}}>
          ↓ {bookProgress.done} / {bookProgress.total || '?'} books
        </span>
        {byteProgress.total > 0 && (
          <span style={{display:'block', fontFamily:'"JetBrains Mono", monospace', fontSize:11, color:'var(--ink-3)', marginTop:2}}>
            {fmtMB(byteProgress.received)} / {fmtMB(byteProgress.total)} MB
          </span>
        )}
        <div style={{height:3, marginTop:6, background:'var(--rule-soft)', borderRadius:2, overflow:'hidden'}}>
          <div style={{height:'100%', width: pct + '%', background:'var(--ink)', transition:'width 0.15s linear'}} />
        </div>
        <button
          onClick={cancel}
          style={{display:'block', marginTop:6, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:'var(--ink-3)', borderBottom:'1px dotted var(--ink-3)'}}
        >Cancel</button>
      </>
    );
  } else if (status === 'error') {
    body = (
      <>
        <span style={{color:'var(--ink-2)'}}>⚠ {errorMsg || 'error'}</span>
        <button
          onClick={downloadForOffline}
          style={{display:'block', marginTop:6, fontSize:10, letterSpacing:'0.14em', textTransform:'uppercase', color:'var(--ink-3)', borderBottom:'1px dotted var(--ink-3)'}}
        >Retry</button>
      </>
    );
  } else { // absent
    // Show the estimated total size if the catalog already knows it.
    const sizeMb = window.BIBLE_CURRENT
      && window.BIBLE_CURRENT.meta
      && window.BIBLE_CURRENT.meta.sizeBytes
        ? ` (${fmtMB(window.BIBLE_CURRENT.meta.sizeBytes)} MB)`
        : '';
    body = (
      <button
        onClick={downloadForOffline}
        style={{textAlign:'left', borderBottom:'1px dotted var(--ink-3)'}}
      >Download for offline{sizeMb}</button>
    );
  }
  return <div className="v">{body}</div>;
}


function Reader({ bookId, chapter, navigate, switchTranslation }) {
  const book = useMemoR(() => BIBLE_BOOKS.find(b => b.id === bookId) || BIBLE_BOOKS[0], [bookId, window.BIBLE_CURRENT && window.BIBLE_CURRENT.slug]);
  const key = `${book.id}.${chapter}`;
  const meta = (window.BIBLE_CURRENT && window.BIBLE_CURRENT.meta) || {};
  const activeSlug = window.BIBLE_CURRENT && window.BIBLE_CURRENT.slug;
  const [selected, setSelected] = useStateR(null);
  const [pickerOpen, setPickerOpen] = useStateR(false);
  const [transPickerOpen, setTransPickerOpen] = useStateR(false);
  // Per-book lazy load. `bookLoadState` drives both the verses lookup
  // (after 'loaded' the BIBLE_TEXT global has this book's chapters) and
  // the in-Reader loading/error states. It's keyed off (activeSlug, book.id)
  // so a translation switch OR a different-book nav re-fires the load.
  const [bookLoadState, setBookLoadState] = useStateR('idle');
  useEffectR(() => {
    if (!activeSlug || !book.id) return;
    // Probe BIBLE_TEXT for any chapter of this book — if present, it was
    // already loaded by a sibling component (e.g. Landing's VOTD prefetch).
    const probeKey = `${book.id}.1`;
    if (window.BIBLE_TEXT && window.BIBLE_TEXT[probeKey] !== undefined) {
      setBookLoadState('loaded');
      return;
    }
    setBookLoadState('loading');
    let cancelled = false;
    window.TranslationLoader.ensureBookLoaded(activeSlug, book.id)
      .then(() => { if (!cancelled) setBookLoadState('loaded'); })
      .catch(() => { if (!cancelled) setBookLoadState('error'); });
    return () => { cancelled = true; };
  }, [activeSlug, book.id]);
  // The verses lookup must run on every render so the new chapter shows when
  // BIBLE_TEXT mutates underneath us. bookLoadState transitions drive the
  // re-renders; the lookup itself is cheap.
  const verses = window.BIBLE_TEXT && window.BIBLE_TEXT[key];
  const hasText = !!verses;
  // Verses targeted by a #vN or #vS-vE URL fragment — highlighted briefly
  // and scrolled into view. Populated by the hash-anchor effect below.
  const [highlighted, setHighlighted] = useStateR(() => new Set());
  const [fontSize, setFontSize] = useStateR(() => {
    try { return parseInt(localStorage.getItem('ob.fontSize') || '20', 10); } catch { return 20; }
  });
  useEffectR(() => {
    try { localStorage.setItem('ob.fontSize', String(fontSize)); } catch {}
  }, [fontSize]);

  // save last read position
  useEffectR(() => {
    try { localStorage.setItem('ob.lastRead', JSON.stringify({ bookId, chapter })); } catch {}
  }, [bookId, chapter]);

  const readPath = (bid, ch) =>
    activeSlug ? `/read/${activeSlug}/${bid}/${ch}` : `/read/${bid}/${ch}`;

  const prev = () => {
    if (chapter > 1) { navigate(readPath(book.id, chapter - 1)); return; }
    const idx = BIBLE_BOOKS.findIndex(b => b.id === book.id);
    if (idx > 0) {
      const p = BIBLE_BOOKS[idx - 1];
      navigate(readPath(p.id, p.chapters));
    }
  };
  const next = () => {
    if (chapter < book.chapters) { navigate(readPath(book.id, chapter + 1)); return; }
    const idx = BIBLE_BOOKS.findIndex(b => b.id === book.id);
    if (idx < BIBLE_BOOKS.length - 1) {
      const n = BIBLE_BOOKS[idx + 1];
      navigate(readPath(n.id, 1));
    }
  };

  useEffectR(() => {
    const onKey = (e) => {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
      if (e.key === 'ArrowRight' || e.key === 'j') next();
      else if (e.key === 'ArrowLeft' || e.key === 'k') prev();
      else if (e.key === 'Escape') { setPickerOpen(false); setSelected(null); }
      else if (e.key === '=' || e.key === '+') setFontSize(s => Math.min(28, s + 1));
      else if (e.key === '-' || e.key === '_') setFontSize(s => Math.max(14, s - 1));
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [book, chapter]);

  // Hash-anchor: parse #vN or #vS-vE from the URL, scroll to the first
  // matching verse, briefly highlight the range. Re-runs whenever the
  // chapter changes (so /read/.../jhn/3#v16 still scrolls if the user
  // navigated here via the SPA), and on hashchange events (so editing
  // the hash directly in the address bar works too). Verses are
  // identified by id="v{vnum}" — rendered below in the verses.map.
  useEffectR(() => {
    function applyHash() {
      const m = (location.hash || '').match(/^#v(\d+)(?:-(\d+))?$/);
      if (!m) { setHighlighted(new Set()); return; }
      const start = parseInt(m[1], 10);
      const end = m[2] ? parseInt(m[2], 10) : start;
      if (start < 1) return;
      const range = new Set();
      for (let v = start; v <= end; v++) range.add(v);
      setHighlighted(range);
      // Scroll into view after the next paint (the verse <p>s render in
      // the same effect cycle as the highlighted state). The reader page
      // has a sticky header; scrollMarginTop in CSS keeps the verse from
      // hiding under it. Behavior 'auto' (instant) avoids Firefox-specific
      // races when another scroll fires mid-animation — opening a deep link
      // should feel like a jump, not an animated reveal anyway.
      requestAnimationFrame(() => {
        const el = document.getElementById('v' + start);
        if (el) el.scrollIntoView({ behavior: 'auto', block: 'start' });
      });
      // Fade out the highlight after a few seconds. The verse stays in view.
      const t = setTimeout(() => setHighlighted(new Set()), 4500);
      return () => clearTimeout(t);
    }
    const cleanup = applyHash();
    window.addEventListener('hashchange', applyHash);
    return () => {
      window.removeEventListener('hashchange', applyHash);
      if (cleanup) cleanup();
    };
  }, [bookId, chapter, hasText]);

  // chapter list for sidebar — show numerical list of chapters for this book
  const chapters = Array.from({ length: book.chapters }, (_, i) => i + 1);

  return (
    <main>
      {/* Reader control bar */}
      <div className="reader-bar">
        <div className="breadcrumb">
          <button onClick={() => setPickerOpen(true)}>
            {book.name.toUpperCase()}
          </button>
          <span className="sep">/</span>
          <button onClick={() => setPickerOpen(true)}>
            Ch {chapter}
          </button>
          <span className="sep">·</span>
          <button onClick={() => setTransPickerOpen(true)} title="Change translation" style={{borderBottom:'1px dotted var(--ink-3)'}}>
            {meta.name || activeSlug || '—'}
          </button>
          <span className="sep">·</span>
          <span>{(meta.language || '—').toUpperCase()}</span>
        </div>
        <div className="title serif">{book.name} {chapter}</div>
        <div className="controls">
          <button onClick={() => setFontSize(s => Math.max(14, s - 1))}>A−</button>
          <button onClick={() => setFontSize(s => Math.min(28, s + 1))}>A+</button>
          <button onClick={prev}>← Prev</button>
          <button onClick={next}>Next →</button>
        </div>
      </div>

      <div className="reader">
        {/* LEFT: chapter picker */}
        <aside className="sidebar">
          <h4>Book</h4>
          <div style={{display:'flex', alignItems:'center', justifyContent:'space-between', padding:'6px 0', borderBottom:'1px solid var(--rule)', cursor:'pointer'}} onClick={() => setPickerOpen(true)}>
            <span className="serif" style={{fontSize:18}}>{book.name}</span>
            <span style={{fontSize:10, letterSpacing:'0.2em', color:'var(--ink-3)'}}>CHANGE</span>
          </div>
          <h4>Chapters · {book.chapters}</h4>
          <ol>
            {chapters.map(c => (
              <li
                key={c}
                className={c === chapter ? 'active' : ''}
                onClick={() => navigate(readPath(book.id, c))}
              >
                <span className="idx">{String(c).padStart(3, '0')}</span>
                <span>{book.name}</span>
                <span className="ch">{c}</span>
              </li>
            ))}
          </ol>
        </aside>

        {/* CENTER: scripture */}
        <article className="scripture">
          <h1 className="book-title">{book.name}</h1>
          <div className="chapter-label">Chapter {chapter} · {book.testament === 'ot' ? 'Old Testament' : book.testament === 'apoc' ? 'Apocrypha' : 'New Testament'}</div>

          {hasText ? (
            verses.map((txt, i) => {
              const vnum = i + 1;
              const isSel = selected === vnum;
              const isHighlighted = highlighted.has(vnum);
              const isFirst = i === 0;
              return (
                <p
                  key={i}
                  id={`v${vnum}`}
                  className={`verse ${isSel ? 'selected' : ''} ${isHighlighted ? 'highlight' : ''}`}
                  style={{fontSize: fontSize}}
                  onClick={() => setSelected(isSel ? null : vnum)}
                >
                  {isFirst && <span className="drop-cap">{txt[0]}</span>}
                  <span className="vnum">{vnum}</span>
                  {isFirst ? txt.slice(1) : txt}
                </p>
              );
            })
          ) : bookLoadState === 'loading' ? (
            <div style={{padding:'40px 0', color:'var(--ink-3)', fontSize:14, lineHeight:1.7, fontFamily:'"JetBrains Mono", monospace', letterSpacing:'0.02em'}}>
              <div style={{fontSize:11, letterSpacing:'0.22em', textTransform:'uppercase'}}>↓ Loading {book.name}…</div>
            </div>
          ) : bookLoadState === 'error' ? (
            <div style={{padding:'40px 0', color:'var(--ink-3)', fontSize:14, lineHeight:1.7, fontFamily:'"JetBrains Mono", monospace', letterSpacing:'0.02em'}}>
              <div style={{fontSize:11, letterSpacing:'0.22em', textTransform:'uppercase', marginBottom:16}}>⚠ Could not load {book.name}</div>
              <p style={{maxWidth:'50ch'}}>The book failed to fetch. Check your connection and try reloading the page, or pick a different chapter from the sidebar.</p>
            </div>
          ) : (
            // Book loaded but the requested chapter doesn't exist (e.g. URL
            // points past book.chapters). Rare — usually only happens with
            // hand-typed URLs.
            <div style={{padding:'40px 0', color:'var(--ink-3)', fontSize:14, lineHeight:1.7, fontFamily:'"JetBrains Mono", monospace', letterSpacing:'0.02em'}}>
              <div style={{fontSize:11, letterSpacing:'0.22em', textTransform:'uppercase', marginBottom:16}}>∅ No chapter {chapter} in {book.name}</div>
              <p style={{maxWidth:'50ch'}}>{book.name} has {book.chapters} chapters. Pick one from the sidebar.</p>
            </div>
          )}

          <div className="chapter-nav">
            <button onClick={prev}><span className="arrow">←</span> Previous</button>
            <span className="label">Chapter {chapter} of {book.chapters}</span>
            <button onClick={next}>Next <span className="arrow">→</span></button>
          </div>
        </article>

        {/* RIGHT: meta */}
        <aside className="meta">
          <div className="block">
            <div className="k label">Translation</div>
            <div className="v">
              <button
                onClick={() => setTransPickerOpen(true)}
                style={{textAlign:'left', borderBottom:'1px dotted var(--ink-3)'}}
                title="Change translation"
              >
                {meta.name || '—'}
              </button>
            </div>
          </div>
          {meta.year != null && (
            <div className="block">
              <div className="k label">Year</div>
              <div className="v">{meta.year}</div>
            </div>
          )}
          <div className="block">
            <div className="k label">Language</div>
            <div className="v">{meta.nativeName || meta.languageName || '—'}</div>
          </div>
          <div className="block">
            <div className="k label">License</div>
            <div className="v mono">Public Domain</div>
          </div>
          <div className="block">
            <div className="k label">Offline</div>
            <OfflineToggle slug={activeSlug} />
          </div>
          <div className="block">
            <div className="k label">Verses · chapter</div>
            <div className="v">{verses ? verses.length : '—'}</div>
          </div>
          <hr className="hrule-soft" />
          <div className="block">
            <div className="k label">Shortcuts</div>
            <div style={{marginTop:8, fontSize:11, letterSpacing:0.02, textTransform:'none', fontFamily:'"JetBrains Mono", monospace', lineHeight:1.9, color:'var(--ink)'}}>
              <div><span className="kbd">←</span> prev</div>
              <div><span className="kbd">→</span> next</div>
              <div><span className="kbd">+</span> / <span className="kbd">−</span> size</div>
              <div><span className="kbd">Esc</span> close</div>
            </div>
          </div>
          {selected && verses && (
            <div className="block" style={{borderTop:'1px solid var(--rule)', paddingTop:16}}>
              <div className="k label">Selected · v{selected}</div>
              <div style={{marginTop:10, display:'flex', flexDirection:'column', gap:6, fontFamily:'"JetBrains Mono", monospace', fontSize:10.5, letterSpacing:'0.14em', textTransform:'uppercase'}}>
                <button style={{textAlign:'left', borderBottom:'1px solid var(--rule-soft)', paddingBottom:6}}>Copy</button>
                <button style={{textAlign:'left', borderBottom:'1px solid var(--rule-soft)', paddingBottom:6}}>Share as image</button>
                <button style={{textAlign:'left'}}>Bookmark</button>
              </div>
            </div>
          )}
        </aside>
      </div>

      {pickerOpen && (
        <BookPicker
          onClose={() => setPickerOpen(false)}
          onPick={(b) => { setPickerOpen(false); navigate(readPath(b.id, 1)); }}
        />
      )}
      {transPickerOpen && (
        <TranslationPicker
          activeSlug={activeSlug}
          onClose={() => setTransPickerOpen(false)}
          onPick={(slug) => {
            setTransPickerOpen(false);
            // Keep the same book/chapter, but anchor to the new translation.
            navigate(`/read/${slug}/${book.id}/${chapter}`);
          }}
        />
      )}
    </main>
  );
}

function TranslationPicker({ activeSlug, onClose, onPick }) {
  const all = (window.TranslationLoader && window.TranslationLoader.list()) || [];
  // Group by language code, then sort groups by display order from apps_data
  // (if available) else alphabetically by languageName.
  const groups = {};
  for (const t of all) {
    const key = t.language || 'xx';
    (groups[key] = groups[key] || { code: key, languageName: t.languageName, nativeName: t.nativeName, items: [] }).items.push(t);
  }
  const order = ['en','de','es','fr','pt','ru','uk','sw','th','tl','to','tgp'];
  const ordered = order.filter(c => groups[c]).map(c => groups[c]);
  // Append any unknown languages at the end.
  for (const k of Object.keys(groups)) if (!order.includes(k)) ordered.push(groups[k]);

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <header>
          <span>Pick a translation · {all.length} available</span>
          <button onClick={onClose}>Close · Esc</button>
        </header>
        <div className="trans-grid">
          {ordered.map(g => (
            <React.Fragment key={g.code}>
              <div className="testament-header">
                {g.nativeName || g.languageName || g.code.toUpperCase()}
                <span style={{color:'var(--ink-4)', marginLeft:8, fontSize:10}}>{g.code.toUpperCase()} · {g.items.length}</span>
              </div>
              {[...g.items].sort((a,b) => (a.year || 0) - (b.year || 0)).map(t => (
                <div
                  key={t.slug}
                  className={`b ${t.slug === activeSlug ? 'active' : ''}`}
                  onClick={() => onPick(t.slug)}
                >
                  <span className="name">{t.name}</span>
                  <span className="meta-sm">{t.year || ''} · {t.scope === 'nt' ? 'NT' : t.scope === 'ot+nt+apocrypha' ? 'OT+NT+Apoc' : 'OT+NT'}</span>
                </div>
              ))}
            </React.Fragment>
          ))}
        </div>
      </div>
    </div>
  );
}

function BookPicker({ onClose, onPick }) {
  const ot = BIBLE_BOOKS.filter(b => b.testament === 'ot');
  const nt = BIBLE_BOOKS.filter(b => b.testament === 'nt');
  const apoc = BIBLE_BOOKS.filter(b => b.testament === 'apoc');
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={e => e.stopPropagation()}>
        <header>
          <span>Select a book</span>
          <button onClick={onClose}>Close · Esc</button>
        </header>
        <div className="book-grid">
          {ot.length > 0 && <div className="testament-header">Old Testament · {ot.length} books</div>}
          {ot.map(b => (
            <div key={b.id} className="b" onClick={() => onPick(b)}>
              <span className="name">{b.name}</span>
              <span className="meta-sm">{b.chapters} ch</span>
            </div>
          ))}
          {nt.length > 0 && <div className="testament-header">New Testament · {nt.length} books</div>}
          {nt.map(b => (
            <div key={b.id} className="b" onClick={() => onPick(b)}>
              <span className="name">{b.name}</span>
              <span className="meta-sm">{b.chapters} ch</span>
            </div>
          ))}
          {apoc.length > 0 && <div className="testament-header">Apocrypha · {apoc.length} books</div>}
          {apoc.map(b => (
            <div key={b.id} className="b" onClick={() => onPick(b)}>
              <span className="name">{b.name}</span>
              <span className="meta-sm">{b.chapters} ch</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* =========================================================================
   LANGUAGES
   Driven by APPS_BY_LANGUAGE (the catalog of live translations) plus a small
   hand-curated list of languages we're actively working on. No fabricated
   queue list — "7,000 languages" is the long-term goal, not a tracked column.
   ========================================================================= */
const IN_TRANSLATION = [
  // Our own 3-step translations in progress at ~/bible_translation/.
  // Add/remove as work moves forward. Keep this in sync with reality.
  { code: 'sv', native: 'Svenska', name: 'Swedish', note: 'Three-step literal · Genesis in progress' },
];

function Languages() {
  const live = window.APPS_BY_LANGUAGE || [];
  const liveCount = live.length;
  const translationCount = (window.APPS || []).length;
  const nextCount = IN_TRANSLATION.length;

  return (
    <main>
      <div className="container">
        <section className="lang-head">
          <div className="eyebrow">§ Languages · Translation program</div>
          <h1>Every language,<br/><em>in time.</em></h1>
          <p>
            We host public-domain Bibles in {liveCount} languages today, and are translating our own from
            Hebrew and Greek into more. Each translation we commission is released into the public domain.
            The goal is faithfulness, not speed.
          </p>
          <div className="lang-stats">
            <div className="c">
              <div className="n"><em>{liveCount}</em></div>
              <div className="l">Languages live</div>
            </div>
            <div className="c">
              <div className="n">{translationCount}</div>
              <div className="l">Translations live</div>
            </div>
            <div className="c">
              <div className="n">{nextCount}</div>
              <div className="l">In active translation</div>
            </div>
            <div className="c">
              <div className="n">~7,000</div>
              <div className="l">Living languages · Goal</div>
            </div>
          </div>
        </section>

        <section className="lang-table">
          <div style={{display:'flex', justifyContent:'space-between', margin:'0 0 20px', alignItems:'baseline'}}>
            <div className="label">Live now · {liveCount} languages · {translationCount} translations</div>
            <div className="label" style={{color:'var(--ink-4)'}}>● live  ○ in translation</div>
          </div>
          {live.map(g => (
            <div key={g.code} className="lang-row">
              <div className="code">{g.code}</div>
              <div className="native serif">{g.native}</div>
              <div className="en">{g.name}</div>
              <div className="status-cell now">● {g.apps.length} translation{g.apps.length === 1 ? '' : 's'}</div>
              <div className="formats"><span>web</span><span>android</span></div>
            </div>
          ))}
          {IN_TRANSLATION.length > 0 && (
            <>
              <div className="label" style={{margin:'40px 0 16px'}}>In active translation</div>
              {IN_TRANSLATION.map(l => (
                <div key={l.code} className="lang-row">
                  <div className="code">{l.code}</div>
                  <div className="native serif">{l.native}</div>
                  <div className="en">{l.name}</div>
                  <div className="status-cell next">○ {l.note}</div>
                  <div className="formats"><span>soon</span></div>
                </div>
              ))}
            </>
          )}
        </section>

        <section className="cta-band">
          <h3 className="serif">Translate with us.<br/>Your language is not too small.</h3>
          <a href="mailto:hello@offline.bible" className="btn">Become a translator →</a>
        </section>
      </div>
    </main>
  );
}

/* =========================================================================
   DOWNLOADS (standalone)
   ========================================================================= */
function Downloads({ navigate }) {
  const androidCount = (window.APPS || []).length;
  const langCount = (window.APPS_BY_LANGUAGE || []).length;
  return (
    <main>
      <div className="container">
        <section className="lang-head">
          <div className="eyebrow">§ Downloads · Free forever</div>
          <h1>Take the Bible<br/><em>everywhere.</em></h1>
          <p>
            Two formats are shipping today: the in-browser reader (you're using it) and a
            dedicated Android app for every translation we host. PDFs, EPUBs, iOS, and desktop
            builds are planned per translation as each one is finalized. No accounts, no DRM,
            redistribute freely.
          </p>
        </section>

        <section style={{padding:'40px 0 80px'}}>
          <div className="label" style={{marginBottom:20}}>Available today</div>
          <div className="downloads">
            <div className="dl-card">
              <div className="type">Android · Google Play</div>
              <div className="size">{androidCount}<span className="unit">apps</span></div>
              <div className="desc">A dedicated Play Store app per translation, across {langCount} languages. Install once and read forever — no account, no tracking, no ads.</div>
              <a
                className="cta"
                href="/apps"
                onClick={(e) => { e.preventDefault(); navigate && navigate('/apps'); }}
              >Browse apps <span className="arrow">→</span></a>
            </div>
            <div className="dl-card">
              <div className="type">Web · in-browser reader</div>
              <div className="size">{androidCount}<span className="unit">trans</span></div>
              <div className="desc">All {androidCount} translations, readable in any browser. Designed for offline use after first load — translations you've opened are cached locally.</div>
              <a
                className="cta"
                href="/read/king-james-bible/gen/1"
                onClick={(e) => { e.preventDefault(); navigate && navigate('/read/king-james-bible/gen/1'); }}
              >Open the reader <span className="arrow">→</span></a>
            </div>
          </div>

          <div className="label" style={{margin:'56px 0 20px'}}>Planned · rolling per translation</div>
          <div className="downloads">
            <div className="dl-card">
              <div className="type">PDF · per translation</div>
              <div className="size">—</div>
              <div className="desc">Print-ready typesetting. Generated and posted per translation as each one is finalized.</div>
              <a className="cta soon" href="#">Planned</a>
            </div>
            <div className="dl-card">
              <div className="type">EPUB · per translation</div>
              <div className="size">—</div>
              <div className="desc">For e-readers. Reflowable, with working table of contents.</div>
              <a className="cta soon" href="#">Planned</a>
            </div>
            <div className="dl-card">
              <div className="type">iOS · TestFlight</div>
              <div className="size">—</div>
              <div className="desc">Native iOS app. Same design, same principles.</div>
              <a className="cta soon" href="#">Planned · 2026</a>
            </div>
            <div className="dl-card">
              <div className="type">Desktop · Tauri</div>
              <div className="size">—</div>
              <div className="desc">macOS, Windows, Linux. Tiny binaries. Full-screen reading mode.</div>
              <a className="cta soon" href="#">Planned · 2026</a>
            </div>
          </div>

          <div className="label" style={{margin:'56px 0 20px'}}>Source · For developers & translators</div>
          <div className="downloads">
            <div className="dl-card">
              <div className="type">JSON · per translation</div>
              <div className="size">~4<span className="unit">mb</span></div>
              <div className="desc">Each translation is served as <span className="mono">data/&lt;slug&gt;.json</span> — same files the reader fetches. Use them directly for embeds or redistribution.</div>
              <a className="cta" href="data/index.json">data/index.json <span className="arrow">↓</span></a>
            </div>
            <div className="dl-card">
              <div className="type">Plain Text · UTF-8</div>
              <div className="size">—</div>
              <div className="desc">One verse per line. Coming once the per-translation pipeline ships PDFs and EPUBs.</div>
              <a className="cta soon" href="#">Planned</a>
            </div>
            <div className="dl-card">
              <div className="type">USFM · source files</div>
              <div className="size">—</div>
              <div className="desc">Standard Unified Scripture Format. For translation teams.</div>
              <a className="cta soon" href="#">Planned</a>
            </div>
            <div className="dl-card">
              <div className="type">Git · source repo</div>
              <div className="size">—</div>
              <div className="desc">All site source. All history. Open code, public-domain text.</div>
              <a className="cta soon" href="#">Planned</a>
            </div>
          </div>
        </section>
      </div>
    </main>
  );
}

Object.assign(window, { Reader, BookPicker, TranslationPicker, Languages, Downloads });
