---
// src/components/SidePanel.astro
---
<aside class="page-panel" aria-label="Panneau contextuel">
  <div class="page-panel__inner">
    <div class="panel-head">
      <div class="panel-head__left">
        <span class="panel-head__label" aria-hidden="true">¶</span>
        <code class="panel-head__id" id="panel-para-id">—</code>
      </div>
    </div>

    <div class="panel-msg panel-msg--head" id="panel-head-msg" hidden></div>

    {/* ✅ actions medias déplacées en haut (niveau 3 uniquement) */}
    <div class="panel-top-actions level-3" aria-label="Actions médias">
      <div class="panel-actions">
        <button class="panel-btn" id="panel-media-all" type="button">Voir tous les éléments</button>
        <button class="panel-btn panel-btn--primary" id="panel-media-submit" type="button">
          Soumettre un média (Gitea)
        </button>
      </div>
      <div class="panel-msg" id="panel-media-msg" hidden></div>
    </div>

    {/* ✅ actions références en haut (niveau 2 uniquement) */}
    <div class="panel-top-actions level-2" aria-label="Actions références">
      <div class="panel-actions">
        <button class="panel-btn panel-btn--primary" id="panel-ref-submit" type="button">
          Soumettre une référence (Gitea)
        </button>
      </div>
      <div class="panel-msg" id="panel-ref-msg" hidden></div>
    </div>

    <section class="panel-block level-2" aria-label="Références et auteurs">
      <h2 class="panel-title">Références & auteurs</h2>
      <div class="panel-body" id="panel-l2"></div>
    </section>

    <section class="panel-block level-3" aria-label="Illustrations et diagrammes">
      <h2 class="panel-title">Illustrations & diagrammes</h2>
      <div class="panel-body" id="panel-l3"></div>
    </section>

    <section class="panel-block level-4" aria-label="Commentaires">
      <h2 class="panel-title">Commentaires</h2>
      <div class="panel-body" id="panel-l4"></div>

      <div class="panel-compose">
        <label class="panel-label" for="panel-comment-text">Nouveau commentaire</label>
        <textarea id="panel-comment-text" class="panel-textarea" rows="5" placeholder="Écris ici…"></textarea>

        <div class="panel-actions">
          <button class="panel-btn panel-btn--primary" id="panel-comment-send" type="button">Envoyer</button>
        </div>

        <div class="panel-msg" id="panel-comment-msg" hidden></div>
      </div>
    </section>
  </div>

  {/* ✅ Lightbox media (plein écran) */}
  <div class="panel-lightbox" id="panel-lightbox" hidden aria-hidden="true">
    <div class="panel-lightbox__overlay" data-close="1"></div>
    <div class="panel-lightbox__dialog" role="dialog" aria-modal="true" aria-label="Aperçu du média">
      <button class="panel-lightbox__close" type="button" data-close="1" aria-label="Fermer">×</button>
      <div class="panel-lightbox__content" id="panel-lightbox-content"></div>
      <div class="panel-lightbox__caption" id="panel-lightbox-caption" hidden></div>
    </div>
  </div>
</aside>

