// export.jsx — 把一页打成「自带样式、可独立打开、可分享」的网页 / 幻灯片 / Markdown
// 依赖 engine.jsx 的 downloadFile / safeName / contentHash

function escHtml(s) {
  return String(s == null ? '' : s).replace(/[&<>"']/g, c => (
    { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]
  ));
}

// 媒体标记 [名称：完成态] → 识别成图 / 视频 / 音乐 / 配音 / 链接
function mediaKind(name) {
  const n = String(name || '');
  if (/视频|短片|youtube|影/i.test(n)) return 'video';
  if (/配音|语音|tts|朗读/i.test(n)) return 'voice';
  if (/音乐|配乐|谱|声/i.test(n)) return 'audio';
  if (/连接|链接|link/i.test(n)) return 'link';
  if (/图|画|插画|image/i.test(n)) return 'image';
  return 'image';
}
const MEDIA_GLYPH = {
  image: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="8.5" cy="10" r="1.5"/><path d="M4 17l5-5 4 4 3-3 4 4"/></svg>',
  video: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="3.5" y="6" width="13" height="12" rx="2"/><path d="M16.5 10l4-2.2v8.4l-4-2.2"/></svg>',
  audio: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><path d="M4 9v6M8 6v12M12 8v8M16 5v14M20 9v6"/></svg>',
  voice: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3"/></svg>',
  link:  '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><path d="M10 13a5 5 0 0 0 7.07 0l2-2a5 5 0 0 0-7.07-7.07l-1 1"/><path d="M14 11a5 5 0 0 0-7.07 0l-2 2a5 5 0 0 0 7.07 7.07l1-1"/></svg>',
};
const MEDIA_LABEL = { image: '图像', video: '影像', audio: '配乐', voice: '配音', link: '连接' };

function mediaCardHtml(name, done) {
  const k = mediaKind(name);
  return '<figure class="pp-media pp-' + k + '">'
    + '<span class="pp-media-ico">' + MEDIA_GLYPH[k] + '</span>'
    + '<span class="pp-media-meta"><b>' + escHtml(name) + '</b><i>' + escHtml(done || MEDIA_LABEL[k]) + '</i></span>'
    + '</figure>';
}

// 真实媒体内联：图片 / 视频 / 音频·配乐 / 作者朗读 / YouTube → 可直接播放、随网页自包含
function realMediaHtml(m) {
  if (!m || m.placeholder || !(m.src || m.embed)) return null;
  if (m.kind === 'image') {
    return '<figure class="pp-fig"><img src="' + escHtml(m.src) + '" alt="' + escHtml(m.name || '') + '" loading="lazy"/>'
      + (m.name ? '<figcaption>' + escHtml(m.name) + '</figcaption>' : '') + '</figure>';
  }
  if (m.kind === 'video') {
    return '<figure class="pp-fig pp-fig-video"><video src="' + escHtml(m.src) + '" controls playsinline preload="metadata"></video>'
      + (m.name ? '<figcaption>' + escHtml(m.name) + '</figcaption>' : '') + '</figure>';
  }
  if (m.kind === 'youtube' && m.embed) {
    return '<figure class="pp-fig pp-embed"><div class="pp-embed-frame"><iframe src="' + escHtml(m.embed) + '" title="YouTube" frameborder="0" '
      + 'allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" allowfullscreen></iframe></div>'
      + '<figcaption>YouTube 片段' + (m.span ? ' · ' + escHtml(m.span) : '') + '</figcaption></figure>';
  }
  if (m.kind === 'voice' || m.kind === 'audio') {
    const isAuthor = m.kind === 'voice' && m.author;
    const label = m.kind === 'voice' ? (m.label || '配音') : '配乐';
    const sub = isAuthor ? '作者的声音 · 比文字更近一层' : (m.name || label);
    return '<figure class="pp-audio' + (isAuthor ? ' pp-voice' : '') + '">'
      + '<div class="pp-audio-head"><span class="pp-audio-ico">' + (m.kind === 'voice' ? MEDIA_GLYPH.voice : MEDIA_GLYPH.audio) + '</span>'
      + '<span class="pp-audio-meta"><b>' + escHtml(label) + '</b><i>' + escHtml(sub) + '</i></span></div>'
      + '<audio src="' + escHtml(m.src) + '" controls preload="metadata"></audio></figure>';
  }
  return null;
}

