// Anatomy explorer + class library + exercise encyclopedia for pilates.exe
// Exposes window.AnatomyExplorer, window.ClassLibrary, window.ExerciseEncyclopedia
// Plus: modal stack, exercise detail, class detail, auth modal — all functional.

const { useState, useMemo, useRef, useEffect, useCallback } = React;

/* ─── GLOBAL MODAL STORE ─── */
// A tiny pub/sub so any component (or vanilla JS in index.html) can open modals.
const ModalStore = (() => {
  const listeners = new Set();
  let state = { kind: null, payload: null };
  function set(next) { state = next; listeners.forEach(fn => fn(state)); }
  function open(kind, payload) { set({ kind, payload }); document.body.style.overflow = "hidden"; }
  function close() { set({ kind: null, payload: null }); document.body.style.overflow = ""; }
  function subscribe(fn) { listeners.add(fn); fn(state); return () => listeners.delete(fn); }
  return { open, close, subscribe };
})();
window.PilatesModal = ModalStore;

function useModalState() {
  const [s, setS] = useState({ kind: null, payload: null });
  useEffect(() => ModalStore.subscribe(setS), []);
  return s;
}

/* ─── MODAL ROOT — single mount, switches by kind ─── */
function ModalRoot() {
  const { kind, payload } = useModalState();
  useEffect(() => {
    function onKey(e) { if (e.key === "Escape") ModalStore.close(); }
    if (kind) window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [kind]);
  if (!kind) return null;
  return (
    <div className="modal-overlay" onClick={ModalStore.close}>
      <div className="modal-panel" onClick={e => e.stopPropagation()}>
        <button className="modal-close" onClick={ModalStore.close} aria-label="Close">×</button>
        {kind === "exercise" && <ExerciseModal id={payload.id} from={payload.from} />}
        {kind === "class" && <ClassModal cls={payload.cls} />}
      </div>
    </div>
  );
}

/* ─── EXERCISE DETAIL MODAL ─── */
function ExerciseModal({ id, from }) {
  // Local state version: re-renders the modal if the image manifest finishes
  // loading while it's open. (NOP in most cases — modal opens after boot.)
  const [_imgV, _setImgV] = useState(0);
  useEffect(() => {
    const h = () => _setImgV(v => v + 1);
    window.addEventListener("ex-images-loaded", h);
    return () => window.removeEventListener("ex-images-loaded", h);
  }, []);
  // Track whether the detail bundle has arrived so we can re-render on load.
  const [detailLoaded, setDetailLoaded] = useState(!!window.PilatesExerciseDetail);
  useEffect(() => {
    if (window.PilatesExerciseDetail) return;
    const h = () => setDetailLoaded(true);
    window.addEventListener("pilates:detail-loaded", h);
    return () => window.removeEventListener("pilates:detail-loaded", h);
  }, []);

  // Exercise detail data is member-gated. If it hasn't loaded yet, show the
  // appropriate state: loading spinner for signed-in users, lock gate for guests.
  if (!detailLoaded) {
    const isSignedIn = !!(window.Clerk?.user);
    if (isSignedIn) {
      return (
        <div className="ex-modal">
          <div className="lock-gate" style={{ border: 0 }}>
            <div className="lock-gate-eyebrow mono" style={{ marginTop: 40 }}>Loading…</div>
            <p className="lock-gate-text">Fetching exercise details.</p>
          </div>
        </div>
      );
    }
    return (
      <div className="ex-modal">
        <div className="lock-gate" style={{ border: 0 }}>
          <div className="lock-gate-mark">🔒</div>
          <div className="lock-gate-eyebrow mono">Members only</div>
          <h3 className="lock-gate-title serif">Exercise prescriptions are a member feature</h3>
          <p className="lock-gate-text">Sign in to view the full breakdown — setup, breath, execution and progressions.</p>
          <div className="lock-gate-cta">
            <button className="lock-gate-btn solid" onClick={() => window.openAuth("signup")}>Create account →</button>
            <button className="lock-gate-btn" onClick={() => window.openAuth("signin")}>Sign in</button>
          </div>
        </div>
      </div>
    );
  }
  const detail = window.PilatesExerciseDetail.get(id);
  const { MUSCLES } = window.PilatesAnatomy;
  if (!detail) return <div style={{ padding: 40 }}>Exercise not found.</div>;
  const muscleObjs = detail.muscles.map(m => MUSCLES[m]).filter(Boolean);

  return (
    <div className="ex-modal">
      <div className="ex-modal-head">
        <div className="ex-modal-meta mono">
          <span>{detail.id}</span>
          <span>·</span>
          <span>{detail.apparatus}</span>
          <span>·</span>
          <span data-level={detail.level}>{detail.level}</span>
          <span>·</span>
          <span>{detail.duration}</span>
        </div>
        <h2 className="ex-modal-title serif">{detail.name}</h2>
        <div className="ex-modal-focus">{detail.focus}</div>
      </div>

      <div className="ex-modal-body">
        {(() => {
          // When a YouTube URL is registered for this exercise, the hero
          // becomes a clickable thumbnail (max-res with sd fallback) that
          // opens the video. Otherwise: fall back to the local-image /
          // SVG line-art path via the visual engine.
          const ytId = window.ExerciseVideos?.getYouTubeId(detail.id);
          if (ytId && !window.ExerciseDiagram?.hasImage(detail.id)) {
            const videoUrl = window.ExerciseVideos.get(detail.id).youtube;
            return (
              <a
                href={videoUrl}
                target="_blank"
                rel="noopener noreferrer"
                className="ex-modal-diagram ex-modal-diagram-link"
                aria-label={`Watch ${detail.name} on YouTube`}
              >
                <img
                  src={`https://i.ytimg.com/vi/${ytId}/maxresdefault.jpg`}
                  alt={detail.name}
                  loading="lazy"
                  decoding="async"
                  className="ex-img ex-yt-thumb"
                  onError={(e) => {
                    if (!e.currentTarget.dataset.f) {
                      e.currentTarget.dataset.f = "1";
                      e.currentTarget.src = `https://i.ytimg.com/vi/${ytId}/sddefault.jpg`;
                    } else if (e.currentTarget.dataset.f === "1") {
                      e.currentTarget.dataset.f = "2";
                      e.currentTarget.src = `https://i.ytimg.com/vi/${ytId}/hqdefault.jpg`;
                    }
                  }}
                />
                <span className="ex-yt-badge" aria-hidden="true">▶</span>
              </a>
            );
          }
          if (window.ExerciseDiagram) {
            return (
              <div className="ex-modal-diagram"
                dangerouslySetInnerHTML={{ __html: window.ExerciseDiagram.svg(detail.id) }}
              />
            );
          }
          return null;
        })()}

        {/* Video reference buttons — search YouTube + Pilatesology by exercise name.
            Direct URLs added in shared/exercise-videos.js override map take precedence. */}
        {window.ExerciseVideos && (() => {
          const v = window.ExerciseVideos.get(detail.id);
          if (!v) return null;
          return (
            <div className="ex-modal-videos">
              <div className="ex-modal-label mono">{tt("modal.video.heading")}</div>
              <div className="ex-modal-video-row">
                <a
                  className="ex-modal-video-btn yt"
                  href={v.youtube}
                  target="_blank"
                  rel="noopener noreferrer"
                  title={v.directYoutube ? tt("modal.video.direct") : tt("modal.video.search")}
                >
                  <span className="ex-modal-video-mark">▶</span>
                  <span className="ex-modal-video-label">
                    {tt("modal.video.youtube")}
                    {!v.directYoutube && <span className="ex-modal-video-tag"> · {tt("modal.video.search")}</span>}
                  </span>
                  <span className="ex-modal-video-arrow">↗</span>
                </a>
                <a
                  className="ex-modal-video-btn pi"
                  href={v.pilatesology}
                  target="_blank"
                  rel="noopener noreferrer"
                  title={v.directPilatesology ? tt("modal.video.direct") : tt("modal.video.search")}
                >
                  <span className="ex-modal-video-mark">▶</span>
                  <span className="ex-modal-video-label">
                    {tt("modal.video.pilatesology")}
                    {!v.directPilatesology && <span className="ex-modal-video-tag"> · {tt("modal.video.search")}</span>}
                  </span>
                  <span className="ex-modal-video-arrow">↗</span>
                </a>
              </div>
            </div>
          );
        })()}

        <p className="ex-modal-summary">{detail.summary}</p>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">{tt("modal.setup")}</div>
          <p>{detail.setup}</p>
        </div>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">{tt("modal.breath")}</div>
          <p>{detail.breath}</p>
        </div>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">{tt("modal.execution")}</div>
          <ol className="ex-modal-list">
            {detail.execution.map((step, i) => (
              <li key={i}><span className="ex-modal-num">{String(i + 1).padStart(2, "0")}</span>{step}</li>
            ))}
          </ol>
        </div>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">{tt("modal.avoid")}</div>
          <ul className="ex-modal-bullet">
            {detail.avoid.map((a, i) => <li key={i}>{a}</li>)}
          </ul>
        </div>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">{tt("modal.modifications")}</div>
          <ul className="ex-modal-bullet">
            {detail.modifications.map((m, i) => <li key={i}>{m}</li>)}
          </ul>
        </div>

        <div className="ex-modal-grid">
          <div>
            <div className="ex-modal-label mono">{tt("modal.prescription")}</div>
            <div className="ex-modal-data serif">{detail.reps}</div>
          </div>
          <div>
            <div className="ex-modal-label mono">{tt("modal.sequence")}</div>
            <div className="ex-modal-data serif">No. {detail.order}</div>
          </div>
          <div>
            <div className="ex-modal-label mono">{tt("modal.muscles")}</div>
            <div className="ex-modal-tags">
              {muscleObjs.slice(0, 4).map(m => (
                <button key={m.label} className="ex-modal-tag" onClick={() => {
                  ModalStore.close();
                  setTimeout(() => {
                    document.getElementById("anatomy")?.scrollIntoView({ behavior: "smooth" });
                    window.PilatesAnatomy.__pendingMuscle = Object.entries(MUSCLES).find(([id, mm]) => mm.label === m.label)?.[0];
                    window.dispatchEvent(new Event("pilates:select-muscle"));
                  }, 100);
                }}>{m.label}</button>
              ))}
            </div>
          </div>
        </div>

        {detail.origin && (
          <div className="ex-modal-origin">
            <span className="mono">// origin · </span>{detail.origin}
          </div>
        )}
      </div>

      <div className="ex-modal-foot">
        <button className="ex-modal-btn-ghost mono" onClick={ModalStore.close}>← back to {from || "encyclopedia"}</button>
      </div>
    </div>
  );
}

/* ─── CLASS DETAIL MODAL ─── */
function ClassModal({ cls }) {
  const { MUSCLES } = window.PilatesAnatomy;
  const muscleObjs = cls.muscles.map(m => MUSCLES[m]).filter(Boolean);
  return (
    <div className="ex-modal">
      <div className="cls-modal-hero">
        <img src={cls.photo} alt={cls.title} referrerPolicy="no-referrer" />
        <div className="cls-modal-overlay" />
        <div className="cls-modal-hero-text">
          <div className="ex-modal-meta mono">
            <span>{cls.id}</span><span>·</span>
            <span>{cls.apparatus}</span><span>·</span>
            <span>{cls.level}</span><span>·</span>
            <span>{cls.duration}</span>
          </div>
          <h2 className="cls-modal-title serif">{cls.title}</h2>
          <div className="cls-modal-subtitle">{cls.subtitle}</div>
        </div>
      </div>

      <div className="ex-modal-body">
        <p className="ex-modal-summary">{cls.summary}</p>

        <div className="ex-modal-grid">
          <div>
            <div className="ex-modal-label mono">Instructor</div>
            <div className="ex-modal-data serif">{cls.teacher}</div>
          </div>
          <div>
            <div className="ex-modal-label mono">Targeted</div>
            <div className="ex-modal-tags">
              {muscleObjs.map(m => <span key={m.label} className="ex-modal-tag-static">{m.label}</span>)}
            </div>
          </div>
          <div>
            <div className="ex-modal-label mono">Length</div>
            <div className="ex-modal-data serif">{cls.duration}</div>
          </div>
        </div>

        <div className="ex-modal-section">
          <div className="ex-modal-label mono">Sequence · {cls.sequence.length} exercises</div>
          <div className="cls-modal-seq">
            {cls.sequence.map((exId, i) => {
              const ex = window.PilatesAnatomy.EXERCISES.find(e => e.id === exId);
              if (!ex) return null;
              return (
                <button key={exId} className="cls-modal-seq-row" onClick={() => ModalStore.open("exercise", { id: exId, from: "session" })}>
                  <span className="cls-modal-seq-n mono">{String(i + 1).padStart(2, "0")}</span>
                  <span className="cls-modal-seq-id mono">{ex.id}</span>
                  <span className="cls-modal-seq-name serif">{ex.name}</span>
                  <span className="cls-modal-seq-focus mono">{ex.focus}</span>
                  <span className="cls-modal-seq-dur mono">{ex.duration}</span>
                  <span className="cls-modal-seq-arrow">→</span>
                </button>
              );
            })}
          </div>
        </div>
      </div>

      <div className="ex-modal-foot">
        <button className="ex-modal-btn-ghost mono" onClick={ModalStore.close}>← back to library</button>
        <button className="ex-modal-btn-solid mono" onClick={() => window.openAuth("signup")}>▷ start session</button>
      </div>
    </div>
  );
}

/* ─── ANATOMY EXPLORER ─── */
function AnatomyExplorer() {
  const A = window.PilatesAnatomy;
  const [view, setView] = useState("front");
  const [layer, setLayer] = useState("super");
  const [hover, setHover] = useState(null);
  const [selected, setSelected] = useState(null);

  // Cross-link from exercise modal: an event sets __pendingMuscle, then we react to it.
  useEffect(() => {
    function onSelect() {
      const id = window.PilatesAnatomy.__pendingMuscle;
      if (!id) return;
      const m = A.MUSCLES[id];
      if (!m) return;
      if (m.view !== view) setView(m.view);
      if (id === "multifidus" && layer !== "deep") setLayer("deep");
      setSelected(id);
      window.PilatesAnatomy.__pendingMuscle = null;
    }
    window.addEventListener("pilates:select-muscle", onSelect);
    return () => window.removeEventListener("pilates:select-muscle", onSelect);
  }, [view, layer, A.MUSCLES]);
  const [cursor, setCursor] = useState({ x: 0, y: 0, visible: false });
  const figRef = useRef(null);
  const rafRef = useRef(null);
  const pendingRef = useRef(null);

  const paths = view === "front" ? A.FRONT : A.BACK;
  const striations = view === "front" ? A.STRIATIONS_FRONT : A.STRIATIONS_BACK;
  const landmarks = view === "front" ? A.LANDMARKS_FRONT : A.LANDMARKS_BACK;

  const visibleMuscles = useMemo(() => {
    return Object.entries(A.MUSCLES).filter(([id, m]) => {
      if (m.view !== view) return false;
      return layer === "deep" ? !!m.deep : !m.deep;
    });
  }, [view, layer]);

  const activeMuscle = selected ? A.MUSCLES[selected] : null;

  function onMove(e) {
    if (!figRef.current) return;
    const r = figRef.current.getBoundingClientRect();
    const clientX = e.touches ? e.touches[0].clientX : e.clientX;
    const clientY = e.touches ? e.touches[0].clientY : e.clientY;
    pendingRef.current = { x: clientX - r.left, y: clientY - r.top, visible: true };
    if (!rafRef.current) {
      rafRef.current = requestAnimationFrame(() => {
        rafRef.current = null;
        if (pendingRef.current) setCursor(pendingRef.current);
      });
    }
  }
  useEffect(() => () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); }, []);

  useEffect(() => {
    function onKey(e) {
      if (e.key === "Escape") setSelected(null);
      if (e.key === "f") { setView("front"); setSelected(null); }
      if (e.key === "b") { setView("back"); setSelected(null); }
      if (e.key === "d") setLayer(l => l === "deep" ? "super" : "deep");
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  function selectMuscle(id) {
    setSelected(prev => prev === id ? null : id);
  }

  const GRAD_ID_MAP = {
    pec: "muscleWarm", delt: "muscleCool", delt_back: "muscleCool",
    rectus_abdominis: "muscleWarm", oblique: "muscleWarm", serratus: "muscleCool",
    bicep: "muscleCool", tricep: "muscleCool", forearm: "muscleCool",
    trapezius: "muscleCool", lat: "muscleCool",
    erector: "muscleDeep", multifidus: "muscleDeep",
    quad: "muscleWarm", adductor: "muscleWarm",
    glute: "muscleWarm", glute_med: "muscleWarm",
    hamstring: "muscleWarm", calf: "muscleCool", tib: "muscleCool",
  };

  return (
    <div className="anatomy-grid">
      {/* LEFT: controls + index */}
      <aside className="anatomy-left">
        <div className="mono" style={{ fontSize: 11, letterSpacing: "0.12em", color: "var(--ink-2)", textTransform: "uppercase", marginBottom: 8 }}>
          · plate {view === "front" ? "01.A" : "01.B"} · {layer === "deep" ? "deep layer" : "superficial"}
        </div>
        <h3 className="serif">
          {view === "front" ? "Anterior view" : "Posterior view"}
          {layer === "deep" && <em style={{ color: "var(--accent)", fontStyle: "italic" }}> — deep</em>}
        </h3>
        <p>
          {hover
            ? <span><strong>{A.MUSCLES[hover].label}</strong> — {A.MUSCLES[hover].fn}</span>
            : "Hover any muscle group to identify it. Click to open its prescription — function, clinical note, and the classical exercises that recruit it."}
        </p>

        <div className="ctrl-label">View <span style={{ color: "var(--ink-3)", letterSpacing: 0 }}>· press f / b</span></div>
        <div className="ctrl-row" role="tablist">
          <button data-active={view === "front"} onClick={() => { setView("front"); setSelected(null); }}>Anterior</button>
          <button data-active={view === "back"} onClick={() => { setView("back"); setSelected(null); }}>Posterior</button>
        </div>

        <div className="ctrl-label">Layer <span style={{ color: "var(--ink-3)", letterSpacing: 0 }}>· d</span></div>
        <div className="ctrl-row">
          <button data-active={layer === "super"} onClick={() => { setLayer("super"); setSelected(null); }}>Superficial</button>
          <button data-active={layer === "deep"} onClick={() => { setLayer("deep"); setSelected(null); }}>Deep</button>
        </div>

        <div className="muscle-index-wrap" style={{ marginTop: 22, paddingTop: 14, borderTop: "1px solid var(--rule)" }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: "0.12em", color: "var(--ink-2)", textTransform: "uppercase", marginBottom: 10 }}>
            Index · {visibleMuscles.length} groups
          </div>
          <div className="muscle-index-list">
            {visibleMuscles.map(([id, m]) => (
              <button
                key={id}
                onMouseEnter={() => setHover(id)}
                onMouseLeave={() => setHover(null)}
                onClick={() => selectMuscle(id)}
                className="muscle-index-row"
                data-active={selected === id || hover === id}
              >
                <span className="muscle-index-dot" />
                <span className="muscle-index-name">{m.label}</span>
                <span className="muscle-index-count">{m.exercises.length}</span>
              </button>
            ))}
          </div>
        </div>
      </aside>

      {/* CENTER: SVG figure */}
      <div
        className="anatomy-figure"
        ref={figRef}
        onMouseMove={onMove}
        onMouseLeave={() => setCursor(c => ({ ...c, visible: false }))}
      >
        <span className="anatomy-corner-tl">FIG. {view === "front" ? "01.A" : "01.B"} — {layer.toUpperCase()}</span>
        <span className="anatomy-corner-tr">pilates.exe · anatomy.001</span>
        <span className="anatomy-corner-bl">scale 1:8 · ⌨ f/b/d</span>
        <span className="anatomy-corner-br">{visibleMuscles.length} groups</span>

        <svg className="anatomy-svg" viewBox="0 0 400 720" preserveAspectRatio="xMidYMid meet">
          <defs>
            {/* Skin-tone gradient for the body silhouette */}
            <linearGradient id="bodySkin" x1="20%" y1="0%" x2="80%" y2="100%">
              <stop offset="0%" stopColor="#e8e0d4" stopOpacity="0.90" />
              <stop offset="40%" stopColor="#ddd4c6" stopOpacity="0.82" />
              <stop offset="100%" stopColor="#c8bcaa" stopOpacity="0.72" />
            </linearGradient>

            {/* Resting muscle — always visible, warm anatomical red */}
            <radialGradient id="muscleRest" cx="40%" cy="32%" r="64%">
              <stop offset="0%" stopColor="#c84030" stopOpacity="0.42" />
              <stop offset="55%" stopColor="#9e2816" stopOpacity="0.34" />
              <stop offset="100%" stopColor="#6e1408" stopOpacity="0.26" />
            </radialGradient>

            {/* Hover — bright, full depth */}
            <radialGradient id="muscleHover" cx="36%" cy="28%" r="66%">
              <stop offset="0%" stopColor="#e05030" stopOpacity="0.88" />
              <stop offset="35%" stopColor="#c03020" stopOpacity="0.84" />
              <stop offset="75%" stopColor="#901c0e" stopOpacity="0.80" />
              <stop offset="100%" stopColor="#601008" stopOpacity="0.76" />
            </radialGradient>

            {/* Selected — full 3D medical-illustration depth */}
            <radialGradient id="muscleActive" cx="32%" cy="24%" r="70%">
              <stop offset="0%" stopColor="#f06040" stopOpacity="1" />
              <stop offset="20%" stopColor="#d03820" stopOpacity="0.98" />
              <stop offset="55%" stopColor="#9e1e10" stopOpacity="0.96" />
              <stop offset="100%" stopColor="#580e06" stopOpacity="0.93" />
            </radialGradient>

            {/* Warm anatomical muscles (anterior chain) — resting */}
            <radialGradient id="muscleWarm" cx="38%" cy="30%" r="66%">
              <stop offset="0%" stopColor="#c84030" stopOpacity="0.42" />
              <stop offset="55%" stopColor="#a02818" stopOpacity="0.34" />
              <stop offset="100%" stopColor="#6e1408" stopOpacity="0.26" />
            </radialGradient>

            {/* Cooler / deeper muscles — resting */}
            <radialGradient id="muscleCool" cx="40%" cy="34%" r="62%">
              <stop offset="0%" stopColor="#b83820" stopOpacity="0.38" />
              <stop offset="55%" stopColor="#942010" stopOpacity="0.30" />
              <stop offset="100%" stopColor="#641008" stopOpacity="0.22" />
            </radialGradient>

            {/* Deep stabilisers (multifidus, erector) — resting */}
            <radialGradient id="muscleDeep" cx="50%" cy="40%" r="58%">
              <stop offset="0%" stopColor="#a02c18" stopOpacity="0.36" />
              <stop offset="100%" stopColor="#6a1810" stopOpacity="0.26" />
            </radialGradient>

            {/* Glow filter for selected state */}
            <filter id="glowSel" x="-25%" y="-25%" width="150%" height="150%">
              <feGaussianBlur in="SourceAlpha" stdDeviation="4.5" result="blurred" />
              <feFlood floodColor="#c83520" floodOpacity="0.45" result="colored" />
              <feComposite in="colored" in2="blurred" operator="in" result="glow" />
              <feMerge>
                <feMergeNode in="glow" />
                <feMergeNode in="SourceGraphic" />
              </feMerge>
            </filter>

            {/* Lighter glow for hover */}
            <filter id="glowHov" x="-15%" y="-15%" width="130%" height="130%">
              <feGaussianBlur in="SourceAlpha" stdDeviation="2.5" result="blurred" />
              <feFlood floodColor="#b83420" floodOpacity="0.28" result="colored" />
              <feComposite in="colored" in2="blurred" operator="in" result="glow" />
              <feMerge>
                <feMergeNode in="glow" />
                <feMergeNode in="SourceGraphic" />
              </feMerge>
            </filter>

            {/* Subtle crosshatch texture — overlaid on active muscles */}
            <pattern id="fiberPat" x="0" y="0" width="5" height="5" patternUnits="userSpaceOnUse" patternTransform="rotate(32)">
              <line x1="0" y1="0" x2="0" y2="5" stroke="rgba(255,255,255,0.09)" strokeWidth="1" />
            </pattern>

            {/* Rim highlight — top-left specular */}
            <linearGradient id="rimLight" x1="0%" y1="0%" x2="100%" y2="100%">
              <stop offset="0%" stopColor="rgba(255,255,255,0.18)" />
              <stop offset="40%" stopColor="rgba(255,255,255,0.0)" />
            </linearGradient>
          </defs>

          {/* ── Background anatomy plate lines (cross-ruling) ── */}
          <g opacity="0.04" stroke="#0f0f10" strokeWidth="0.3" fill="none">
            {[...Array(12)].map((_, i) => (
              <line key={`h${i}`} x1="0" y1={60 * i} x2="400" y2={60 * i} />
            ))}
          </g>

          {/* ── Center axis ── */}
          <line x1="200" y1="18" x2="200" y2="700" stroke="#14141415" strokeWidth="0.4" strokeDasharray="2.5,3.5" />

          {/* ── Body silhouette — warm skin tone underlay ── */}
          <path d={paths.body} fill="url(#bodySkin)" stroke="#9e8e7838" strokeWidth="0.6" strokeLinejoin="round" />
          <path d={paths.head_face} fill="#ece4d850" stroke="#9e8e7828" strokeWidth="0.4" />
          {paths.neck && <path d={paths.neck} fill="#e4dace44" stroke="#9e8e7818" strokeWidth="0.3" />}

          {/* ── Skeletal landmarks ── */}
          <g>
            {landmarks.map(l => (
              <path key={l.key} d={l.d} stroke="#8a7a6828" strokeWidth="0.6" fill="none" strokeDasharray="2.5,2.5" />
            ))}
          </g>

          {/* ── Muscles ── */}
          {visibleMuscles.map(([id, m]) => {
            const isHov = hover === id;
            const isSel = selected === id;
            const isActive = isHov || isSel;
            const gradId = isSel ? "muscleActive" : isHov ? "muscleHover" : "muscleRest";
            const filterId = isSel ? "url(#glowSel)" : isHov ? "url(#glowHov)" : "none";
            const stroke = isSel ? "#58100648" : isHov ? "#7a1c0e60" : "#9e281648";
            const strokeW = isSel ? 0.9 : isHov ? 0.7 : 0.5;

            return (
              <g
                key={id}
                onMouseEnter={() => setHover(id)}
                onMouseLeave={() => setHover(null)}
                onClick={() => selectMuscle(id)}
                className="muscle-group"
              >
                {/* Shadow layer — slight depth behind each muscle */}
                {!isActive && m.paths.map(p => paths[p] && (
                  <path
                    key={`s${p}`}
                    d={paths[p]}
                    fill="none"
                    stroke="#5a140818"
                    strokeWidth="2.5"
                    strokeLinejoin="round"
                    style={{ pointerEvents: "none" }}
                  />
                ))}

                {/* Base fill */}
                {m.paths.map(p => paths[p] && (
                  <path
                    key={p}
                    className="muscle"
                    d={paths[p]}
                    fill={`url(#${gradId})`}
                    stroke={stroke}
                    strokeWidth={strokeW}
                    strokeLinejoin="round"
                    filter={filterId}
                    style={{ transition: "fill 0.2s ease, stroke 0.2s ease, filter 0.28s ease" }}
                  />
                ))}

                {/* Fiber striations — always visible, brighter on hover/select */}
                {m.paths.flatMap(p => (striations[p] || [])).map((d, i) => (
                  <path
                    key={`f${i}`}
                    d={d}
                    stroke={isActive ? "rgba(255,255,255,0.26)" : "rgba(255,255,255,0.10)"}
                    strokeWidth={isActive ? 0.55 : 0.4}
                    strokeLinecap="round"
                    fill="none"
                    style={{ pointerEvents: "none", transition: "stroke 0.2s ease" }}
                  />
                ))}

                {/* Crosshatch texture overlay on active */}
                {isActive && m.paths.map(p => paths[p] && (
                  <path
                    key={`t${p}`}
                    d={paths[p]}
                    fill="url(#fiberPat)"
                    style={{ pointerEvents: "none" }}
                  />
                ))}

                {/* Rim specular on selected */}
                {isSel && m.paths.map(p => paths[p] && (
                  <path
                    key={`r${p}`}
                    d={paths[p]}
                    fill="url(#rimLight)"
                    style={{ pointerEvents: "none" }}
                  />
                ))}
              </g>
            );
          })}

          {/* ── Leader labels for selected muscle ── */}
          {activeMuscle && activeMuscle.paths.map(p => {
            const d = paths[p];
            const match = d && d.match(/^M([-\d.]+),([-\d.]+)/);
            if (!match) return null;
            const sx = parseFloat(match[1]);
            const sy = parseFloat(match[2]);
            const right = sx > 200;
            const labelX = right ? Math.min(sx + 62, 380) : Math.max(sx - 62, 20);
            const anchor = right ? "start" : "end";
            return (
              <g key={p + "-lbl"}>
                <circle cx={sx} cy={sy} r="2" fill="var(--accent)" opacity="0.9" />
                <path d={`M${sx},${sy} L${labelX},${sy}`} stroke="var(--ink)" strokeWidth="0.35" fill="none" />
                <text className="lbl" x={labelX + (right ? 3 : -3)} y={sy + 1.8} textAnchor={anchor}>
                  {activeMuscle.label}
                </text>
                <text className="lbl-latin" x={labelX + (right ? 3 : -3)} y={sy + 8} textAnchor={anchor}>
                  {activeMuscle.latin}
                </text>
              </g>
            );
          })}
        </svg>

        {/* Floating cursor tooltip */}
        {hover && cursor.visible && (
          <div
            className="anatomy-cursor"
            style={{
              left: cursor.x,
              top: cursor.y,
              transform: cursor.x > (figRef.current?.offsetWidth ?? 400) * 0.65
                ? "translate(calc(-100% - 16px), -50%)"
                : "translate(16px, -50%)",
            }}
          >
            <span className="ac-name">{A.MUSCLES[hover].label}</span>
            <span className="ac-meta">{A.MUSCLES[hover].group} · {A.MUSCLES[hover].exercises.length} exercises</span>
          </div>
        )}
      </div>

      {/* RIGHT: detail pane */}
      <aside className="anatomy-detail">
        {!activeMuscle
          ? <PlaceholderPane setView={setView} setLayer={setLayer} setSelected={setSelected} view={view} layer={layer} A={A} />
          : <DetailPane m={activeMuscle} onClose={() => setSelected(null)} />
        }
      </aside>
    </div>
  );
}

function PlaceholderPane({ setView, setLayer, setSelected, view, layer, A }) {
  const suggested = ["rectus_abdominis", "glute", "serratus", "multifidus"];
  return (
    <div className="placeholder">
      <div className="mono" style={{ fontSize: 11, letterSpacing: "0.12em", color: "var(--ink-2)", textTransform: "uppercase" }}>
        · prescription pane
      </div>
      <p>Select any muscle from the figure — the pane opens with anatomy, clinical pattern, and the canonical Pilates exercises that recruit it.</p>
      <p className="arrow mono">← click any muscle, or jump below</p>
      <div style={{ marginTop: 14, paddingTop: 16, borderTop: "1px solid var(--rule)" }}>
        <div className="mono" style={{ fontSize: 10, letterSpacing: "0.12em", color: "var(--ink-2)", textTransform: "uppercase", marginBottom: 10 }}>
          Suggested
        </div>
        {suggested.map(id => {
          const m = A.MUSCLES[id];
          if (!m) return null;
          return (
            <button
              key={id}
              onClick={() => {
                if (m.view !== view) setView(m.view);
                if (id === "multifidus" && layer !== "deep") setLayer("deep");
                setSelected(id);
              }}
              style={{ display: "block", padding: "11px 0", borderTop: "1px solid var(--rule-2)", width: "100%", textAlign: "left" }}
            >
              <span className="mono" style={{ fontSize: 10, letterSpacing: "0.06em", color: "var(--ink-2)", textTransform: "uppercase" }}>{m.group}</span>
              <div className="serif" style={{ fontSize: 19, marginTop: 2 }}>{m.label} <span style={{ color: "var(--accent)" }}>→</span></div>
              <span className="mono" style={{ fontSize: 10, color: "var(--ink-3)", letterSpacing: "0.06em" }}>{m.exercises.length} exercises indexed</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function DetailPane({ m, onClose }) {
  return (
    <>
      <div className="detail-group">{m.group} · {m.view === "front" ? "anterior" : "posterior"}</div>
      <div className="detail-name serif">{m.label}</div>
      <div className="detail-latin">{m.latin}</div>

      <div className="detail-row">
        <div className="k">Function</div>
        <div className="v">{m.fn}</div>
      </div>
      <div className="detail-row">
        <div className="k">Clinical</div>
        <div className="v">{m.clinical}</div>
      </div>

      <div className="exercise-head">
        <span>· Recruited by</span>
        <span>{m.exercises.length} exercises</span>
      </div>

      {m.exercises.slice(0, 14).map(ex => (
        <button
          className="exercise exercise-clickable"
          key={ex.id}
          onClick={() => ModalStore.open("exercise", { id: ex.id, from: "muscle" })}
        >
          <div className="ex-id">{ex.id}</div>
          <div className="ex-name">
            {ex.name}
            <span className="sub">{ex.apparatus} · {ex.level}</span>
          </div>
          <div className="ex-meta">{ex.duration} <span className="ex-arrow">→</span></div>
        </button>
      ))}

      {m.exercises.length > 14 && (
        <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.06em", padding: "10px 0", textAlign: "center" }}>
          · + {m.exercises.length - 14} more in the encyclopedia ↓
        </div>
      )}

      <div style={{ marginTop: 22, paddingTop: 14, borderTop: "1px solid var(--rule)", display: "flex", gap: 10 }}>
        <button
          className="mono"
          onClick={onClose}
          style={{ fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", padding: "10px 14px", border: "1px solid var(--rule)", cursor: "pointer" }}
        >
          ← back
        </button>
        <a
          className="mono"
          href="#encyclopedia"
          style={{ fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", padding: "10px 14px", background: "var(--ink)", color: "var(--paper)", textDecoration: "none" }}
        >
          encyclopedia →
        </a>
      </div>
    </>
  );
}

/* ─── CLASS LIBRARY ─── */
function ClassLibrary() {
  const { CLASSES, MUSCLES } = window.PilatesAnatomy;
  const [filter, setFilter] = useState("all");
  const [apparatus, setApparatus] = useState("all");

  const filtered = CLASSES.filter(c => {
    if (filter !== "all" && c.level !== filter) return false;
    if (apparatus !== "all" && !c.apparatus.toLowerCase().includes(apparatus)) return false;
    return true;
  });

  return (
    <div>
      <div className="lib-toolbar">
        <span style={{ textTransform: "uppercase", letterSpacing: "0.06em" }}>Level:</span>
        {[["all","All"],["Basic","Basic"],["Intermediate","Intermediate"],["Advanced","Advanced"]].map(([v,l]) => (
          <span key={v} className="pill" data-active={filter === v} onClick={() => setFilter(v)}>{l}</span>
        ))}
        <span style={{ marginLeft: 10, textTransform: "uppercase", letterSpacing: "0.06em" }}>Apparatus:</span>
        {[["all","All"],["mat","Mat"],["reformer","Reformer"],["cadillac","Cadillac"]].map(([v,l]) => (
          <span key={v} className="pill" data-active={apparatus === v} onClick={() => setApparatus(v)}>{l}</span>
        ))}
        <span className="right">{filtered.length} of {CLASSES.length} sessions</span>
      </div>
      <div className="lib-grid">
        {filtered.map(c => <ClassCard key={c.id} c={c} muscles={MUSCLES} />)}
      </div>
    </div>
  );
}

function ClassCard({ c, muscles }) {
  const [hov, setHov] = useState(false);
  return (
    <div
      className="class-card"
      onMouseEnter={() => setHov(true)}
      onMouseLeave={() => setHov(false)}
      onClick={() => ModalStore.open("class", { cls: c })}
      role="button"
      tabIndex={0}
      onKeyDown={e => { if (e.key === "Enter" || e.key === " ") ModalStore.open("class", { cls: c }); }}
      style={{ cursor: "pointer" }}
    >
      <div className="ix">
        <span className="id">{c.id}</span>
        <span>{c.teacher}</span>
      </div>
      <div className="class-thumb">
        <span className="duration">▷ {c.duration}</span>
        <span className="level">{c.level}</span>
        <img
          src={c.photo} alt={c.title} loading="lazy" referrerPolicy="no-referrer"
          style={{
            position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover",
            filter: hov ? "none" : "grayscale(0.5) contrast(1.02)",
            transform: hov ? "scale(1.04)" : "scale(1)",
            transition: "filter .45s, transform .65s cubic-bezier(.2,.7,.3,1)",
          }}
        />
        <span className="play" aria-hidden="true" style={{ transform: hov ? "scale(1.12)" : "scale(1)", transition: "transform .25s" }}>
          <svg viewBox="0 0 16 16" width="12" height="12"><polygon points="4,3 13,8 4,13" fill="currentColor" /></svg>
        </span>
      </div>
      <div className="title serif">{c.title}</div>
      <div className="subtitle">{c.subtitle}</div>
      <div className="summary">{c.summary}</div>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 4, marginBottom: 10 }}>
        {c.muscles.slice(0, 3).map(m => (
          <span key={m} className="mono" style={{ fontSize: 10, letterSpacing: "0.06em", padding: "3px 7px", background: "var(--accent-soft,transparent)", border: "1px solid var(--rule-2)", color: "var(--ink-2)" }}>
            · {muscles[m]?.label.split(" ")[0] || m}
          </span>
        ))}
      </div>
      <div className="footer">
        <span>{c.apparatus}</span>
        <span>{c.sequence.length} exercises{hov ? " → open ↗" : ""}</span>
      </div>
    </div>
  );
}

/* ─── EXERCISE ENCYCLOPEDIA ─── */
const PAGE_SIZE = 12; // exercises per apparatus page (web-design: keep scannable)

function useLangLocal() {
  const [lang, setLang] = useState(window.PilatesI18n?.getLang() || "en");
  useEffect(() => window.PilatesI18n?.subscribe(setLang), []);
  return lang;
}
function tt(key, vars) { return window.PilatesI18n?.t(key, vars) || key; }

// Re-render trigger when the exercise-image manifest loads.
// Allows photos to replace SVG line-art on the next render.
function useImagesLoaded() {
  const [v, setV] = useState(0);
  useEffect(() => {
    const h = () => setV(x => x + 1);
    window.addEventListener("ex-images-loaded", h);
    return () => window.removeEventListener("ex-images-loaded", h);
  }, []);
  return v;
}

function ExerciseEncyclopedia() {
  const { EXERCISES, MUSCLES } = window.PilatesAnatomy;
  const [apparatus, setApparatus] = useState("All");
  const [level, setLevel] = useState("All");
  const [muscle, setMuscle] = useState("All");
  const [query, setQuery] = useState("");
  const [hovered, setHovered] = useState(null);
  const lang = useLangLocal();
  useImagesLoaded(); // re-render when image manifest arrives
  // openedSet: apparatuses the user has explicitly opened (overrides default).
  // closedSet: apparatuses the user has explicitly closed (overrides default).
  // Default: only the first apparatus is open.
  const [openedSet, setOpenedSet] = useState(() => new Set());
  const [closedSet, setClosedSet] = useState(() => new Set());
  // page index per apparatus
  const [pages, setPages] = useState({});

  const apparatuses = useMemo(() => ["All", ...Array.from(new Set(EXERCISES.map(e => e.apparatus)))], []);
  const levels = ["All", "Basic", "Intermediate", "Advanced"];

  // Unique muscle IDs across all exercises, sorted by display label.
  const muscles = useMemo(() => {
    const set = new Set();
    EXERCISES.forEach(e => (e.muscles || []).forEach(m => set.add(m)));
    return ["All", ...Array.from(set).sort((a, b) => {
      const la = (MUSCLES[a]?.label || a).toLowerCase();
      const lb = (MUSCLES[b]?.label || b).toLowerCase();
      return la < lb ? -1 : la > lb ? 1 : 0;
    })];
  }, [EXERCISES, MUSCLES]);

  const filtered = useMemo(() => EXERCISES.filter(e => {
    if (apparatus !== "All" && e.apparatus !== apparatus) return false;
    if (level !== "All" && e.level !== level) return false;
    if (muscle !== "All" && !(e.muscles || []).includes(muscle)) return false;
    if (query) {
      const q = query.toLowerCase();
      if (!e.name.toLowerCase().includes(q) && !e.focus.toLowerCase().includes(q)) return false;
    }
    return true;
  }), [apparatus, level, muscle, query]);

  const grouped = useMemo(() => {
    const g = {};
    filtered.forEach(e => { if (!g[e.apparatus]) g[e.apparatus] = []; g[e.apparatus].push(e); });
    return g;
  }, [filtered]);

  // When a search/filter is active OR only one apparatus, expand all by default.
  // Otherwise, only the first apparatus is expanded (keeps the page short and scannable).
  const isFiltering = query.length > 0 || level !== "All" || apparatus !== "All" || muscle !== "All";
  const apparatusKeys = Object.keys(grouped);

  // Reset paging when filter changes
  useEffect(() => { setPages({}); }, [apparatus, level, muscle, query]);

  function isOpen(app, idx) {
    if (isFiltering) return true; // when filtering, all matching apparatuses expanded
    if (openedSet.has(app)) return true; // user explicitly opened
    if (closedSet.has(app)) return false; // user explicitly closed
    return idx === 0; // default: only the first apparatus expanded
  }

  function toggle(app, idx) {
    const open = isOpen(app, idx);
    if (open) {
      setOpenedSet(prev => { const n = new Set(prev); n.delete(app); return n; });
      setClosedSet(prev => { const n = new Set(prev); n.add(app); return n; });
    } else {
      setClosedSet(prev => { const n = new Set(prev); n.delete(app); return n; });
      setOpenedSet(prev => { const n = new Set(prev); n.add(app); return n; });
    }
  }

  function openApp(app) {
    setClosedSet(prev => { const n = new Set(prev); n.delete(app); return n; });
    setOpenedSet(prev => { const n = new Set(prev); n.add(app); return n; });
  }

  function reset() {
    setApparatus("All"); setLevel("All"); setMuscle("All"); setQuery("");
    setOpenedSet(new Set()); setClosedSet(new Set()); setPages({});
  }

  return (
    <div className="encyclopedia">
      <div className="enc-toolbar">
        <div className="enc-search-col">
          <div className="enc-search">
            <span className="enc-search-icon">⌕</span>
            <input
              type="text" value={query} onChange={e => setQuery(e.target.value)}
              placeholder={tt("enc.search.placeholder")}
            />
            {query && <button onClick={() => setQuery("")} className="enc-clear">×</button>}
          </div>
          <div className="enc-filters">
            <span className="enc-filter-label">{tt("enc.filter.apparatus")}</span>
            <div className="enc-filter-row">
              {apparatuses.map(a => (
                <button key={a} className="enc-pill" data-active={apparatus === a} onClick={() => setApparatus(a)}>{a === "All" ? tt("enc.filter.all") : a}</button>
              ))}
            </div>
            <span className="enc-filter-label">{tt("enc.filter.level")}</span>
            <div className="enc-filter-row">
              {levels.map(l => {
                const lvLabel = l === "All" ? tt("enc.filter.all")
                  : lang === "th" ? ({Basic: "พื้นฐาน", Intermediate: "ปานกลาง", Advanced: "ขั้นสูง"})[l] || l
                  : l;
                return <button key={l} className="enc-pill" data-active={level === l} onClick={() => setLevel(l)}>{lvLabel}</button>;
              })}
            </div>
          </div>
        </div>
        <div className="enc-filters">
          <span className="enc-filter-label">{tt("enc.filter.muscle")}</span>
          <div className="enc-filter-row">
            {muscles.map(m => {
              if (m === "All") {
                return <button key={m} className="enc-pill" data-active={muscle === "All"} onClick={() => setMuscle("All")}>{tt("enc.filter.all")}</button>;
              }
              const ms = MUSCLES[m];
              const fullLabel = ms?.label || m;
              const shortLabel = fullLabel.split(/[\s(]/)[0]; // first word only for compact pill
              return (
                <button
                  key={m}
                  className="enc-pill"
                  data-active={muscle === m}
                  onClick={() => setMuscle(m)}
                  title={fullLabel}
                >{shortLabel}</button>
              );
            })}
          </div>
        </div>
        <div className="enc-count">
          <span className="enc-count-n">{filtered.length}</span>
          <span className="enc-count-l">{tt("enc.count.label", {total: EXERCISES.length})}</span>
        </div>
      </div>

      {/* Apparatus quick-jump bar — counts per category */}
      {!isFiltering && apparatusKeys.length > 1 && (
        <div className="enc-jumpbar">
          <span className="enc-jump-label mono">{tt("enc.jump.label")}</span>
          {apparatusKeys.map(app => (
            <button key={app} className="enc-jump" onClick={() => {
              openApp(app);
              setTimeout(() => document.getElementById(`enc-${app.replace(/\s+/g, "-")}`)?.scrollIntoView({ behavior: "smooth", block: "start" }), 80);
            }}>
              <span>{app}</span>
              <span className="enc-jump-count">{grouped[app].length}</span>
            </button>
          ))}
        </div>
      )}

      <div className="enc-body">
        {apparatusKeys.map((app, idx) => {
          const list = grouped[app];
          const open = isOpen(app, idx);
          const page = pages[app] || 0;
          const totalPages = Math.ceil(list.length / PAGE_SIZE);
          const start = page * PAGE_SIZE;
          const visible = list.slice(start, start + PAGE_SIZE);
          return (
            <div key={app} className="enc-group" id={`enc-${app.replace(/\s+/g, "-")}`}>
              <button
                className="enc-group-head"
                onClick={() => toggle(app, idx)}
                aria-expanded={open}
              >
                <span className="enc-group-toggle" data-open={open}>{open ? "−" : "+"}</span>
                <span className="enc-group-label">// {app}</span>
                <span className="enc-group-count">{list.length} {list.length === 1 ? "exercise" : "exercises"}</span>
              </button>
              {open && (
                <>
                  <div className="enc-list">
                    {visible.map(ex => (
                      <div
                        key={ex.id}
                        className="enc-row"
                        onMouseEnter={() => setHovered(ex.id)}
                        onMouseLeave={() => setHovered(null)}
                        onClick={() => ModalStore.open("exercise", { id: ex.id, from: "encyclopedia" })}
                        role="button"
                        tabIndex={0}
                        onKeyDown={e => { if (e.key === "Enter") ModalStore.open("exercise", { id: ex.id, from: "encyclopedia" }); }}
                        style={{ cursor: "pointer" }}
                      >
                        <div className="enc-row-id">{ex.id}</div>
                        <div
                          className="enc-row-thumb"
                          dangerouslySetInnerHTML={{ __html: window.ExerciseDiagram ? window.ExerciseDiagram.svg(ex.id) : "" }}
                        />
                        <div className="enc-row-name">
                          <span className="enc-row-title">{ex.name}</span>
                          <span className="enc-row-focus">{ex.focus}</span>
                        </div>
                        <div className="enc-row-muscles">
                          {ex.muscles.slice(0, 3).map(mid => (
                            <span key={mid} className="enc-row-muscle">· {MUSCLES[mid]?.label.split(" ")[0] || mid}</span>
                          ))}
                        </div>
                        <div className="enc-row-level" data-level={ex.level}>{ex.level}</div>
                        <div className="enc-row-duration">{ex.duration}</div>
                        <div className="enc-row-action">{hovered === ex.id ? "open ↗" : "→"}</div>
                      </div>
                    ))}
                  </div>

                  {/* Pagination — only when more than one page */}
                  {totalPages > 1 && (
                    <div className="enc-paging">
                      <button
                        className="enc-page-btn mono"
                        disabled={page === 0}
                        onClick={() => {
                          setPages(p => ({ ...p, [app]: Math.max(0, page - 1) }));
                          setTimeout(() => document.getElementById(`enc-${app.replace(/\s+/g, "-")}`)?.scrollIntoView({ behavior: "smooth", block: "start" }), 30);
                        }}
                      >← prev</button>
                      <div className="enc-page-info mono">
                        {Array.from({ length: totalPages }).map((_, i) => (
                          <button
                            key={i}
                            className="enc-page-num"
                            data-active={i === page}
                            onClick={() => {
                              setPages(p => ({ ...p, [app]: i }));
                              setTimeout(() => document.getElementById(`enc-${app.replace(/\s+/g, "-")}`)?.scrollIntoView({ behavior: "smooth", block: "start" }), 30);
                            }}
                          >{String(i + 1).padStart(2, "0")}</button>
                        ))}
                        <span className="enc-page-meta">{start + 1}–{Math.min(start + PAGE_SIZE, list.length)} of {list.length}</span>
                      </div>
                      <button
                        className="enc-page-btn mono"
                        disabled={page >= totalPages - 1}
                        onClick={() => {
                          setPages(p => ({ ...p, [app]: Math.min(totalPages - 1, page + 1) }));
                          setTimeout(() => document.getElementById(`enc-${app.replace(/\s+/g, "-")}`)?.scrollIntoView({ behavior: "smooth", block: "start" }), 30);
                        }}
                      >next →</button>
                    </div>
                  )}
                </>
              )}
            </div>
          );
        })}
        {filtered.length === 0 && (
          <div className="enc-empty mono">
            {tt("enc.empty")}
            <button onClick={reset} className="enc-reset">{tt("enc.empty.reset")}</button>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { AnatomyExplorer, ClassLibrary, ExerciseEncyclopedia, ModalRoot });
