// src/app/detail/ArticleClient.tsx  ← 覆盖此文件
'use client';

import { useEffect, useState, useRef } from 'react';
import Script from 'next/script';
import ScrollTopWidget from '@/components/ScrollTopWidget';
import Link from 'next/link';
import './article-detail.css';

interface ArticleData {
  id: number; title: string; content: string;
  created: string; updated: string; views: number;
  categoryId: number | null; categoryName: string | null;
  authorName: string; isEncrypted: boolean;
}
interface SimpleArticle { id: number; title: string; created?: string; }
interface Props {
  article: ArticleData;
  prevArticle: { id: number; title: string } | null;
  nextArticle: { id: number; title: string } | null;
  sameDayArticles: SimpleArticle[];
  isAdmin: boolean;
  absoluteUrl: string;
}

const THEME_CONFIG: Record<string, { icon: string; label: string }> = {
  light:      { icon: '☀️', label: '浅色模式' },
  dark:       { icon: '🌙', label: '深色模式' },
  sakura:     { icon: '🌸', label: '樱花主题' },
  ocean:      { icon: '🌊', label: '海洋主题' },
  electronic: { icon: '⚡', label: '电子风格' },
};

function truncate(s: string, n: number) { return s.length > n ? s.slice(0, n) + '…' : s; }

// ── 用 getElementById 跳转,避免中文 ID 的 querySelector 失败 ──
function scrollToId(id: string) {
  const el = document.getElementById(id);
  if (!el) return;
  const y = el.getBoundingClientRect().top + window.pageYOffset - 90;
  window.scrollTo({ top: y, behavior: 'smooth' });
  el.classList.add('highlighted-heading');
  setTimeout(() => el.classList.remove('highlighted-heading'), 1800);
}