// 行内 markdown：**粗** *斜* `码` [链](url)
function mdInlineExport(text) {
  let s = escHtml(text);
  s = s.replace(/`([^`]+)`/g, '<code>$1</code>');
  s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
  s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
  s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
  return s;
}

// 正文（markdown 文本 + [媒体标记]）→ HTML；media 为按序的真实媒体（内联用）
function exportBodyToHtml(body, media) {
  const lines = String(body || '').split('\n');
  let html = '', para = [], mi = 0;
  const flush = () => { if (para.length) { html += '<p>' + para.map(mdInlineExport).join('<br/>') + '</p>'; para = []; } };
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].replace(/\u00a0/g, ' ');
    const t = line.trim();
    if (!t) { flush(); continue; }
    // 围栏代码块 ```
    if (t.startsWith('```')) {
      flush(); const buf = []; i++;
      while (i < lines.length && !lines[i].trim().startsWith('```')) { buf.push(lines[i]); i++; }
      html += '<pre><code>' + escHtml(buf.join('\n')) + '</code></pre>'; continue;
    }
    // 媒体标记 [名称：完成态] —— 优先内联真实媒体，缺失时回落为占位卡
    const m = t.match(/^\[([^：:]+)[：:]\s*(.*?)\]$/);
    if (m) {
      flush();
      const payload = media && media[mi++];
      html += (payload && realMediaHtml(payload)) || mediaCardHtml(m[1].trim(), m[2].trim());
      continue;
    }
    // 列表
    if (/^[-*]\s/.test(t)) {
      flush(); const buf = [];
      while (i < lines.length && /^\s*[-*]\s/.test(lines[i])) { buf.push(lines[i].trim().replace(/^[-*]\s/, '')); i++; }
      i--; html += '<ul>' + buf.map(b => '<li>' + mdInlineExport(b) + '</li>').join('') + '</ul>'; continue;
    }
    if (/^>\s?/.test(t)) {
      flush(); const buf = [];
      while (i < lines.length && /^>/.test(lines[i].trim())) { buf.push(lines[i].trim().replace(/^>\s?/, '')); i++; }
      i--; html += '<blockquote>' + buf.filter(Boolean).map(b => '<p>' + mdInlineExport(b) + '</p>').join('') + '</blockquote>'; continue;
    }
    if (/^#{1,3}\s/.test(t)) { flush(); const lv = t.match(/^#+/)[0].length; html += '<h' + (lv + 1) + ' class="pp-sub">' + mdInlineExport(t.replace(/^#+\s/, '')) + '</h' + (lv + 1) + '>'; continue; }
    if (/^(-{3,}|\*{3,})$/.test(t)) { flush(); html += '<hr/>'; continue; }
    para.push(t);
  }
  flush();
  return html || '<p class="pp-empty">这一页还没有正文。</p>';
}