<script is:inline>
(() => {
  const root = document.querySelector(".page-panel");
  if (!root) return;

  // Anti double-init (HMR / script injecté 2x / re-mount)
  if (root.dataset.archiSidePanelInit === "1") return;
  root.dataset.archiSidePanelInit = "1";

  const pageKey = String(location.pathname || "").replace(/^\/+/, "").replace(/\/+$/, "");

  const elId = root.querySelector("#panel-para-id");
  const elL2 = root.querySelector("#panel-l2");
  const elL3 = root.querySelector("#panel-l3");
  const elL4 = root.querySelector("#panel-l4");

  const msgHead = root.querySelector("#panel-head-msg");

  const btnMediaAll = root.querySelector("#panel-media-all");
  const btnMediaSubmit = root.querySelector("#panel-media-submit");
  const msgMedia = root.querySelector("#panel-media-msg");

  const btnRefSubmit = root.querySelector("#panel-ref-submit");
  const msgRef = root.querySelector("#panel-ref-msg");

  const taComment = root.querySelector("#panel-comment-text");
  const btnSend = root.querySelector("#panel-comment-send");
  const msgComment = root.querySelector("#panel-comment-msg");

  const lb = root.querySelector("#panel-lightbox");
  const lbContent = root.querySelector("#panel-lightbox-content");
  const lbCaption = root.querySelector("#panel-lightbox-caption");

  const docTitle = document.body?.dataset?.docTitle || document.title || "Archicratie";
  const docVersion = document.body?.dataset?.docVersion || "";

  const FULL_TEXT_SOFT_LIMIT = 1200;
  const URL_HARD_LIMIT = 6500;

  let _idxP = null;
  let currentParaId = "";
  let mediaShowAll = (localStorage.getItem("archicratie:panel:mediaAll") === "1");

  // ===== cosmetics: micro flash “update” =====
  let _flashT = 0;
  function flashUpdate(){
    try {
      root.classList.add("is-updating");
      if (_flashT) clearTimeout(_flashT);
      _flashT = setTimeout(() => root.classList.remove("is-updating"), 180);
    } catch {}
  }

  // ===== globals =====
  function getG() {
    return window.__archiGitea || { ready: false, base: "", owner: "", repo: "" };
  }
  function getAuthInfoP() {
    return window.__archiAuthInfoP || Promise.resolve({ ok: false, groups: [] });
  }

  const access = { ready: false, canUsers: false };

  // On verrouille les boutons tant que l’accès n’est pas résolu
  if (btnMediaSubmit) btnMediaSubmit.disabled = true;
  if (btnSend) btnSend.disabled = true;
  if (btnRefSubmit) btnRefSubmit.disabled = true;

  function inGroup(groups, g) {
    const gg = String(g || "").toLowerCase();
    return Array.isArray(groups) && groups.some((x) => String(x).toLowerCase() === gg);
  }

  // ✅ readers + editors peuvent soumettre médias + commentaires + refs
  getAuthInfoP().then((info) => {
    const groups = Array.isArray(info?.groups) ? info.groups : [];
    const canReaders = inGroup(groups, "readers");
    const canEditors = inGroup(groups, "editors");

    const whoamiSkipped = Boolean(window.__archiFlags && window.__archiFlags.whoamiSkipped);
    access.canUsers = Boolean((info?.ok && (canReaders || canEditors)) || whoamiSkipped);
    access.ready = true;

    if (btnMediaSubmit) btnMediaSubmit.disabled = !access.canUsers;
    if (btnSend) btnSend.disabled = !access.canUsers;
    if (btnRefSubmit) btnRefSubmit.disabled = !access.canUsers;

    if (!access.canUsers) {
      if (msgHead) {
        msgHead.hidden = false;
        msgHead.textContent = "Connexion requise (readers ou editors) pour soumettre des médias/commentaires.";
        msgHead.dataset.kind = "warn";
      }
    }
  }).catch(() => {
    access.ready = true;
    if (Boolean(window.__archiFlags && window.__archiFlags.whoamiSkipped)) {
      access.canUsers = true;
      if (btnMediaSubmit) btnMediaSubmit.disabled = false;
      if (btnSend) btnSend.disabled = false;
      if (btnRefSubmit) btnRefSubmit.disabled = false;
    }
  });

  function esc(s) { return String(s ?? ""); }

  function clear(el) {
    if (!el) return;
    while (el.firstChild) el.removeChild(el.firstChild);
  }

  function pText(txt) {
    const p = document.createElement("p");
    p.textContent = txt;
    return p;
  }

  function link(url, label) {
    const a = document.createElement("a");
    a.href = url;
    a.target = "_blank";
    a.rel = "noopener noreferrer";
    a.textContent = label || url;
    return a;
  }

  function showMsg(el, text, kind = "info") {
    if (!el) return;
    el.hidden = false;
    el.textContent = text;
    el.dataset.kind = kind;
  }
  function hideMsg(el) {
    if (!el) return;
    el.hidden = true;
    el.textContent = "";
    el.dataset.kind = "";
  }

  async function loadIndex() {
    if (_idxP) return _idxP;
    _idxP = (async () => {
      try {
        const res = await fetch("/annotations-index.json?_=" + Date.now(), { cache: "no-store" });
        if (res && res.ok) return await res.json();
      } catch {}
      _idxP = null;
      return null;
    })();
    return _idxP;
  }

  function getParaText(paraId) {
    try {
      const el = document.getElementById(paraId);
      if (!el) return "";
      const clone = el.cloneNode(true);
      clone.querySelectorAll(".para-tools,[data-noquote]").forEach((n) => n.remove());
      return String(clone.innerText || clone.textContent || "").replace(/\s+/g, " ").trim();
    } catch { return ""; }
  }

  function quoteBlock(s) {
    return String(s || "")
      .split(/\r?\n/)
      .map((l) => (`> ${l}`).trimEnd())
      .join("\n");
  }

  function buildIssueURL({ title, body }) {
    const g = getG();
    if (!g.ready) return "";
    const base = String(g.base).replace(/\/+$/, "");
    const issue = new URL(`${base}/${g.owner}/${g.repo}/issues/new`);
    issue.searchParams.set("title", title);

    let b = String(body || "");
    if (b.length > 9000) b = b.slice(0, 9000) + "\n\n(…troncature automatique…)";
    issue.searchParams.set("body", b);

    if (issue.toString().length > URL_HARD_LIMIT) {
      const shortBody = b.slice(0, 1800) + "\n\n(…troncature pour limite URL…)";
      issue.searchParams.set("body", shortBody);
    }
    return issue.toString();
  }

  function openNewTab(url) {
    try {
      const a = document.createElement("a");
      a.href = url;
      a.target = "_blank";
      a.rel = "noopener noreferrer";
      a.style.display = "none";
      document.body.appendChild(a);
      a.click();
      a.remove();
      return true;
    } catch {
      return false;
    }
  }

  const _openStamp = new Map();
  function openOnce(key, fn) {
    const now = Date.now();
    const prev = _openStamp.get(key) || 0;
    if (now - prev < 700) return false;
    _openStamp.set(key, now);
    return Boolean(fn());
  }

  function guardEventOnce(ev, key) {
    try {
      const k = "__archiOnce_" + key;
      if (ev && ev[k]) return true;
      if (ev) ev[k] = true;
      try { ev.stopImmediatePropagation(); } catch {}
      try { ev.stopPropagation(); } catch {}
    } catch {}
    return false;
  }

  function bindClickOnce(el, handler) {
    if (!el) return;
    if (el.dataset.bound === "1") return;
    el.dataset.bound = "1";
    el.addEventListener("click", handler, { passive: false });
  }

  // ===== Lightbox =====
  function lockScroll(on) {
    try {
      document.documentElement.classList.toggle("archi-lb-open", !!on);
    } catch {}
  }

  function closeLightbox() {
    if (!lb) return;
    lb.hidden = true;
    lb.setAttribute("aria-hidden", "true");
    if (lbContent) clear(lbContent);
    if (lbCaption) { lbCaption.hidden = true; lbCaption.textContent = ""; }
    lockScroll(false);
  }

  function openLightbox({ type, src, caption }) {
    if (!lb || !lbContent) return;
    clear(lbContent);

    const t = String(type || "link");
    const s = String(src || "");
    const cap = String(caption || "").trim();

    if (!s) return;

    if (t === "image") {
      const img = document.createElement("img");
      img.loading = "eager";
      img.alt = cap || "Illustration";
      img.src = s;
      lbContent.appendChild(img);
    } else if (t === "video") {
      const v = document.createElement("video");
      v.controls = true;
      v.autoplay = false;
      v.src = s;
      lbContent.appendChild(v);
    } else if (t === "audio") {
      const a = document.createElement("audio");
      a.controls = true;
      a.src = s;
      lbContent.appendChild(a);
    } else {
      const a = link(s, cap || s);
      a.className = "panel-lightbox__link";
      lbContent.appendChild(a);
    }

    if (lbCaption) {
      if (cap) { lbCaption.hidden = false; lbCaption.textContent = cap; }
      else { lbCaption.hidden = true; lbCaption.textContent = ""; }
    }

    lockScroll(true);
    lb.hidden = false;
    lb.setAttribute("aria-hidden", "false");
  }

  if (lb) {
    bindClickOnce(lb, (ev) => {
      const t = ev.target;
      if (t && t.getAttribute && t.getAttribute("data-close") === "1") {
        ev.preventDefault();
        closeLightbox();
      }
    });
    window.addEventListener("keydown", (ev) => {
      if (ev.key === "Escape" && !lb.hidden) closeLightbox();
    });
  }

  function renderLevel2(data) {
    clear(elL2);
    if (!elL2) return;

    if (!data) {
      elL2.appendChild(pText("Aucune annotation pour ce paragraphe."));
      return;
    }

    if (Array.isArray(data.authors) && data.authors.length) {
      const h = document.createElement("h3");
      h.className = "panel-subtitle";
      h.textContent = "Auteurs";
      elL2.appendChild(h);

      const ul = document.createElement("ul");
      ul.className = "panel-list";
      for (const a of data.authors) {
        const li = document.createElement("li");
        li.textContent = esc(a);
        ul.appendChild(li);
      }
      elL2.appendChild(ul);
    }

    if (Array.isArray(data.refs) && data.refs.length) {
      const h = document.createElement("h3");
      h.className = "panel-subtitle";
      h.textContent = "Références";
      elL2.appendChild(h);

      const ul = document.createElement("ul");
      ul.className = "panel-list";
      for (const r of data.refs) {
        const li = document.createElement("li");
        const label = esc(r?.label || r?.url || "Référence");
        const url = esc(r?.url || "");
        const kind = esc(r?.kind || "");
        if (url) li.appendChild(link(url, label));
        else li.textContent = label;

        if (kind) {
          const k = document.createElement("span");
          k.className = "panel-chip";
          k.textContent = kind;
          li.appendChild(document.createTextNode(" "));
          li.appendChild(k);
        }
        ul.appendChild(li);
      }
      elL2.appendChild(ul);
    }

    if (Array.isArray(data.quotes) && data.quotes.length) {
      const h = document.createElement("h3");
      h.className = "panel-subtitle";
      h.textContent = "Citations";
      elL2.appendChild(h);

      for (const q of data.quotes) {
        const block = document.createElement("blockquote");
        block.className = "panel-quote";

        const t = document.createElement("div");
        t.textContent = esc(q?.text || "");
        block.appendChild(t);

        const src = esc(q?.source || "");
        if (src) {
          const s = document.createElement("div");
          s.className = "panel-quote__src";
          s.textContent = src;
          block.appendChild(s);
        }
        elL2.appendChild(block);
      }
    }

    if (!elL2.firstChild) {
      elL2.appendChild(pText("Aucune annotation pour ce paragraphe."));
    }
  }

  function renderLevel3(data) {
    clear(elL3);
    hideMsg(msgMedia);
    if (!elL3) return;

    const arr = Array.isArray(data?.media) ? data.media : [];
    if (!arr.length) {
      elL3.appendChild(pText("Aucun média associé à ce paragraphe."));
      if (btnMediaAll) btnMediaAll.disabled = true;
      return;
    }

    if (btnMediaAll) btnMediaAll.disabled = arr.length <= 6;

    const grid = document.createElement("div");
    grid.className = "panel-media-grid";

    const items = mediaShowAll ? arr : arr.slice(0, 6);

    for (const m of items) {
      const type = esc(m?.type || "link");
      const src = esc(m?.src || "");
      const caption = esc(m?.caption || "");
      const credit = esc(m?.credit || "");

      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "panel-media-tile";
      btn.setAttribute("aria-label", caption ? `Ouvrir média: ${caption}` : "Ouvrir média");
      btn.dataset.src = src;
      btn.dataset.type = type;
      btn.dataset.caption = caption;

      if (type === "image" && src) {
        const img = document.createElement("img");
        img.loading = "lazy";
        img.alt = caption || "Illustration";
        img.src = src;
        btn.appendChild(img);
      } else {
        const ph = document.createElement("div");
        ph.className = "panel-media-ph";
        ph.textContent = type.toUpperCase();
        btn.appendChild(ph);
      }

      if (caption) {
        const c = document.createElement("div");
        c.className = "panel-media-cap";
        c.textContent = caption;
        btn.appendChild(c);
      } else if (src) {
        const c = document.createElement("div");
        c.className = "panel-media-cap";
        c.textContent = src.split("/").pop() || src;
        btn.appendChild(c);
      }

      if (credit) {
        const cr = document.createElement("div");
        cr.className = "panel-media-credit";
        cr.textContent = credit;
        btn.appendChild(cr);
      }

      bindClickOnce(btn, (ev) => {
        ev.preventDefault();
        if (!src) return;
        openLightbox({ type, src, caption });
      });

      grid.appendChild(btn);
    }

    elL3.appendChild(grid);
  }

  function renderLevel4(data) {
    clear(elL4);
    hideMsg(msgComment);
    if (!elL4) return;

    const arr = Array.isArray(data?.comments_editorial) ? data.comments_editorial : [];
    if (!arr.length) {
      elL4.appendChild(pText("Aucun commentaire éditorial pour ce paragraphe."));
      return;
    }

    const ul = document.createElement("ul");
    ul.className = "panel-list";

    for (const c of arr) {
      const li = document.createElement("li");
      li.className = "panel-comment";

      const txt = document.createElement("div");
      txt.textContent = esc(c?.text || "");
      li.appendChild(txt);

      const st = esc(c?.status || "");
      if (st) {
        const s = document.createElement("span");
        s.className = "panel-chip";
        s.textContent = st;
        li.appendChild(s);
      }

      ul.appendChild(li);
    }

    elL4.appendChild(ul);
  }

  async function updatePanel(paraId) {
    currentParaId = paraId || currentParaId || "";
    if (elId) elId.textContent = currentParaId || "—";

    flashUpdate();

    hideMsg(msgHead);
    hideMsg(msgMedia);
    hideMsg(msgComment);
    hideMsg(msgRef);

    const idx = await loadIndex();

    if (!idx && msgHead && msgHead.hidden) {
      msgHead.hidden = false;
      msgHead.textContent = "Index annotations indisponible (annotations-index.json).";
      msgHead.dataset.kind = "info";
    }

    const data = idx?.pages?.[pageKey]?.paras?.[currentParaId] || null;

    renderLevel2(data);
    renderLevel3(data);
    renderLevel4(data);
  }

  if (btnMediaAll) {
    bindClickOnce(btnMediaAll, (ev) => {
      ev.preventDefault();
      mediaShowAll = !mediaShowAll;
      localStorage.setItem("archicratie:panel:mediaAll", mediaShowAll ? "1" : "0");
      btnMediaAll.textContent = mediaShowAll ? "Réduire la liste" : "Voir tous les éléments";
      updatePanel(currentParaId);
    });
    btnMediaAll.textContent = mediaShowAll ? "Réduire la liste" : "Voir tous les éléments";
  }

  if (btnMediaSubmit) {
    bindClickOnce(btnMediaSubmit, (ev) => {
      ev.preventDefault();
      hideMsg(msgMedia);

      if (guardEventOnce(ev, "gitea_open_media")) return;

      if (!currentParaId) return showMsg(msgMedia, "Choisis d’abord un paragraphe (scroll / survol).", "warn");
      if (!getG().ready) return showMsg(msgMedia, "Gitea non configuré (PUBLIC_GITEA_*).", "error");
      if (btnMediaSubmit.disabled) return showMsg(msgMedia, "Connexion requise (readers/editors).", "error");

      const pageUrl = new URL(location.href);
      pageUrl.search = "";
      pageUrl.hash = currentParaId;

      const paraTxt = getParaText(currentParaId);
      const excerpt = paraTxt.length > FULL_TEXT_SOFT_LIMIT ? (paraTxt.slice(0, FULL_TEXT_SOFT_LIMIT) + "…") : paraTxt;

      const title = `[Media] ${currentParaId} — ${docTitle}`;
      const body = [
        `Chemin: ${location.pathname}`,
        `URL: ${pageUrl.toString()}`,
        `Ancre: #${currentParaId}`,
        `Version: ${docVersion || "(non renseignée)"}`,
        `Type: type/media`,
        ``,
        `Contexte (extrait):`,
        quoteBlock(excerpt || ""),
        ``,
        `---`,
        `Action: dans Gitea, ajoute le fichier via la zone “Joindre un fichier” (drag & drop / upload).`,
        `But: associer le média au paragraphe #${currentParaId}.`,
      ].join("\n");

      const url = buildIssueURL({ title, body });
      if (!url) return showMsg(msgMedia, "Impossible de générer l’issue (config).", "error");

      const ok = openOnce(`media:${currentParaId}`, () => openNewTab(url));
      if (!ok) showMsg(msgMedia, "Popup bloqué : autorise les popups pour ouvrir Gitea.", "error");
    });
  }

  if (btnRefSubmit) {
    bindClickOnce(btnRefSubmit, (ev) => {
      ev.preventDefault();
      hideMsg(msgRef);

      if (guardEventOnce(ev, "gitea_open_ref")) return;

      if (!currentParaId) return showMsg(msgRef, "Choisis d’abord un paragraphe (scroll / survol).", "warn");
      if (!getG().ready) return showMsg(msgRef, "Gitea non configuré (PUBLIC_GITEA_*).", "error");
      if (btnRefSubmit.disabled) return showMsg(msgRef, "Connexion requise (readers/editors).", "error");

      const pageUrl = new URL(location.href);
      pageUrl.search = "";
      pageUrl.hash = currentParaId;

      const paraTxt = getParaText(currentParaId);
      const excerpt = paraTxt.length > FULL_TEXT_SOFT_LIMIT ? (paraTxt.slice(0, FULL_TEXT_SOFT_LIMIT) + "…") : paraTxt;

      const title = `[Reference] ${currentParaId} — ${docTitle}`;
      const body = [
        `Chemin: ${location.pathname}`,
        `URL: ${pageUrl.toString()}`,
        `Ancre: #${currentParaId}`,
        `Version: ${docVersion || "(non renseignée)"}`,
        `Type: type/reference`,
        ``,
        `Contexte (extrait):`,
        quoteBlock(excerpt || ""),
        ``,
        `Référence (à compléter):`,
        `- URL:`,
        `- Label:`,
        `- Kind: (livre / article / vidéo / site / autre)`,
        `- Citation / passage (optionnel):`,
        ``,
        `---`,
        `Note: issue générée depuis le site (pré-remplissage).`,
      ].join("\n");

      const url = buildIssueURL({ title, body });
      if (!url) return showMsg(msgRef, "Impossible de générer l’issue.", "error");

      const ok = openOnce(`ref:${currentParaId}`, () => openNewTab(url));
      if (!ok) showMsg(msgRef, "Si rien ne s’ouvre : autorise les popups pour ce site.", "error");
    });
  }

  if (btnSend) {
    bindClickOnce(btnSend, (ev) => {
      ev.preventDefault();
      hideMsg(msgComment);

      if (guardEventOnce(ev, "gitea_open_comment")) return;

      if (!currentParaId) return showMsg(msgComment, "Choisis d’abord un paragraphe (scroll / survol).", "warn");
      if (!getG().ready) return showMsg(msgComment, "Gitea non configuré (PUBLIC_GITEA_*).", "error");
      if (btnSend.disabled) return showMsg(msgComment, "Connexion requise (readers/editors).", "error");

      const txt = String(taComment?.value || "").trim();
      if (txt.length < 3) return showMsg(msgComment, "Commentaire trop court.", "warn");

      const pageUrl = new URL(location.href);
      pageUrl.search = "";
      pageUrl.hash = currentParaId;

      const paraTxt = getParaText(currentParaId);
      const excerpt = paraTxt.length > FULL_TEXT_SOFT_LIMIT ? (paraTxt.slice(0, FULL_TEXT_SOFT_LIMIT) + "…") : paraTxt;

      const title = `[Comment] ${currentParaId} — ${docTitle}`;
      const body = [
        `Chemin: ${location.pathname}`,
        `URL: ${pageUrl.toString()}`,
        `Ancre: #${currentParaId}`,
        `Version: ${docVersion || "(non renseignée)"}`,
        `Type: type/comment`,
        ``,
        `Contexte (extrait):`,
        quoteBlock(excerpt || ""),
        ``,
        `Commentaire:`,
        txt,
        ``,
        `---`,
        `Note: issue générée depuis le site (pré-remplissage).`,
      ].join("\n");

      const url = buildIssueURL({ title, body });
      if (!url) return showMsg(msgComment, "Impossible de générer l’issue.", "error");

      const ok = openOnce(`comment:${currentParaId}`, () => openNewTab(url));
      if (!ok) return showMsg(msgComment, "Popup bloqué : autorise les popups pour ouvrir Gitea.", "error");

      taComment.value = "";
      showMsg(msgComment, "Issue Gitea ouverte dans un nouvel onglet.", "ok");
      setTimeout(() => hideMsg(msgComment), 900);
    });
  }

  // ===== wiring: para courant (SOURCE OF TRUTH = EditionLayout) =====
  function onCurrentPara(ev) {
    try {
      const id = ev?.detail?.id ? String(ev.detail.id) : "";
      if (!id || !/^p-\d+-/i.test(id)) return;
      if (id === currentParaId) return;
      updatePanel(id);
    } catch {}
  }
  window.addEventListener("archicratie:currentPara", onCurrentPara);

  const initial = String(location.hash || "").replace(/^#/, "").trim();

  if (/^p-\d+-/i.test(initial)) {
    updatePanel(initial);
  } else if (window.__archiCurrentParaId && /^p-\d+-/i.test(String(window.__archiCurrentParaId))) {
    updatePanel(String(window.__archiCurrentParaId));
  } else {
    setTimeout(() => {
      try {
        const id = String(window.__archiCurrentParaId || "").trim();
        if (/^p-\d+-/i.test(id)) updatePanel(id);
      } catch {}
    }, 0);
  }
})();
</script>

<style>
  /* Fallback : niveau 1 → jamais de panel */
  :global(body[data-reading-level="1"]) .page-panel{ display: none !important; }

  .page-panel{
    grid-column: 3;
    position: sticky;
    top: calc(var(--sticky-header-h) + var(--page-gap));
    align-self: start;

    --thumb: 92px; /* ✅ taille des vignettes (80–110 selon goût) */
  }

  :global(body[data-reading-level="3"]) .page-panel{
    grid-column: 2;
  }

  .page-panel__inner{
    max-height: calc(100vh - (var(--sticky-header-h) + var(--page-gap) + 12px));
    overflow: auto;
    scrollbar-gutter: stable;

    border: 1px solid rgba(127,127,127,.22);
    border-radius: 16px;
    padding: 12px;
    background: rgba(127,127,127,0.04);
  }

  .panel-head{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;

    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px dashed rgba(127,127,127,0.25);
  }

  .panel-head__left{
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
  }

  .panel-head__label{ font-weight: 900; opacity: .8; }
  .panel-head__id{
    font-weight: 850;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 14rem;
  }

  .panel-msg{
    margin-top: 8px;
    font-size: 12px;
    opacity: .95;
  }
  .panel-msg--head{
    margin-top: 0;
    margin-bottom: 10px;
  }

  .panel-title{
    font-size: 13px;
    font-weight: 900;
    margin: 10px 0 8px;
    opacity: .9;
  }
  .panel-subtitle{
    font-size: 12px;
    font-weight: 850;
    margin: 10px 0 6px;
    opacity: .88;
  }

  .panel-body p{ margin: 8px 0; opacity: .9; }

  .panel-list{ margin: 0; padding-left: 18px; }
  .panel-list li{ margin: 6px 0; }

  .panel-chip{
    display: inline-block;
    margin-left: 8px;
    font-size: 11px;
    font-weight: 800;
    padding: 2px 7px;
    border-radius: 999px;
    border: 1px solid rgba(127,127,127,0.30);
    background: rgba(127,127,127,0.10);
    opacity: .92;
  }

  .panel-quote{
    margin: 8px 0;
    padding: 8px 10px;
    border-left: 3px solid rgba(127,127,127,0.35);
    background: rgba(127,127,127,0.06);
    border-radius: 10px;
  }
  .panel-quote__src{ margin-top: 6px; font-size: 12px; opacity: .85; }

  .panel-actions{
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 10px;
  }

  .panel-btn{
    border: 1px solid rgba(127,127,127,0.40);
    background: rgba(127,127,127,0.10);
    border-radius: 999px;
    padding: 6px 10px;
    font-size: 13px;
    cursor: pointer;
  }
  .panel-btn:disabled{ opacity: .55; cursor: default; }
  .panel-btn--primary{
    border-color: rgba(127,127,127,0.65);
    font-weight: 800;
  }

  /* actions médias en haut */
  .panel-top-actions{ margin-top: 8px; }

  /* ===== media thumbnails (plus petits + plus denses) ===== */
  .panel-media-grid{
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--thumb), 1fr));
    gap: 10px;
  }

  .panel-media-tile{
    width: 100%;
    border: 1px solid rgba(127,127,127,.20);
    border-radius: 14px;
    padding: 8px;
    background: rgba(127,127,127,0.04);
    cursor: pointer;
    text-align: left;
    transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
  }

  .panel-media-tile:hover{
    transform: translateY(-1px);
    background: rgba(127,127,127,0.07);
    border-color: rgba(127,127,127,.32);
  }

  .panel-media-tile img{
    width: 100%;
    height: var(--thumb);
    object-fit: cover;
    display: block;
    border-radius: 10px;
    margin-bottom: 8px;
  }

  .panel-media-ph{
    width: 100%;
    height: var(--thumb);
    border-radius: 10px;
    display: grid;
    place-items: center;
    background: rgba(127,127,127,0.10);
    margin-bottom: 8px;
    font-weight: 900;
    font-size: 12px;
    opacity: .9;
  }

  .panel-media-cap{
    font-size: 12px;
    font-weight: 800;
    opacity: .92;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .panel-media-credit{
    margin-top: 4px;
    font-size: 11px;
    opacity: .8;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* ===== compose ===== */
  .panel-compose{ margin-top: 12px; padding-top: 10px; border-top: 1px dashed rgba(127,127,127,0.25); }
  .panel-label{ display: block; font-size: 12px; font-weight: 900; opacity: .9; margin-bottom: 6px; }

  .panel-textarea{
    width: 100%;
    box-sizing: border-box;
    border: 1px solid rgba(127,127,127,0.35);
    border-radius: 12px;
    padding: 8px 10px;
    background: transparent;
    font-size: 13px;
    resize: vertical;
  }

  /* ===== Lightbox (plein écran “cinéma”) ===== */
  :global(html.archi-lb-open){
    overflow: hidden; /* ✅ empêche le scroll derrière */
  }

  .panel-lightbox{
    position: fixed;
    inset: 0;
    z-index: 120;
  }

  .panel-lightbox__overlay{
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,0.84);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }

  .panel-lightbox__dialog{
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);

    width: min(1100px, 92vw);
    max-height: 92vh;
    overflow: auto;

    border: 1px solid rgba(255,255,255,0.14);
    border-radius: 18px;

    background: rgba(20,20,20,0.55);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);

    padding: 16px;
    box-shadow: 0 24px 70px rgba(0,0,0,0.55);
  }

  .panel-lightbox__close{
    position: absolute;
    top: 12px;
    right: 12px;

    display: inline-flex;
    align-items: center;
    justify-content: center;

    width: 44px;
    height: 40px;

    border-radius: 14px;
    border: 1px solid rgba(255,255,255,0.22);
    background: rgba(255,255,255,0.10);

    cursor: pointer;
    font-size: 22px;
    font-weight: 900;
  }

  .panel-lightbox__content{
    margin-top: 36px;
  }

  .panel-lightbox__content img,
  .panel-lightbox__content video{
    display: block;
    width: 100%;
    max-height: calc(92vh - 160px);
    object-fit: contain;

    background: rgba(0,0,0,0.22);
    border-radius: 14px;
  }

  .panel-lightbox__content audio{
    width: 100%;
  }

  .panel-lightbox__caption{
    margin-top: 12px;
    font-size: 12px;
    font-weight: 800;
    opacity: .92;
    color: rgba(255,255,255,0.92);
  }

  @media (max-width: 1100px){
    .page-panel{ display: none; }
  }
</style>