export default function ArticleClient({
  article, prevArticle, nextArticle, sameDayArticles, isAdmin, absoluteUrl,
}: Props) {
  const [wordCount, setWordCount] = useState(0);
  const [views,     setViews]     = useState(article.views);
  const [theme,     setTheme]     = useState('light');
  const [showThemeMenu, setShowThemeMenu] = useState(false);
  const themeMenuRef = useRef<HTMLDivElement>(null);
  const echartsReady = useRef(false);
  const prismDone    = useRef(false);

  // ── 1. 主题 ──────────────────────────────────────
  useEffect(() => { applyTheme(localStorage.getItem('theme') || 'light'); }, []);
  function applyTheme(t: string) {
    if (!THEME_CONFIG[t]) return;
    setTheme(t); localStorage.setItem('theme', t);
    document.documentElement.setAttribute('data-theme', t);
    setShowThemeMenu(false);
  }
  useEffect(() => {
    const h = (e: MouseEvent) => {
      if (themeMenuRef.current && !themeMenuRef.current.contains(e.target as Node)) setShowThemeMenu(false);
    };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);

  // ── 2. 阅读量 ────────────────────────────────────
  useEffect(() => {
    fetch('/api/view', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ articleId: article.id }) })
      .then(r => r.json()).then(d => { if (typeof d.views === 'number') setViews(d.views); })
      .catch(() => {});
  }, [article.id]);

  // ── 3. 字数 ──────────────────────────────────────
  useEffect(() => {
    const el = document.getElementById('article_content');
    if (el) setWordCount(el.innerText.replace(/\s+/g, '').length);
  }, [article.content]);

  // ── 3b. 浏览记录 + 阅读时长追踪(存入 localStorage)──
  useEffect(() => {
    const LS_VISITS   = 'blog_visits';    // [{id,title,ts}]
    const LS_READTIME = 'blog_read_time'; // {[id]:seconds}
    const CUTOFF_MS   = 7 * 86400_000;   // 7天

    // ① 记录本次浏览
    try {
      const visits: { id: number; title: string; ts: number }[] =
        JSON.parse(localStorage.getItem(LS_VISITS) || '[]');
      const now = Date.now();
      // 去重(已有则更新时间戳)+ 过滤7天外的
      const filtered = visits
        .filter(v => v.id !== article.id && now - v.ts < CUTOFF_MS)
        .slice(0, 49); // 最多保留 50 条
      filtered.unshift({ id: article.id, title: article.title, ts: now });
      localStorage.setItem(LS_VISITS, JSON.stringify(filtered));
    } catch {}

    // ② 计时阅读时长
    const startTs = Date.now();
    let accumulated = 0;
    let hidden = false;
    let hiddenAt = 0;

    function onVisibilityChange() {
      if (document.hidden) {
        hidden  = true;
        hiddenAt = Date.now();
      } else {
        if (hidden) {
          // 页面切走了,暂停计时
          accumulated -= Date.now() - hiddenAt;
          hidden = false;
        }
      }
    }
    document.addEventListener('visibilitychange', onVisibilityChange);

    function saveReadTime() {
      try {
        const elapsed = Math.round((Date.now() - startTs + accumulated) / 1000);
        if (elapsed < 3) return; // 少于3秒不计
        const map: Record<string, number> =
          JSON.parse(localStorage.getItem(LS_READTIME) || '{}');
        // 取历史最大值(防止刷新重置)
        map[String(article.id)] = Math.max(map[String(article.id)] || 0, elapsed);
        // 只保留最多 100 篇
        const entries = Object.entries(map)
          .sort(([,a],[,b]) => b - a)
          .slice(0, 100);
        localStorage.setItem(LS_READTIME, JSON.stringify(Object.fromEntries(entries)));
      } catch {}
    }

    // 离开页面时保存
    window.addEventListener('beforeunload', saveReadTime);
    // 每 30 秒也保存一次(防止直接关 tab)
    const interval = setInterval(saveReadTime, 30_000);

    return () => {
      document.removeEventListener('visibilitychange', onVisibilityChange);
      window.removeEventListener('beforeunload', saveReadTime);
      clearInterval(interval);
      saveReadTime(); // 路由切换时保存
    };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [article.id]);

  // ── 4. TOC 目录(滚动书签跟随)──────────────────
  useEffect(() => {
    let cleanupFn: (() => void) | null = null;
    // 切换文章时先清空旧 TOC 内容
    const tocContainer = document.getElementById('toc-nav');
    if (tocContainer) tocContainer.innerHTML = '';
    const tocCardWrap = document.getElementById('toc-card-wrap');
    if (tocCardWrap) tocCardWrap.style.display = '';
    const tocSideWrap = document.querySelector('.toc-sidebar-wrapper') as HTMLElement | null;
    if (tocSideWrap) tocSideWrap.style.display = '';
    // 等待 200ms 让 dangerouslySetInnerHTML 完全注入 DOM
    const timer = setTimeout(() => {
      cleanupFn = setupTOC();
    }, 300);

    function setupTOC() {
      const articleContent = document.getElementById('article_content');
      const tocContainer   = document.getElementById('toc-nav');
      const tocCardWrap    = document.getElementById('toc-card-wrap');
      const tocSideWrap    = document.querySelector('.toc-sidebar-wrapper') as HTMLElement | null;
      if (!articleContent || !tocContainer || !tocCardWrap) return null;
      const toc = tocContainer;

      const headings = Array.from(
        articleContent.querySelectorAll<HTMLElement>('h1,h2,h3,h4,h5,h6')
      );

      if (headings.length === 0) {
        tocCardWrap.style.display = 'none';
        if (!sameDayArticles.length && tocSideWrap) tocSideWrap.style.display = 'none';
        return null;
      }

      // ── 强制分配纯数字 ID(避免中文/特殊字符在 CSS 选择器中失效)──
      headings.forEach((h, i) => { h.id = `toc-h-${i}`; });

      // ── 构建 TOC HTML ─────────────────────────────
      let html = '<ul class="toc-list">';
      headings.forEach((h, i) => {
        const level = parseInt(h.tagName[1], 10);
        const text  = (h.textContent || '').trim().replace(/</g, '&lt;');
        html += `<li class="toc-item toc-level-${level}"><a class="toc-link" data-hid="toc-h-${i}" href="#toc-h-${i}">${text}</a></li>`;
      });
      html += '</ul>';
      toc.innerHTML = html;

      // ── 建立映射 ──────────────────────────────────
      const map = new Map<HTMLElement, HTMLAnchorElement>();
      headings.forEach((h, i) => {
        const a = toc.querySelector<HTMLAnchorElement>(`[data-hid="toc-h-${i}"]`);
        if (a) map.set(h, a);
      });

      let activeLink: HTMLAnchorElement | null = null;

      // ── scroll 滚动跟随 ────────────────────────────
      function updateActive() {
        const OFFSET = 110;
        let current: HTMLElement = headings[0];
        for (const h of headings) {
          if (h.getBoundingClientRect().top <= OFFSET) current = h;
        }
        const link = map.get(current);
        if (!link || link === activeLink) return;
        activeLink?.classList.remove('active');
        link.classList.add('active');
        activeLink = link;
        // TOC 面板内自动可见:操作容器 scrollTop,避免 scrollIntoView 在手机端滚动整页
        const tocCard = toc.closest<HTMLElement>('.toc-card') || toc.parentElement;
        if (tocCard && tocCard.scrollHeight > tocCard.clientHeight) {
          const linkTop    = link.offsetTop;
          const cardHeight = tocCard.clientHeight;
          const curScroll  = tocCard.scrollTop;
          if (linkTop < curScroll + 20 || linkTop > curScroll + cardHeight - 40) {
            tocCard.scrollTop = Math.max(0, linkTop - cardHeight / 2);
          }
        }
      }

      window.addEventListener('scroll', updateActive, { passive: true });
      updateActive();

      // ── 点击跳转 ──────────────────────────────────
      function onTocClick(e: MouseEvent) {
        const el = e.target as HTMLElement;
        const a  = el.closest<HTMLAnchorElement>('.toc-link');
        if (!a) return;
        e.preventDefault();
        e.stopPropagation();
        const hid = a.getAttribute('data-hid');
        if (!hid) return;
        scrollToId(hid);
        // 复制锚点
        if (window.isSecureContext && navigator.clipboard) {
          const orig = a.textContent || '';
          navigator.clipboard.writeText(
            `${location.origin}${location.pathname}${location.search}#${hid}`
          ).then(() => {
            a.textContent = '🔗 已复制!';
            setTimeout(() => { a.textContent = orig; }, 1500);
          }).catch(() => {});
        }
      }
      toc.addEventListener('click', onTocClick);

      // URL 有 hash 则自动跳转
      // Only scroll to hash if user explicitly navigated via anchor link (not on initial article load)
      if (location.hash && document.referrer && new URL(document.referrer, location.href).pathname === location.pathname) {
        setTimeout(() => scrollToId(location.hash.slice(1)), 300);
      }

      return () => {
        window.removeEventListener('scroll', updateActive);
        toc.removeEventListener('click', onTocClick);
      };
    }

    return () => {
      clearTimeout(timer);
      if (cleanupFn) cleanupFn();
    };
  }, [article.content]);

  // ── 5. Prism 代码高亮 + 一键复制 ────────────────
  useEffect(() => {
    // 切换文章时清理旧的代码块包装,避免重复
    document.querySelectorAll('.code-block-wrap pre[data-prism-done]').forEach(pre => {
      delete (pre as HTMLElement).dataset.prismDone;
    });
    document.querySelectorAll('.code-block-wrap').forEach(wrap => {
      const pre = wrap.querySelector('pre');
      if (pre) wrap.parentNode?.insertBefore(pre, wrap);
      wrap.remove();
    });
    prismDone.current = false;

    function doHighlight() {
      const Prism = (window as any).Prism;
      if (!Prism) return false;
      const articleEl = document.getElementById('article_content');
      if (!articleEl) return false;

      // CKEditor codesnippet 输出: <pre><code class="language-xxx">…</code></pre>
      articleEl.querySelectorAll<HTMLPreElement>('pre').forEach(pre => {
        if (pre.dataset.prismDone) return;
        pre.dataset.prismDone = '1';
        pre.classList.add('line-numbers');

        const code = pre.querySelector('code');
        if (code) {
          const hasLang = [...code.classList].some(c => c.startsWith('language-'));
          if (!hasLang) {
            const fromPre = [...pre.classList].find(c => c.startsWith('language-'));
            code.classList.add(fromPre ?? 'language-plaintext');
          }
        }

        // 已有包装则跳过
        if (pre.parentElement?.classList.contains('code-block-wrap')) return;

        // 包装 div
        const wrap = document.createElement('div');
        wrap.className = 'code-block-wrap';
        pre.parentNode!.insertBefore(wrap, pre);
        wrap.appendChild(pre);

        // 语言名
        const langMatch = (code?.className ?? pre.className).match(/language-(\S+)/);
        const lang = langMatch ? langMatch[1] : 'code';
        const displayLang = lang === 'plaintext' ? 'text' : lang;

        // 工具栏
        const toolbar = document.createElement('div');
        toolbar.className = 'code-toolbar-custom';
        toolbar.innerHTML = `
          <span class="code-lang-badge">${displayLang}</span>
          <button class="code-copy-btn" aria-label="复制代码">
            <i class="fas fa-copy"></i>&nbsp;复制
          </button>`;
        wrap.insertBefore(toolbar, pre);

        const btn = toolbar.querySelector<HTMLButtonElement>('.code-copy-btn')!;
        btn.addEventListener('click', () => {
          const text = (code?.textContent ?? pre.textContent) || '';
          navigator.clipboard.writeText(text).then(() => {
            btn.innerHTML = '<i class="fas fa-check"></i>&nbsp;已复制!';
            btn.classList.add('copied');
            setTimeout(() => {
              btn.innerHTML = '<i class="fas fa-copy"></i>&nbsp;复制';
              btn.classList.remove('copied');
            }, 2000);
          }).catch(() => {
            const ta = document.createElement('textarea');
            ta.value = text; ta.style.cssText = 'position:fixed;opacity:0';
            document.body.appendChild(ta); ta.select();
            document.execCommand('copy');
            document.body.removeChild(ta);
          });
        });
      });

      Prism.highlightAllUnder(articleEl);
      prismDone.current = true;
      return true;
    }

    let tries = 0;
    function poll() {
      if (doHighlight()) return;
      if (tries++ < 50) setTimeout(poll, 200);
    }
    const t = setTimeout(poll, 300);
    return () => clearTimeout(t);
  }, [article.content]);

  // ── 5b. MathJax 重新排版(切换文章 / 手机端同样生效)────
  useEffect(() => {
    const t = setTimeout(() => {
      const MJ = (window as any).MathJax;
      if (!MJ) return;
      const el = document.getElementById('article_content');
      if (!el) return;
      if (typeof MJ.typesetPromise === 'function') {
        MJ.typesetPromise([el]).catch(() => {});
      } else if (typeof MJ.Hub?.Queue === 'function') {
        MJ.Hub.Queue(['Typeset', MJ.Hub, el]);
      }
    }, 800);
    return () => clearTimeout(t);
  }, [article.content]);

  // ── 6. Fancybox 图片灯箱 ─────────────────────────
  useEffect(() => {
    const t = setTimeout(() => {
      const FB = (window as any).Fancybox;
      if (!FB) return;
      // 清理旧绑定
      try { FB.unbind?.('#article_content'); } catch {}
      document.querySelectorAll('#article_content a[data-fancybox]').forEach(a => {
        const img = a.querySelector('img');
        if (img) { a.parentNode?.insertBefore(img, a); a.remove(); }
      });
      document.querySelectorAll('#article_content img').forEach(img => {
        const el = img as HTMLImageElement;
        if (el.closest('a[data-fancybox]')) return;
        const a = document.createElement('a');
        a.href = el.src; a.dataset.fancybox = 'gallery'; a.dataset.caption = el.alt || '';
        el.parentNode!.insertBefore(a, el); a.appendChild(el);
      });
      FB.bind('[data-fancybox="gallery"]', {});
    }, 1500);
    return () => clearTimeout(t);
  }, [article.id]);

  // ── 7. SVG pan-zoom ──────────────────────────────
  useEffect(() => {
    const t = setTimeout(() => {
      const spz = (window as any).svgPanZoom;
      if (!spz) return;
      document.querySelectorAll('#article_content svg').forEach((el: any) => {
        if (el._spz) return;
        try { el._spz = spz(el, { zoomEnabled:true, controlIconsEnabled:true, fit:true, center:true, minZoom:.5, maxZoom:10 }); } catch {}
      });
    }, 1500);
    return () => clearTimeout(t);
  }, [article.id]);


  // ── Quiz 选择题 ──────────────────────────────────
  useEffect(() => {
    // 清理上一篇文章的 quiz wrappers
    document.querySelectorAll('[id^="quiz-wrap-"]').forEach(w => {
      const tbl = w.querySelector('table');
      if (tbl) { delete (tbl as HTMLElement).dataset.quizInit; w.parentNode?.insertBefore(tbl, w); }
      w.remove();
    });

    // ── 所有 quiz 逻辑定义在此,挂到 window 供 JSX onClick 调用 ──
    let _tbl: HTMLTableElement | null = null;
    let _idx = -1;
    let _multi = false;
    let _shuffled: { html: string; val: string }[] = [];
    let _answered = false;

    function $id(id: string) { return document.getElementById(id); }

    function setSubmitEnabled(en: boolean) {
      const btn = $id('qz-btn-submit') as HTMLButtonElement | null;
      if (!btn) return;
      // Don't use btn.disabled - React re-render resets it
      // Use pointer-events + opacity to simulate disabled state
      btn.style.opacity       = en ? '1' : '.45';
      btn.style.cursor        = en ? 'pointer' : 'not-allowed';
      btn.style.pointerEvents = en ? '' : 'none';
      btn.dataset.enabled     = en ? '1' : '0';
    }

    function renderOptions() {
      const box = $id('qz-options');
      if (!box) return;
      box.innerHTML = '';
      _shuffled.forEach((item, i) => {
        const letter = String.fromCharCode(65 + i);
        const div  = document.createElement('div');
        div.className = 'qz-opt'; div.id = 'qz-opt-' + i;
        const inp  = document.createElement('input') as HTMLInputElement;
        inp.type = _multi ? 'checkbox' : 'radio'; inp.name = 'qz';
        inp.value = item.val; inp.id = 'qz-inp-' + i;
        inp.addEventListener('change', () => {
          setSubmitEnabled((box.querySelectorAll('input:checked').length) > 0);
        });
        const lbl = document.createElement('label');
        lbl.htmlFor = 'qz-inp-' + i;
        lbl.innerHTML = `<strong>${letter}.</strong> ` + item.html;
        div.appendChild(inp); div.appendChild(lbl);
        div.addEventListener('click', (e) => {
          const t = e.target as HTMLElement;
          if (t === inp || t === lbl || t.tagName === 'STRONG' || t.tagName === 'B') return;
          if (!_multi) box.querySelectorAll<HTMLInputElement>('input').forEach(r => { r.checked = false; });
          inp.checked = !inp.checked;
          setSubmitEnabled(box.querySelectorAll('input:checked').length > 0);
        });
        box.appendChild(div);
      });
    }

    function openQuizModal(tbl: HTMLTableElement, idx: number, multi: boolean) {
      _tbl = tbl; _idx = idx; _multi = multi; _answered = false;
      const rows = Array.from(tbl.querySelectorAll('tr'));
      const n = rows.length;
      const titleEl = $id('qz-title');
      const bodyEl  = $id('qz-body');
      const tipsEl  = $id('qz-tips');
      if (titleEl) titleEl.textContent = `第${idx + 1}题 · ` + (multi ? '多选' : '单选');
      if (bodyEl)  bodyEl.innerHTML = rows[0] ? rows[0].innerHTML : '';
      // build options (exclude last 2 hidden rows + this btn row = n-3 options)
      const numOpts = n - 3;
      const opts: { html: string; val: string }[] = [];
      for (let i = 1; i <= numOpts; i++) {
        const row = rows[i]; if (!row) continue;
        const html = (row.innerHTML || '')
          .replace(/<td[^>]*>/gi, '').replace(/<\/td>/gi, '')
          .replace(/&nbsp;/g, '').trim().replace(/^[A-Za-z][..]\s*/, '');
        opts.push({ html, val: String.fromCharCode(64 + i) });
      }
      // shuffle
      for (let i = opts.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [opts[i], opts[j]] = [opts[j], opts[i]];
      }
      _shuffled = opts;
      renderOptions();
      if (tipsEl) tipsEl.textContent = '';
      document.querySelectorAll('.qz-analysis-block').forEach(el => el.remove());
      const retryBtn  = $id('qz-btn-retry')  as HTMLElement | null;
      const answerBtn = $id('qz-btn-answer') as HTMLElement | null;
      if (retryBtn)  retryBtn.style.display  = 'none';
      if (answerBtn) answerBtn.style.display = '';
      setSubmitEnabled(false);
      const overlay = $id('quiz-modal-overlay');
      if (overlay) overlay.style.display = 'flex';
      // fold wrapper with animation
      const wrap = $id('quiz-wrap-' + idx);
      if (wrap) {
        wrap.style.transition = 'transform .35s,opacity .35s';
        wrap.style.transformOrigin = 'left';
        wrap.style.transform = 'scaleX(0)';
        wrap.style.opacity = '0';
        setTimeout(() => { wrap.style.display = 'none'; }, 350);
      }
    }

    // Expose to window for JSX onClick handlers
    (window as any).openQuizModal = openQuizModal;

    (window as any).__qzClose = () => {
      const overlay = $id('quiz-modal-overlay');
      if (overlay) overlay.style.display = 'none';
      if (_idx >= 0) {
        const wrap = $id('quiz-wrap-' + _idx);
        if (wrap) {
          wrap.style.display = 'block';
          wrap.offsetHeight; // reflow
          wrap.style.transform = 'scaleX(1)';
          wrap.style.opacity = '1';
        }
      }
      _tbl = null; _idx = -1;
    };

    (window as any).__qzRetry = () => {
      _answered = false;
      renderOptions();
      const tipsEl = $id('qz-tips');
      if (tipsEl) tipsEl.textContent = '';
      document.querySelectorAll('.qz-analysis-block').forEach(el => el.remove());
      const retryBtn  = $id('qz-btn-retry')  as HTMLElement | null;
      const answerBtn = $id('qz-btn-answer') as HTMLElement | null;
      if (retryBtn)  retryBtn.style.display  = 'none';
      if (answerBtn) answerBtn.style.display = '';
      setSubmitEnabled(false);
    };

    (window as any).__qzAnswer = () => {
      if (!_tbl) return;
      const rows = Array.from(_tbl.querySelectorAll('tr'));
      const n = rows.length;
      if (rows[n - 2]) (rows[n - 2] as HTMLElement).style.cssText = 'display:table-row;color:#00FFE0;font-weight:700;';
      if (rows[n - 1]) (rows[n - 1] as HTMLElement).style.cssText = 'display:table-row;color:#00c8a8;font-size:95%;background:rgba(0,255,224,.04);';
      const wrap = $id('quiz-wrap-' + _idx);
      if (wrap) { const btn = wrap.querySelector('.quiz-start-btn'); btn?.remove(); }
      (window as any).__qzClose();
    };

    (window as any).__qzSubmit = () => {
      if (_answered) return;
      const box = $id('qz-options');
      if (!box) return;
      const submitBtn = $id('qz-btn-submit') as HTMLButtonElement | null;
      if (submitBtn?.dataset.enabled !== '1') return;
      const checked = Array.from(box.querySelectorAll<HTMLInputElement>('input:checked'));
      if (!checked.length) return;
      _answered = true;
      setSubmitEnabled(false);

      const fd = new FormData();
      const url = window.location.pathname + window.location.search;
      const bodyEl = $id('qz-body');
      const qtxt = (bodyEl?.textContent ?? '').replace(/\s*/g, '');
      const flag = ('id' + url + 'title' + qtxt).slice(0, 40);
      fd.append('title', flag);
      fd.append('flag', String(_idx));
      fd.append('select_type', _multi ? '多选' : '单选');
      const trows = _tbl ? Array.from(_tbl.querySelectorAll('tr')) : [];
      const rightAns = (trows[trows.length - 2]?.textContent ?? '').trim();
      fd.append('right-select', rightAns);
      if (_multi) { checked.forEach((inp, i) => fd.append('multiple' + i, inp.value)); }
      else { fd.append('st', checked[0].value); }

      fetch('/api/quiz/submit', { method: 'POST', body: fd })
        .then(r => r.json())
        .then((d: {
          status: string; value: string; right: string; all_times: number;
          right_times: number; rate: string; text: string;
          correctly_chosen_lis: number[]; incorrectly_chosen_lis: number[]; missed_correct_lis: number[];
        }) => {
          if (d.status !== 'SUCCESS') { _answered = false; setSubmitEnabled(true); return; }
          d.correctly_chosen_lis.forEach(n => {
            const el = $id('qz-opt-' + (n - 1));
            if (el) { el.classList.add('qz-correct'); el.insertAdjacentHTML('beforeend', '<span style="color:#00c850;margin-left:auto;flex-shrink:0">✔</span>'); }
          });
          d.incorrectly_chosen_lis.forEach(n => {
            const el = $id('qz-opt-' + (n - 1));
            if (el) { el.classList.add('qz-wrong'); el.insertAdjacentHTML('beforeend', '<span style="color:#ff5555;margin-left:auto;flex-shrink:0">✗</span>'); }
          });
          d.missed_correct_lis.forEach(n => {
            const el = $id('qz-opt-' + (n - 1));
            if (el) { el.classList.add('qz-missed'); el.insertAdjacentHTML('beforeend', '<span style="color:#00e064;margin-left:auto;flex-shrink:0">漏</span>'); }
          });
          const sorted   = d.value.split('').sort().join('');
          const rateP    = (parseFloat(d.rate) * 100).toFixed(0) + '%';
          const tipsEl   = $id('qz-tips');
          if (tipsEl) tipsEl.innerHTML =
            d.text + '<br/>正确答案: <b style="color:#00FFE0">' + d.right +
            '</b> &nbsp;您的选择: <b style="color:#ffb347">' + sorted +
            '</b><br/><span style="color:rgba(0,255,224,.6)">累计作答 ' +
            d.all_times + ' 次,正确 ' + d.right_times + ' 次,正确率 ' + rateP + '</span>';
          const retryBtn  = $id('qz-btn-retry')  as HTMLElement | null;
          const answerBtn = $id('qz-btn-answer') as HTMLElement | null;
          if (retryBtn)  retryBtn.style.display  = '';
          if (answerBtn) answerBtn.style.display = 'none';
          // 显示解析
          if (_tbl) {
            const rs = Array.from(_tbl.querySelectorAll('tr'));
            const analysisRow = rs[rs.length - 1] as HTMLElement | undefined;
            if (analysisRow) {
              const analysisText = (analysisRow.textContent ?? '').trim();
              if (analysisText) {
                const div = document.createElement('div');
                div.className = 'qz-analysis-block';
                div.style.cssText = 'margin:8px 18px 0;padding:10px 14px;background:rgba(0,255,224,.05);border-left:3px solid rgba(0,255,224,.6);border-radius:0 8px 8px 0;color:#a0e8d8;font-size:.88rem;line-height:1.7;';
                div.innerHTML = '<span style="color:#00FFE0;font-weight:700;font-size:.8rem;letter-spacing:1px;display:block;margin-bottom:4px;">📝 解析</span>' + analysisRow.innerHTML;
                const tipsEl2 = $id('qz-tips');
                tipsEl2?.parentNode?.insertBefore(div, tipsEl2.nextSibling);
              }
              analysisRow.style.cssText = 'display:table-row;color:#00c8a8;font-size:95%;background:rgba(0,255,224,.04);';
            }
          }
        })
        .catch(() => {
          const tipsEl = $id('qz-tips');
          if (tipsEl) tipsEl.textContent = '提交失败,请重试';
          _answered = false; setSubmitEnabled(true);
        });
    };

    // ── 初始化页面中的题目表格 ──
    const t = setTimeout(() => {
      const articleEl = document.getElementById('article_content');
      if (!articleEl) return;
      Array.from(articleEl.querySelectorAll<HTMLTableElement>('table')).forEach((tbl, j) => {
        const summary = tbl.getAttribute('summary') ?? '';
        if (summary !== '单选' && summary !== '多选') return;
        if (tbl.dataset.quizInit === '1') return;
        tbl.dataset.quizInit = '1';
        const isMultiple = summary === '多选';

        const wrapper = document.createElement('div');
        wrapper.id = 'quiz-wrap-' + j;
        wrapper.style.cssText = 'margin:1.5em 0;';
        tbl.parentNode?.insertBefore(wrapper, tbl);
        wrapper.appendChild(tbl);

        tbl.id = 'quiz-table-' + j;
        tbl.style.cssText = [
          'width:100%', 'font-size:16px', 'border-collapse:collapse',
          'background:linear-gradient(135deg,rgba(10,20,40,.92),rgba(5,15,30,.96))',
          'border:1px solid rgba(0,255,224,.25)', 'border-radius:12px',
          'box-shadow:0 0 20px rgba(0,255,224,.1)',
          'color:#c0e8f0', 'line-height:1.8', 'overflow:hidden',
        ].join(';');

        const rows = Array.from(tbl.querySelectorAll<HTMLTableRowElement>('tr'));
        const n = rows.length;
        if (n >= 2) rows[n - 2].style.display = 'none';
        if (n >= 1) rows[n - 1].style.display = 'none';
        rows.forEach((row, ri) => {
          if (ri < n - 2) {
            row.querySelectorAll('td,th').forEach(td => {
              (td as HTMLElement).style.cssText = 'padding:8px 16px;border-bottom:1px solid rgba(0,255,224,.08);';
            });
          }
        });

        const badge = document.createElement('div');
        badge.style.cssText = 'padding:4px 16px 2px;font-size:.75rem;color:rgba(0,255,224,.5);letter-spacing:1px;';
        badge.textContent = (isMultiple ? '【多选题】' : '【单选题】') + ' 第' + (j + 1) + '题';
        wrapper.insertBefore(badge, tbl);

        const btn = document.createElement('button');
        btn.className = 'quiz-start-btn';
        btn.textContent = '📖 开始作答';
        btn.style.cssText = [
          'display:block', 'width:100%', 'margin-top:10px',
          'padding:11px 0', 'font-size:15px',
          'background:rgba(0,255,224,.08)',
          'border:1px solid rgba(0,255,224,.35)',
          'border-radius:8px', 'color:#00FFE0',
          'cursor:pointer', 'transition:all .2s', 'letter-spacing:.5px',
        ].join(';');
        btn.onmouseenter = () => { btn.style.background = 'rgba(0,255,224,.18)'; btn.style.boxShadow = '0 0 14px rgba(0,255,224,.25)'; };
        btn.onmouseleave = () => { btn.style.background = 'rgba(0,255,224,.08)'; btn.style.boxShadow = 'none'; };
        btn.addEventListener('click', () => openQuizModal(tbl, j, isMultiple));
        wrapper.appendChild(btn);
      });
    }, 600);
    return () => clearTimeout(t);
  }, [article.id]);

  // ── 9. ECharts 词云 ──────────────────────────────
  function initWordCloud() {
    let n=0;
    (function tryInit(){
      if(n++>80)return;
      const ec=(window as any).echarts;if(!ec?.version){setTimeout(tryInit,50);return}
      const p=document.createElement('div');p.style.cssText='width:1px;height:1px;position:absolute;left:-9999px';document.body.appendChild(p);
      try{const c=ec.init(p);c.setOption({series:[{type:'wordCloud',data:[]}]});c.dispose();document.body.removeChild(p);doWordCloud(ec)}
      catch{document.body.removeChild(p);setTimeout(tryInit,60)}
    })();
  }
  function doWordCloud(ec:any){
    if(echartsReady.current)return;echartsReady.current=true;
    const aEl=document.getElementById('article_content'),cEl=document.getElementById('article-wordcloud-chart');
    if(!aEl||!cEl)return;const text=aEl.innerText;
    if(!text||text.trim().length<50){cEl.innerHTML='<p style="text-align:center;padding:20px;color:#999">文章内容较少</p>';return}
    const clean=text.replace(/[^\u4e00-\u9fa5]/g,'');
    const ng=new Map<string,number>();for(let n=1;n<=4;n++)for(let i=0;i<=clean.length-n;i++){const w=clean.substring(i,i+n);ng.set(w,(ng.get(w)||0)+1)}
    const ws=new Map<string,number>();
    for(const[w,f]of ng){if(w.length<2)continue;let mi=Infinity;for(let i=1;i<w.length;i++){const a=ng.get(w.slice(0,i))||0,b=ng.get(w.slice(i))||0;if(a>0&&b>0)mi=Math.min(mi,(f*clean.length)/(a*b));else{mi=0;break}}if(mi>0)ws.set(w,mi*Math.log(f+1))}
    const sorted=[...ws.entries()].sort((a,b)=>b[1]-a[1]).map(e=>e[0]);
    const fs=new Set<string>();for(const w of sorted)if(![...fs].some(fw=>fw.includes(w)))fs.add(w);
    const fd=new Set(fs);const res:string[]=[];let i=0;
    while(i<clean.length){let m=false;for(let l=Math.min(7,clean.length-i);l>1;l--){const w=clean.substring(i,i+l);if(fd.has(w)){res.push(w);i+=l;m=true;break}}if(!m)i++}
    const stop=new Set(['的','了','在','是','我','有','和','也','不','都','说','就','们','你','他','她','一个','这个','那个','什么','这','那','但','或','与','及','等']);
    const freq:Record<string,number>={};
    for(const w of[...res,...(text.match(/[a-zA-Z0-9._-]{3,}/g)||[])]){const lw=w.toLowerCase();if(!stop.has(lw)&&lw.length>=2)freq[lw]=(freq[lw]||0)+1}
    const data=Object.entries(freq).map(([name,value])=>({name,value})).sort((a,b)=>b.value-a.value).slice(0,100);
    if(data.length<3){cEl.innerHTML='<p style="text-align:center;padding:20px;color:#999">关键词不足</p>';return}
    const cols=['#5470c6','#91cc75','#fac858','#ee6666','#73c0de','#3ba272','#fc8452','#9a60b4','#ea7ccc'];
    const chart=ec.init(cEl);
    chart.setOption({tooltip:{show:true},series:[{type:'wordCloud',shape:'circle',left:'center',top:'center',width:'95%',height:'90%',sizeRange:[14,60],rotationRange:[-45,45],rotationStep:15,gridSize:10,drawOutOfBound:false,layoutAnimation:true,textStyle:{fontFamily:'sans-serif',fontWeight:'bold',color:()=>cols[Math.floor(Math.random()*cols.length)]},emphasis:{focus:'self',textStyle:{textShadowBlur:8,textShadowColor:'rgba(0,0,0,0.3)'}},data}]});
    window.addEventListener('resize',()=>chart.resize());
  }

  // 切换文章时重新生成词云
  useEffect(() => {
    echartsReady.current = false;
    const cEl = document.getElementById('article-wordcloud-chart');
    if (cEl) cEl.innerHTML = '';
    const t = setTimeout(() => initWordCloud(), 800);
    return () => clearTimeout(t);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [article.content]);

  // 阅读历史随文章更新
  useEffect(() => {
    const setCk=(n:string,v:string,d:number)=>{const dt=new Date();dt.setTime(dt.getTime()+d*864e5);document.cookie=`${n}=${encodeURIComponent(v)};expires=${dt.toUTCString()};path=/;SameSite=Lax${location.protocol==='https:'?';Secure':''}`;};
    const getCk=(n:string)=>{for(const c of document.cookie.split(';')){const t=c.trim();if(t.startsWith(n+'=')){try{return decodeURIComponent(t.slice(n.length+1))}catch{return null}}}return null};
    const COOKIE='recent_article_history',now=Date.now();
    const url=location.pathname+location.search,title=document.title.split(' - ')[0]||document.title;
    if(!url||!title)return;
    let hist:any[]=[];const raw=getCk(COOKIE);if(raw){try{const a=JSON.parse(raw);if(Array.isArray(a))hist=a}catch{}}
    hist=hist.filter(x=>x.url!==url&&x.visited>=now-7*864e5);hist.unshift({title,url,visited:now});if(hist.length>9)hist=hist.slice(0,9);
    setCk(COOKIE,JSON.stringify(hist),7);
  }, [article.id]);

  // ── 渲染 ─────────────────────────────────────────
  return (
    <>
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css" />
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css" />
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css" />

      <Script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js" strategy="afterInteractive" />
      <Script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom/dist/svg-pan-zoom.min.js" strategy="afterInteractive" />
      {/* Prism 加载顺序:core → plugins → autoloader,最后 highlightAll 由 useEffect 调用 */}
      <Script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js" strategy="afterInteractive" />
      <Script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js" strategy="afterInteractive" />
      <Script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js" strategy="afterInteractive" />
      <Script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js" strategy="afterInteractive" />
      <Script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud/dist/echarts-wordcloud.min.js" strategy="afterInteractive" onLoad={initWordCloud} />
      <Script id="mathjax-cfg" strategy="beforeInteractive">{`window.MathJax={tex:{inlineMath:[['$','$'],['\\\\(','\\\\)']],displayMath:[['$$','$$'],['\\\\[','\\\\]']]},options:{skipHtmlTags:['script','noscript','style','textarea','pre','code']}};`}</Script>
      <Script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" strategy="afterInteractive" />

      {/* 内联样式:代码块工具栏 + TOC active */}
      <style>{`
        /* ── 代码块包装 ── */
        .code-block-wrap{position:relative;margin:1.6em 0;border-radius:10px;overflow-x:auto;overflow-y:hidden;box-shadow:0 6px 28px rgba(0,0,0,.5)}
        .code-toolbar-custom{display:flex;align-items:center;justify-content:space-between;background:#13131f;padding:7px 14px 6px;border-bottom:1px solid rgba(255,255,255,.07)}
        .code-lang-badge{font-size:.7rem;color:#6272a4;font-family:"Fira Code","Consolas",monospace;text-transform:uppercase;letter-spacing:1.5px}
        .code-copy-btn{display:inline-flex;align-items:center;gap:4px;background:rgba(0,255,224,.07);border:1px solid rgba(0,255,224,.2);color:#00ffe0;border-radius:5px;padding:3px 12px;cursor:pointer;font-size:.72rem;transition:all .2s;white-space:nowrap}
        .code-copy-btn:hover{background:rgba(0,255,224,.18);border-color:#00ffe0}
        .code-copy-btn.copied{color:#52c41a;border-color:#52c41a}
        /* ── pre 代码渲染修复:确保字体可见 ── */
        .code-block-wrap pre[class*="language-"]{margin:0!important;border-radius:0!important;background:#1e1e2e!important;padding:18px 20px!important}
        .code-block-wrap pre[class*="language-"] code{font-family:"Fira Code","JetBrains Mono","Consolas","Courier New",monospace!important;font-size:.88rem!important;line-height:1.65!important;color:#cdd6f4!important}
        /* CKEditor codesnippet 没有 language-* 时也能显示 */
        #article_content pre{font-family:"Fira Code","JetBrains Mono","Consolas","Courier New",monospace!important;font-size:.87rem!important;line-height:1.65!important;background:#1e1e2e!important;color:#cdd6f4!important;border-radius:0 0 8px 8px!important;padding:16px 18px!important;overflow-x:auto!important}
        #article_content code{font-family:"Fira Code","JetBrains Mono","Consolas","Courier New",monospace!important;font-size:.87em!important;background:rgba(0,255,224,.08)!important;color:#89dceb!important;padding:2px 6px!important;border-radius:4px!important}
        #article_content pre code{background:transparent!important;color:inherit!important;padding:0!important;border-radius:0!important}
        .highlighted-heading{animation:hh-flash .7s ease;outline:2px solid #00ffe0;outline-offset:4px;border-radius:3px}
        @keyframes hh-flash{0%{background:rgba(0,255,224,.2)}100%{background:transparent}}
        .toc-list{list-style:none;margin:0;padding:0}
        .toc-item{margin:1px 0}
        #toc-nav .toc-link{display:block;padding:3px 8px;color:#8a9bb0;font-size:.82rem;text-decoration:none;transition:all .15s;border-left:3px solid transparent;border-radius:0 4px 4px 0}
        #toc-nav .toc-link:hover{color:#c0e8e0;background:rgba(0,255,224,.04)}
        #toc-nav .toc-link.active{color:#00ffe0!important;font-weight:700;border-left-color:#00ffe0;background:rgba(0,255,224,.07)}
        #toc-nav .toc-level-1>a{padding-left:6px}
        #toc-nav .toc-level-2>a{padding-left:16px}
        #toc-nav .toc-level-3>a{padding-left:26px;font-size:.78rem}
        #toc-nav .toc-level-4>a,#toc-nav .toc-level-5>a,#toc-nav .toc-level-6>a{padding-left:34px;font-size:.75rem}
        /* ── 文章布局 ── */
        .article-wrapper{display:flex;gap:24px;align-items:flex-start;max-width:1200px;margin:0 auto;padding:0 16px;width:100%;box-sizing:border-box}
        .article-main{flex:1;min-width:0;overflow:hidden}
        .article-card{background:rgba(8,14,32,.95);border:1px solid rgba(0,255,224,.15);border-radius:14px;overflow:hidden;padding:28px 28px 20px;box-sizing:border-box}
        .article-body{overflow-x:hidden;word-break:break-word;max-width:100%}
        .article-body img{max-width:100%;height:auto;display:block}
        .article-body table{max-width:100%;overflow-x:auto;display:block}
        .article-body pre{overflow-x:auto;max-width:100%;white-space:pre}
        .toc-sidebar-wrapper{width:260px;flex-shrink:0;position:sticky;top:80px;max-height:calc(100vh - 100px);overflow-y:auto}
        /* ── 手机端:目录折叠到顶部,内容不溢出 ── */
        @media(max-width:900px){
          body{overflow-x:hidden}
          .article-wrapper{flex-direction:column;padding:0 8px;gap:12px}
          .toc-sidebar-wrapper{width:100%;position:static;max-height:none;order:-1}
          .toc-card{max-height:220px;overflow-y:auto}
          .article-card{padding:14px 12px;overflow:hidden}
          .article-body{font-size:.95rem;overflow-x:hidden}
          .article-body *{max-width:100%;box-sizing:border-box}
          .code-block-wrap{margin:1em -4px;border-radius:8px}
          .code-block-wrap pre[class*="language-"]{font-size:.8rem!important;padding:14px 14px!important}
          #article_content pre{font-size:.8rem!important;padding:12px 14px!important}
        }
        @media(max-width:480px){
          .article-wrapper{padding:0 4px}
          .article-card{padding:10px 10px;border-radius:10px}
        }
      `}</style>

      <div className="article-wrapper">
        <main className="article-main">
          <article className="article-card">
            <header className="article-header">
              <h1 className="article-title">{article.title}</h1>
              <div className="article-meta">
                <span className="meta-item"><i className="fas fa-user fa-fw"/>{article.authorName}</span>
                <span className="meta-item"><i className="fas fa-eye fa-fw"/>{views} 浏览</span>
                <span className="meta-item"><i className="fas fa-calendar-alt fa-fw"/>发布: {article.created}</span>
                {article.updated && article.updated !== article.created && (
                  <span className="meta-item update-meta"><i className="fas fa-history fa-fw"/>更新: {article.updated}</span>
                )}
                <span className="meta-item"><i className="fas fa-file-word fa-fw"/>字数: {wordCount || '…'}</span>
                {article.isEncrypted && <span className="meta-item"><i className="fas fa-lock fa-fw"/>已加密</span>}
                {article.categoryName && (
                  <span className="meta-item">
                    分类: <Link href={`/?cat_id=${article.categoryId}`} className="badge-cat">{article.categoryName}</Link>
                  </span>
                )}
                <div ref={themeMenuRef} className="theme-switcher-wrap">
                  <button className="theme-btn" onClick={() => setShowThemeMenu(v => !v)}>
                    {THEME_CONFIG[theme]?.icon} <span className="d-none d-md-inline">{THEME_CONFIG[theme]?.label}</span> ▾
                  </button>
                  {showThemeMenu && (
                    <div className="theme-menu">
                      {Object.entries(THEME_CONFIG).map(([k, v]) => (
                        <span key={k} className={`theme-menu-item${theme===k?' active':''}`} onClick={() => applyTheme(k)}>
                          {v.icon} {v.label}
                        </span>
                      ))}
                    </div>
                  )}
                </div>
              </div>
            </header>

            <div id="article_content" className="article-body line-numbers"
              dangerouslySetInnerHTML={{ __html: article.content }}/>

            <div className="wordcloud-section">
              <div className="wordcloud-section-inner">
                <div className="wordcloud-title"><i className="fas fa-cloud fa-fw"/>本文内容词云</div>
                <div id="article-wordcloud-chart" style={{width:'100%',height:360}}/>
              </div>
            </div>

            <footer className="article-footer">
              <hr/>
              <div className="post-copyright-info small text-muted mb-4">
                <p><strong>本文作者:</strong>{article.authorName}</p>
                <p><strong>本文链接:</strong><a href={absoluteUrl} target="_blank" rel="noopener">{absoluteUrl}</a></p>
                <p><strong>版权声明:</strong>均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noreferrer">CC BY-NC-SA 4.0</a> 许可协议,转载请注明出处。</p>
              </div>
              {isAdmin && (
                <div className="admin-actions-panel mb-4">
                  <div className="admin-actions-panel-title"><i className="fas fa-user-shield"/> 管理员操作</div>
                  <Link href={`/admin/write?id=${article.id}`} className="btn btn-sm btn-outline-primary">
                    <i className="fas fa-edit"/> 编辑文章
                  </Link>
                </div>
              )}
              <div className="post-navigation">
                <div className="prev-post">
                  {prevArticle
                    ? <Link href={`/detail/?id=${prevArticle.id}`} className="btn btn-outline-secondary btn-sm rounded-pill"><i className="fas fa-chevron-left"/> {truncate(prevArticle.title,28)}</Link>
                    : <span className="text-muted small">已是第一篇</span>}
                </div>
                <div className="next-post text-right">
                  {nextArticle
                    ? <Link href={`/detail/?id=${nextArticle.id}`} className="btn btn-outline-secondary btn-sm rounded-pill">{truncate(nextArticle.title,28)} <i className="fas fa-chevron-right"/></Link>
                    : <span className="text-muted small">已是最后一篇</span>}
                </div>
              </div>
            </footer>
          </article>
        </main>

        <aside className="toc-sidebar-wrapper">
          <div id="toc-card-wrap" className="toc-card">
            <div className="toc-card-title"><i className="fas fa-stream fa-fw"/> 文章目录</div>
            <hr/>
            <div id="toc-nav" className="toc-nav-scroll">
              <p className="text-muted small text-center" style={{padding:'12px 0'}}>正在加载目录...</p>
            </div>
          </div>
          {sameDayArticles.length > 0 && (
            <div id="card-last-year" className="same-day-card memory-style">
              <div className="same-day-card-title"><i className="fas fa-history fa-fw text-info"/> 往年同期</div>
              <div className="timeline memory-timeline">
                {sameDayArticles.map(a => (
                  <div key={a.id} className="timeline-item memory-item">
                    {a.created && <div className="timeline-date">{a.created}</div>}
                    <div className="timeline-content"><Link href={`/detail/?id=${a.id}`}>{truncate(a.title,28)}</Link></div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </aside>
      </div>
          <ScrollTopWidget articleId={article.id} articleTitle={article.title} />

      {/* ── Quiz 选择题模态框 ── */}
      <div id="quiz-modal-overlay"
        onClick={(e)=>{if(e.currentTarget===e.target)(window as any).__qzClose?.()}}
        style={{display:'none',position:'fixed',inset:0,background:'rgba(0,0,0,.75)',zIndex:9990,alignItems:'center',justifyContent:'center'}}>
        <div style={{background:'rgba(8,16,32,.98)',border:'1px solid rgba(0,255,224,.3)',borderRadius:16,width:'min(560px,94vw)',maxHeight:'85vh',overflowY:'auto',boxShadow:'0 0 40px rgba(0,255,224,.15)'}}>
          <div style={{padding:'14px 18px',borderBottom:'1px solid rgba(0,255,224,.15)',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
            <span id="qz-title" style={{color:'#00FFE0',fontWeight:700,fontSize:'1rem'}}/>
            <button onClick={()=>(window as any).__qzClose?.()} style={{background:'none',border:'none',color:'rgba(0,255,224,.5)',fontSize:'1.5rem',cursor:'pointer',lineHeight:1}}>×</button>
          </div>
          <div id="qz-body" style={{padding:'16px 18px 6px',color:'#c0e8f0',lineHeight:1.7}}/>
          <div id="qz-options" style={{padding:'0 18px 6px'}}/>
          <div id="qz-tips" style={{padding:'4px 18px',color:'#ffb06a',fontSize:'.84rem',minHeight:20}}/>
          <div style={{padding:'12px 18px',borderTop:'1px solid rgba(0,255,224,.1)',display:'flex',justifyContent:'flex-end',gap:10,flexWrap:'wrap'}}>
            <button id="qz-btn-retry" onClick={()=>(window as any).__qzRetry?.()} style={{display:'none',background:'rgba(255,180,0,.1)',border:'1px solid rgba(255,180,0,.35)',color:'#ffb347',borderRadius:7,padding:'7px 16px',cursor:'pointer',fontSize:'.86rem'}}>🔄 重新作答</button>
            <button id="qz-btn-answer" onClick={()=>(window as any).__qzAnswer?.()} style={{background:'rgba(0,255,224,.06)',border:'1px solid rgba(0,255,224,.22)',color:'rgba(0,255,224,.7)',borderRadius:7,padding:'7px 16px',cursor:'pointer',fontSize:'.86rem'}}>看答案</button>
            <button id="qz-btn-submit" onClick={()=>(window as any).__qzSubmit?.()} style={{background:'rgba(0,255,224,.12)',border:'1px solid rgba(0,255,224,.3)',color:'#00FFE0',borderRadius:7,padding:'7px 18px',fontSize:'.88rem',fontWeight:700}}>提交</button>
          </div>
        </div>
      </div>
      <style>{`
        .qz-opt{display:flex;align-items:flex-start;gap:10px;padding:9px 12px;margin:4px 0;border-radius:8px;border:1px solid rgba(0,255,224,.12);cursor:pointer;transition:all .15s;background:rgba(0,255,224,.02);}
        .qz-opt:hover{border-color:rgba(0,255,224,.35);background:rgba(0,255,224,.07);}
        .qz-opt input{margin-top:3px;accent-color:#00FFE0;flex-shrink:0;cursor:pointer;}
        .qz-opt label{color:#c0e8f0;font-size:.93rem;line-height:1.6;cursor:pointer;flex:1;}
        .qz-correct{background:rgba(0,200,80,.14)!important;border-color:#00c850!important;}
        .qz-wrong{background:rgba(255,60,60,.14)!important;border-color:#ff4444!important;}
        .qz-missed{background:rgba(0,255,100,.06)!important;border-color:#00e064!important;border-style:dashed!important;}
        .quiz-start-btn:hover{background:rgba(0,255,224,.2)!important;box-shadow:0 0 12px rgba(0,255,224,.3)!important;}
      `}</style>

</>
  );
}

的文物馆