const SHOWCASE_CSS = `
:root{--paper:#FAF6EE;--paper2:#F3EDE0;--ink:#2a2a2a;--ink2:#6a6a6a;--ink3:#a6a097;--sea:#1a2840;--flame:#8B2020;--gold:#E8C840;--rule:#E4DBCB;--mono:'JetBrains Mono','SF Mono',Menlo,monospace;--serif:'EB Garamond',Georgia,serif;--cjk:'Noto Serif SC','Songti SC',serif;}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{background:var(--paper);color:var(--ink);font-family:var(--cjk);-webkit-font-smoothing:antialiased;line-height:1.95;}
body::before{content:'';position:fixed;inset:0;pointer-events:none;background:radial-gradient(1200px 600px at 50% -10%,rgba(26,40,64,.05),transparent 60%);}
.pp-wrap{max-width:680px;margin:0 auto;padding:0 28px 96px;}
.pp-mast{display:flex;align-items:center;gap:10px;padding:30px 0 0;}
.pp-mark{font-family:var(--mono);font-size:11px;letter-spacing:.46em;color:var(--sea);text-transform:uppercase;}
.pp-rule{flex:1;height:1px;background:var(--rule);}
.pp-flame{width:26px;height:3px;background:var(--flame);border-radius:2px;}
.pp-hero{padding:54px 0 30px;border-bottom:1px solid var(--rule);}
.pp-kicker{font-family:var(--mono);font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--ink3);margin-bottom:18px;}
.pp-title{font-family:var(--cjk);font-weight:500;font-size:39px;line-height:1.28;margin:0;letter-spacing:.01em;}
.pp-en{font-family:var(--serif);font-style:italic;font-weight:400;font-size:22px;color:var(--ink2);margin:10px 0 0;}
.pp-coords{display:flex;flex-wrap:wrap;gap:8px;margin-top:24px;}
.pp-coord{font-family:var(--mono);font-size:11px;color:var(--ink2);background:var(--paper2);border:1px solid var(--rule);border-radius:6px;padding:4px 10px;}
.pp-coord b{color:var(--ink3);font-weight:400;margin-right:6px;}
.pp-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px;}
.pp-tag{font-size:11px;padding:3px 9px;border-radius:5px;border:1px solid;}
.pp-tag.cat{color:#534AB7;background:#EEEDFE;border-color:#cfccf5;}
.pp-tag.note{color:#085041;background:#E1F5EE;border-color:#bfe6d8;}
.pp-tag.loc{color:#7a5b00;background:#FDF6E0;border-color:#ecdca0;}
.pp-body{padding:34px 0 0;font-family:var(--cjk);font-size:17px;line-height:1.98;color:#222;}
.pp-body p{margin:0 0 18px;text-wrap:pretty;}
.pp-body .pp-sub{font-family:var(--cjk);font-weight:500;margin:30px 0 12px;line-height:1.4;}
.pp-body h2.pp-sub{font-size:21px;}.pp-body h3.pp-sub{font-size:18px;}.pp-body h4.pp-sub{font-size:16px;color:var(--ink2);}
.pp-body blockquote{margin:0 0 18px;padding:6px 0 6px 20px;border-left:2px solid var(--gold);background:linear-gradient(90deg,#FDF6E0,transparent 80%);font-style:italic;color:#4a4636;}
.pp-body blockquote p{margin:0 0 6px;}.pp-body blockquote p:last-child{margin:0;}
.pp-body ul{margin:0 0 18px;padding-left:22px;}.pp-body li{margin:0 0 7px;}
.pp-body strong{font-weight:600;color:#1c1c1c;}.pp-body em{font-style:italic;}
.pp-body code{font-family:var(--mono);font-size:14px;background:var(--paper2);padding:1px 6px;border-radius:4px;}
.pp-body pre{font-family:var(--mono);font-size:13.5px;line-height:1.7;background:var(--paper2);border:1px solid var(--rule);padding:14px 16px;border-radius:10px;overflow-x:auto;margin:0 0 18px;}
.pp-body pre code{background:none;padding:0;font-size:13.5px;}
.pp-body a{color:var(--flame);text-decoration:none;border-bottom:1px solid rgba(139,32,32,.32);}
.pp-body hr{border:0;border-top:1px solid var(--rule);margin:24px 0;}
.pp-empty{color:var(--ink3);font-style:italic;}
.pp-media{display:flex;align-items:center;gap:14px;margin:0 0 18px;padding:16px 18px;border:1px solid var(--rule);border-radius:12px;background:repeating-linear-gradient(135deg,#fff,#fff 9px,#FBF7EF 9px,#FBF7EF 18px);}
.pp-media-ico{flex:none;width:38px;height:38px;border-radius:9px;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:var(--sea);}
.pp-media-ico svg{width:21px;height:21px;}
.pp-media-meta{display:flex;flex-direction:column;gap:3px;}
.pp-media-meta b{font-family:var(--cjk);font-weight:500;font-size:14px;color:var(--ink);}
.pp-media-meta i{font-family:var(--mono);font-style:normal;font-size:11px;color:var(--ink3);}
.pp-video .pp-media-ico{color:var(--flame);}.pp-audio .pp-media-ico,.pp-voice .pp-media-ico{color:#085041;}.pp-link .pp-media-ico{color:#534AB7;}
.pp-fig{margin:0 0 22px;}
.pp-fig img{display:block;width:100%;height:auto;border-radius:12px;border:1px solid var(--rule);}
.pp-fig-video video{display:block;width:100%;border-radius:12px;border:1px solid var(--rule);background:#000;}
.pp-fig figcaption{font-family:var(--mono);font-size:11px;color:var(--ink3);margin-top:8px;text-align:center;}
.pp-embed-frame{position:relative;width:100%;aspect-ratio:16/9;border-radius:12px;overflow:hidden;background:#000;border:1px solid var(--rule);}
.pp-embed-frame iframe{position:absolute;inset:0;width:100%;height:100%;}
.pp-audio{margin:0 0 22px;padding:16px 18px;border:1px solid var(--rule);border-radius:12px;background:var(--paper2);}
.pp-audio .pp-audio-head{display:flex;align-items:center;gap:13px;margin-bottom:12px;}
.pp-audio-ico{flex:none;width:38px;height:38px;border-radius:9px;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:#085041;}
.pp-audio-ico svg{width:21px;height:21px;}
.pp-audio-meta{display:flex;flex-direction:column;gap:3px;}
.pp-audio-meta b{font-family:var(--cjk);font-weight:500;font-size:14px;color:var(--ink);}
.pp-audio-meta i{font-family:var(--mono);font-style:normal;font-size:11px;color:var(--ink3);}
.pp-audio audio{width:100%;display:block;}
.pp-voice{background:linear-gradient(180deg,#FBF3EE,var(--paper2));border-color:#E8C9BC;}
.pp-voice .pp-audio-ico{color:var(--flame);border-color:#E8C9BC;}
.pp-seal{display:flex;gap:16px;margin:46px 0 0;padding:20px 22px;background:var(--paper2);border:1px solid var(--rule);border-radius:14px;position:relative;}
.pp-seal-fp{flex:none;width:40px;height:40px;border-radius:50%;background:#fff;border:1px solid var(--rule);display:grid;place-items:center;color:var(--sea);}
.pp-seal-fp svg{width:23px;height:23px;}
.pp-seal-body{flex:1;min-width:0;}
.pp-seal-h{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink3);font-weight:600;margin-bottom:11px;}
.pp-row{display:flex;gap:12px;font-size:12px;margin-bottom:7px;align-items:baseline;}
.pp-row .k{flex:none;width:64px;color:var(--ink3);font-family:var(--cjk);}
.pp-row .v{font-family:var(--mono);color:var(--ink2);word-break:break-all;}
.pp-row .v.hash{color:var(--sea);}
.pp-row .v.link{color:var(--flame);}
.pp-seal-note{font-family:var(--cjk);font-size:11px;color:var(--ink3);line-height:1.7;margin-top:10px;padding-top:10px;border-top:1px solid var(--rule);}
.pp-chop{flex:none;align-self:flex-start;width:32px;height:32px;background:var(--flame);color:#F6E4DC;font-family:var(--cjk);font-size:11px;line-height:1.12;text-align:center;padding-top:4px;border-radius:5px;letter-spacing:.04em;}
.pp-foot{margin:40px 0 0;padding-top:22px;border-top:1px solid var(--rule);display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;}
.pp-foot-l{font-family:var(--cjk);font-size:12px;color:var(--ink2);}
.pp-foot-l b{font-family:var(--mono);letter-spacing:.3em;color:var(--sea);font-weight:400;text-transform:uppercase;font-size:11px;}
.pp-foot-r{font-family:var(--cjk);font-size:11px;color:var(--ink3);font-style:italic;}
@media(max-width:560px){.pp-wrap{padding:0 18px 64px;}.pp-title{font-size:30px;}}
`;

const FP_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M12 4a8 8 0 0 1 8 8"/><path d="M4 12a8 8 0 0 1 4-6.9"/><path d="M7 12a5 5 0 0 1 10 0v2"/><path d="M10 12a2 2 0 0 1 4 0v3a4 4 0 0 0 .8 2.4"/><path d="M7 14v1a6 6 0 0 0 1.2 3.6"/><path d="M12 16v3"/></svg>';

// 完整自带样式的网页
function buildShowcaseHTML(o) {
  o = o || {};
  const title = o.title || '未命名';
  const coords = [];
  if (o.date) coords.push('<span class="pp-coord"><b>制作</b>' + escHtml(o.date) + '</span>');
  if (o.city) coords.push('<span class="pp-coord"><b>地点</b>' + escHtml(o.city) + '</span>');
  if (o.scene) coords.push('<span class="pp-coord"><b>场景</b>' + escHtml(o.scene) + '</span>');
  if (o.gps) coords.push('<span class="pp-coord"><b>GPS</b>' + escHtml(o.gps) + '</span>');
  const tags = []
    .concat(o.cat ? ['<span class="pp-tag cat">' + escHtml(o.cat) + '</span>'] : [])
    .concat((o.notes || []).map(t => '<span class="pp-tag note">' + escHtml(t) + '</span>'))
    .concat((o.locs || []).map(t => '<span class="pp-tag loc">' + escHtml(t) + '</span>'));
  const kicker = [o.date, o.city].filter(Boolean).join(' · ');

  return '<!doctype html><html lang="zh"><head><meta charset="utf-8"/>'
    + '<meta name="viewport" content="width=device-width,initial-scale=1"/>'
    + '<title>' + escHtml(title) + ' · popor</title>'
    + '<meta name="generator" content="popor"/>'
    + '<link rel="preconnect" href="https://fonts.googleapis.com"/>'
    + '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>'
    + '<link href="https://fonts.googleapis.com/css2?family=EB+Garamond:ital@0;1&family=Noto+Serif+SC:wght@400;500&family=JetBrains+Mono&display=swap" rel="stylesheet"/>'
    + '<style>' + SHOWCASE_CSS + '</style></head><body><div class="pp-wrap">'
    + '<div class="pp-mast"><span class="pp-mark">popor</span><span class="pp-rule"></span><span class="pp-flame"></span></div>'
    + '<header class="pp-hero">'
    + (kicker ? '<div class="pp-kicker">' + escHtml(kicker) + '</div>' : '')
    + '<h1 class="pp-title">' + escHtml(title) + '</h1>'
    + (o.en ? '<div class="pp-en">' + escHtml(o.en) + '</div>' : '')
    + (coords.length ? '<div class="pp-coords">' + coords.join('') + '</div>' : '')
    + (tags.length ? '<div class="pp-tags">' + tags.join('') + '</div>' : '')
    + '</header>'
    + '<main class="pp-body">' + exportBodyToHtml(o.body, o.media) + '</main>'
    + '<section class="pp-seal"><span class="pp-seal-fp">' + FP_SVG + '</span>'
    + '<div class="pp-seal-body"><div class="pp-seal-h">数字凭证 · Proof of Authorship</div>'
    + '<div class="pp-row"><span class="k">确权时间</span><span class="v">' + escHtml(o.stamp || '') + '</span></div>'
    + '<div class="pp-row"><span class="k">内容指纹</span><span class="v hash">' + escHtml(o.hash || '') + '</span></div>'
    + (o.shareId ? '<div class="pp-row"><span class="k">分享链接</span><span class="v link">popor.ink/p/' + escHtml(o.shareId) + '</span></div>' : '')
    + '<div class="pp-seal-note">指纹由内容经 SHA-256 计算（此处为演示哈希），可锚定到公链时间戳（OpenTimestamps），作为"这页文字在此刻已存在且未被篡改"的证据——原文不上链，隐私保留。</div>'
    + '</div><div class="pp-chop">钤<br/>印</div></section>'
    + '<footer class="pp-foot"><div class="pp-foot-l">用 <b>popor</b> 书写</div>'
    + '<div class="pp-foot-r">时间、地点与笔迹，都是这页文字的一部分。</div></footer>'
    + '</div></body></html>';
}

// 网页幻灯片（内测）：封面 + 按段落分页，← → 翻页，可打印成 PDF/PPT
function buildSlidesHTML(o) {
  o = o || {};
  const title = o.title || '未命名';
  const blocks = exportBodyToHtml(o.body, o.media)
    .split(/(?=<h[2-4]|<figure|<blockquote)/)
    .map(s => s.trim()).filter(Boolean);
  // 把内容聚成每页 1–2 块
  const slides = [];
  for (let i = 0; i < blocks.length; i += 2) slides.push(blocks.slice(i, i + 2).join(''));
  const kicker = [o.date, o.city].filter(Boolean).join(' · ');

  const cover = '<section class="sl cover"><div class="sl-in">'
    + (kicker ? '<div class="sl-kick">' + escHtml(kicker) + '</div>' : '')
    + '<h1>' + escHtml(title) + '</h1>'
    + (o.en ? '<div class="sl-en">' + escHtml(o.en) + '</div>' : '')
    + '<div class="sl-mark">用 popor 书写</div></div></section>';
  const content = slides.map((s, i) =>
    '<section class="sl"><div class="sl-in"><div class="sl-no">' + String(i + 1).padStart(2, '0') + '</div><div class="sl-body">' + s + '</div></div></section>'
  ).join('');
  const sealSlide = '<section class="sl seal"><div class="sl-in"><div class="sl-seal-h">数字凭证</div>'
    + '<div class="sl-seal-row">' + escHtml(o.stamp || '') + '</div>'
    + '<div class="sl-seal-row mono">' + escHtml(o.hash || '') + '</div>'
    + (o.shareId ? '<div class="sl-seal-row link">popor.ink/p/' + escHtml(o.shareId) + '</div>' : '')
    + '</div></section>';

  const css = SHOWCASE_CSS + `
body{overflow:hidden;}
.sl{position:fixed;inset:0;display:none;place-items:center;padding:7vmin;}
.sl.on{display:grid;}
.sl-in{width:min(900px,92vw);}
.sl.cover .sl-in{text-align:center;}
.sl-kick{font-family:var(--mono);font-size:13px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink3);margin-bottom:24px;}
.sl.cover h1{font-family:var(--cjk);font-weight:500;font-size:6vmin;line-height:1.25;margin:0;}
.sl-en{font-family:var(--serif);font-style:italic;font-size:3vmin;color:var(--ink2);margin-top:14px;}
.sl-mark{font-family:var(--mono);font-size:11px;letter-spacing:.34em;text-transform:uppercase;color:var(--sea);margin-top:42px;}
.sl-no{font-family:var(--mono);font-size:13px;color:var(--flame);letter-spacing:.1em;margin-bottom:22px;}
.sl-body{font-size:2.7vmin;line-height:1.85;}
.sl-body p{margin:0 0 16px;}
.sl-body .pp-media{font-size:14px;}
.sl-body .pp-fig{margin:0;}
.sl-body .pp-fig img,.sl-body .pp-fig-video video{max-height:60vh;width:auto;max-width:100%;margin:0 auto;}
.sl-body .pp-embed-frame{max-width:78vh;margin:0 auto;}
.sl-body .pp-audio{font-size:14px;}
.sl.seal{background:var(--sea);}
.sl.seal .sl-in{text-align:center;color:var(--sea-text,#C8DDF0);}
.sl-seal-h{font-family:var(--mono);font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:#8aa3c8;margin-bottom:24px;}
.sl-seal-row{font-family:var(--cjk);font-size:2.4vmin;color:#C8DDF0;margin-bottom:12px;}
.sl-seal-row.mono{font-family:var(--mono);font-size:1.8vmin;color:#9fb6d8;word-break:break-all;}
.sl-seal-row.link{color:#E8A28C;font-family:var(--mono);}
.sl-nav{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:16px;font-family:var(--mono);font-size:12px;color:var(--ink3);z-index:5;}
.sl-nav button{font:inherit;color:var(--ink2);background:#fff;border:1px solid var(--rule);border-radius:8px;width:34px;height:30px;cursor:pointer;}
.sl-nav button:hover{border-color:var(--ink);}
@media print{.sl{display:grid!important;position:relative;page-break-after:always;height:100vh;}.sl-nav{display:none;}}
`;
  const all = cover + content + sealSlide;
  const n = slides.length + 2;
  return '<!doctype html><html lang="zh"><head><meta charset="utf-8"/>'
    + '<meta name="viewport" content="width=device-width,initial-scale=1"/>'
    + '<title>' + escHtml(title) + ' · 幻灯片</title>'
    + '<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>'
    + '<link href="https://fonts.googleapis.com/css2?family=EB+Garamond:ital@0;1&family=Noto+Serif+SC:wght@400;500&family=JetBrains+Mono&display=swap" rel="stylesheet"/>'
    + '<style>' + css + '</style></head><body>' + all
    + '<div class="sl-nav"><button id="pv">‹</button><span id="pg">1 / ' + n + '</span><button id="nx">›</button><span style="opacity:.6">← → 翻页 · Ctrl/⌘+P 存 PDF</span></div>'
    + '<script>var S=[].slice.call(document.querySelectorAll(".sl")),i=0;function go(k){i=Math.max(0,Math.min(S.length-1,k));S.forEach(function(s,x){s.classList.toggle("on",x===i)});document.getElementById("pg").textContent=(i+1)+" / "+S.length;}document.getElementById("nx").onclick=function(){go(i+1)};document.getElementById("pv").onclick=function(){go(i-1)};addEventListener("keydown",function(e){if(e.key==="ArrowRight")go(i+1);if(e.key==="ArrowLeft")go(i-1);});go(0);<\/script>'
    + '</body></html>';
}

// 媒体标记 → Markdown（图片内联为 data URL；音视频注明见网页版）
function mdBodyWithMedia(body, media) {
  let mi = 0;
  return String(body || '').replace(/^\[([^：:\n]+)[：:]\s*(.*?)\]$/gm, (full, name, done) => {
    const m = media && media[mi++];
    if (!m || m.placeholder) return full;
    if (m.kind === 'image' && m.src) return '![' + (m.name || '图片') + '](' + m.src + ')';
    if (m.kind === 'youtube' && m.embed) return '> 🎬 YouTube 片段' + (m.span ? '（' + m.span + '）' : '') + '：' + m.embed;
    if ((m.kind === 'voice' || m.kind === 'audio') && m.src) return '> 🔊 ' + (m.label || m.name || '语音') + '（音频已内联，见网页版）';
    if (m.kind === 'video' && m.src) return '> 🎞️ ' + (m.name || '视频') + '（视频已内联，见网页版）';
    return full;
  });
}

function buildMarkdownExport(o) {
  o = o || {};
  const meta = [o.date && '📅 ' + o.date, o.city && '📍 ' + o.city, o.scene && '🏠 ' + o.scene].filter(Boolean).join(' · ');
  return '# ' + (o.title || '未命名') + '\n\n'
    + (o.en ? '*' + o.en + '*\n\n' : '')
    + (meta ? '> ' + meta + '\n\n' : '')
    + mdBodyWithMedia(o.body, o.media).trim() + '\n\n'
    + '---\n\n'
    + '**数字凭证 · Proof of Authorship**\n\n'
    + '- 确权时间：' + (o.stamp || '') + '\n'
    + '- 内容指纹（SHA-256，演示）：`' + (o.hash || '') + '`\n'
    + (o.shareId ? '- 分享链接：popor.ink/p/' + o.shareId + '\n' : '')
    + '\n*用 popor 书写。*\n';
}

Object.assign(window, {
  escHtml, exportBodyToHtml, buildShowcaseHTML, buildSlidesHTML, buildMarkdownExport,
});
