#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
宝锐诊断原料 · 客户宣讲 PPT 生成器 v4（科技风动画版）
输入：deck_data.json
输出：宝锐诊断原料_客户宣讲_科技风动画版_41页_可编辑.html

设计原则
- 零外部依赖（无 CDN / 无框架），单文件，离线可开
- 瑞士风基座：宝锐蓝 #002FA7 + 白底大留白 + 极简排版
- 动画 4 套：A 流动数据线｜B 分子粒子网络｜C DNA 双螺旋｜D 全页分子背景
- 动画只出现在 封面 / 章节承接页 / 3 个技术页；正文页保持纯白
- 全部文本 contenteditable，可直接在浏览器里改
"""
import json, os, html

BASE = os.path.dirname(os.path.abspath(__file__))
DATA = json.load(open(os.path.join(BASE, 'deck_data.json'), encoding='utf-8'))
OUT = os.path.join(BASE, '宝锐诊断原料_客户宣讲_科技风动画版_41页_可编辑.html')

def esc(s): return html.escape(str(s), quote=False)

pages = []           # 页面 HTML 列表
page_titles = []     # 导航用

def add(title, body, cls='', nav=None):
    pages.append({'title': title, 'body': body, 'cls': cls, 'nav': nav or title})
    page_titles.append(nav or title)

# ============================================================
#  CSS
# ============================================================
CSS = r"""
:root{
  --blue:#002FA7; --blue2:#1E5BFF; --cyan:#00D4FF; --ink:#0a0a0a; --ink2:#3a3a3c;
  --grey:#6e6e73; --tint:#f5f5f7; --line:rgba(0,47,167,.14); --line2:rgba(0,0,0,.07);
  --fnt:"Microsoft YaHei","PingFang SC","Helvetica Neue",Arial,sans-serif;
  --logo:url('data:image/png;base64,LOGO_B64');
  --t-hero:min(5.4vw,8.2vh); --t-sec:min(3.9vw,6.0vh); --t-h2:min(2.5vw,3.9vh);
  --t-h3:min(1.55vw,2.4vh); --t-sub:min(1.45vw,2.25vh); --t-body:min(1.16vw,1.82vh);
  --t-note:min(.96vw,1.5vh); --t-label:min(.86vw,1.35vh);
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%;background:#e9ecf2;font-family:var(--fnt);color:var(--ink)}
body{overflow:hidden}
.deck{position:relative;width:100vw;height:100vh;overflow:hidden}

/* ---------- 幻灯片 ---------- */
.slide{
  position:absolute;inset:0;background:#fff;
  padding:6.2vh 5.6vw 5vh;display:none;flex-direction:column;
  overflow:hidden;
}
.slide.on{display:flex;animation:fadeIn .42s ease-out}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ---------- 顶栏（宝锐规范：白顶栏 + Logo + BIORI BIOTECH） ---------- */
.topbar{
  position:absolute;top:0;left:0;right:0;height:5.4vh;min-height:38px;z-index:60;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 2.4vw;background:rgba(255,255,255,.94);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--line);
}
.tb-left{display:flex;align-items:center;gap:.62vw}
.tb-logo{
  width:7.2vh;height:2.44vh;min-width:64px;min-height:22px;
  background:var(--logo) left center/contain no-repeat;
  flex:0 0 auto;
}
.tb-name{font-size:1.42vh;font-weight:700;color:var(--blue);letter-spacing:.4px}
.tb-en{font-size:1.02vh;color:var(--grey);letter-spacing:1.5px}
.tb-slogan{font-size:1.0vh;color:var(--grey);letter-spacing:1.2px;font-weight:600}
.tb-slogan b{color:var(--blue);font-weight:700}
.tb-mid{font-size:1.16vh;color:var(--ink2);font-weight:600}
.tb-mid em{font-style:normal;color:var(--blue)}
.tb-right{font-size:1.02vh;color:var(--grey);letter-spacing:.5px}

/* ---------- 标题体系 ---------- */
.kicker{font-size:var(--t-label);font-weight:700;letter-spacing:2.4px;color:var(--blue);text-transform:uppercase;margin-bottom:1.1vh}
h1.hero{font-size:var(--t-hero);font-weight:800;line-height:1.14;letter-spacing:-.8px}
h2.sec{font-size:var(--t-sec);font-weight:800;line-height:1.16;letter-spacing:-.6px}
h3.h3{font-size:var(--t-h2);font-weight:800;line-height:1.24;letter-spacing:-.3px}
.sub{font-size:var(--t-h3);font-weight:600;color:var(--ink2);line-height:1.42;margin-top:.9vh}
.rule{width:4.6vw;height:3px;background:var(--blue);border-radius:2px;margin:1.5vh 0 2.2vh}
.rule.cy{background:linear-gradient(90deg,var(--blue),var(--cyan))}
.lead{font-size:var(--t-body);color:var(--ink2);line-height:1.92}

/* ---------- 布局 ---------- */
.grow{flex:1;min-height:0;display:flex;flex-direction:column}
.row{display:flex;gap:2.2vw}
.col{flex:1;min-width:0}
.card{
  background:#fff;border:1px solid var(--line);border-radius:12px;
  padding:2.1vh 1.5vw;box-shadow:0 2px 16px rgba(0,47,167,.045);
}
.card.blue{background:linear-gradient(160deg,rgba(0,47,167,.035),rgba(0,212,255,.02))}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:1.5vw}
.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:1.3vw}
.grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:1.1vw}

/* ---------- 列表 / 项目符号 ---------- */
.ul{list-style:none;display:flex;flex-direction:column;gap:1.15vh}
.ul li{position:relative;padding-left:1.5vw;font-size:var(--t-body);line-height:1.8;color:var(--ink2)}
.ul li::before{content:'';position:absolute;left:.32vw;top:.78em;width:.42vw;height:.42vw;min-width:4px;min-height:4px;border-radius:50%;background:var(--blue)}
.ul.num{counter-reset:n}
.ul.num li::before{counter-increment:n;content:counter(n);background:none;color:var(--blue);font-weight:800;font-size:var(--t-note);width:auto;height:auto;top:0;left:0}

/* ---------- 数据表 ---------- */
table.dt{width:100%;border-collapse:collapse;font-size:var(--t-note)}
table.dt th{
  background:rgba(0,47,167,.055);color:var(--blue);font-weight:700;
  text-align:left;padding:1.05vh .68vw;border-bottom:1.5px solid var(--line);
  font-size:var(--t-label);letter-spacing:.4px;white-space:nowrap;
}
table.dt td{padding:1.0vh .68vw;border-bottom:1px solid var(--line2);color:var(--ink2);line-height:1.6;vertical-align:top}
table.dt tr:last-child td{border-bottom:none}
table.dt td.k{font-weight:700;color:var(--ink);white-space:nowrap}
table.dt td.v{color:var(--ink2)}
table.dt td.src{color:var(--grey);font-size:calc(var(--t-label) * .94);white-space:nowrap}
table.dt tr.hl td{background:rgba(0,212,255,.06)}
table.dt td.num{font-variant-numeric:tabular-nums;text-align:right;font-weight:600}

/* ---------- 图表（纯 CSS/SVG 柱状） ---------- */
.chart{display:flex;align-items:flex-end;gap:1.1vw;height:30vh;padding:0 .4vw;border-bottom:1.5px solid var(--line);position:relative}
.bargrp{flex:1;display:flex;align-items:flex-end;justify-content:center;gap:.22vw;height:100%;position:relative}
.bar{
  width:100%;max-width:1.5vw;border-radius:4px 4px 0 0;
  background:linear-gradient(180deg,var(--blue2),var(--blue));
  transform-origin:bottom;animation:growBar .9s cubic-bezier(.2,.8,.3,1) both;
  position:relative;
}
.bar.c{background:linear-gradient(180deg,var(--cyan),#0086a8)}
.bar.g{background:linear-gradient(180deg,#9aa4b2,#6e6e73)}
.bar.dim{opacity:.42}
@keyframes growBar{from{transform:scaleY(0)}to{transform:scaleY(1)}}
.blabel{position:absolute;bottom:-3.1vh;left:50%;transform:translateX(-50%);font-size:var(--t-label);color:var(--grey);white-space:nowrap;font-weight:600}
.bval{position:absolute;top:-2.15vh;left:50%;transform:translateX(-50%);font-size:var(--t-label);color:var(--blue);font-weight:700;white-space:nowrap}
.legend{display:flex;gap:1.5vw;flex-wrap:wrap;margin-top:4.4vh;font-size:var(--t-label);color:var(--ink2)}
.legend span{display:flex;align-items:center;gap:.34vw;font-weight:600}
.legend i{width:.78vw;height:.78vw;min-width:9px;min-height:9px;border-radius:3px;display:inline-block}

/* ---------- 角标 / 标签 ---------- */
.pill{
  display:inline-block;padding:.45vh .72vw;border-radius:20px;font-size:var(--t-label);
  background:rgba(0,47,167,.07);color:var(--blue);font-weight:700;letter-spacing:.6px;
}
.pill.cy{background:rgba(0,212,255,.13);color:#007d9c}
.pill.g{background:rgba(0,160,90,.1);color:#0a7f3f}
.src-tag{font-size:calc(var(--t-label) * .92);color:var(--grey);font-weight:600;letter-spacing:.3px}

/* ---------- 承接页（方案 D） ---------- */
.slide.stage{padding:0}
.slide.stage .canvasHost{position:absolute;inset:0;z-index:1}
.slide.stage .stageTxt{
  position:relative;z-index:3;flex:1;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;padding:8vh 6vw;
}
.slide.stage h2{font-size:var(--t-hero);font-weight:800;color:var(--blue);letter-spacing:-.6px;text-shadow:0 2px 26px rgba(255,255,255,.95)}
.slide.stage .st-sub{margin-top:1.9vh;font-size:var(--t-h3);color:var(--ink2);letter-spacing:.8px;font-weight:600;max-width:60vw;line-height:1.7}
.slide.stage .u{width:5.4vw;height:3px;background:var(--blue);margin:2.4vh auto 0;border-radius:2px}
.slide.stage .st-num{
  position:absolute;top:9vh;left:6vw;font-size:min(13vw,19vh);font-weight:800;
  color:rgba(0,47,167,.055);line-height:1;z-index:2;letter-spacing:-2px;
}

/* ---------- 封面 ---------- */
.cover{position:absolute;inset:0;z-index:3;display:flex;flex-direction:column;justify-content:center;padding:0 7vw}
.cover .logoRow{display:flex;align-items:center;gap:1.5vw;margin-bottom:3.4vh}
.cover .logoRow .tb-logo{width:20vh;height:6.8vh;min-width:180px;min-height:60px}
.cover .logoRow .cslogan{font-size:min(1.5vw,2.3vh);font-weight:700;color:var(--blue);letter-spacing:2.2px}
.cover .logoRow .cslogan b{color:var(--cyan);font-weight:800}
.cover h1{font-size:min(6.2vw,9.4vh);font-weight:800;line-height:1.1;letter-spacing:-1.4px}
.cover h1 em{font-style:normal;background:linear-gradient(96deg,var(--blue),var(--cyan));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.cover .csub{margin-top:2.6vh;font-size:min(1.75vw,2.7vh);color:var(--ink2);font-weight:600;letter-spacing:.6px}
.cover .cline{width:6.4vw;height:3px;background:linear-gradient(90deg,var(--blue),var(--cyan));margin:3vh 0 2.6vh;border-radius:2px}
.cover .cmeta{font-size:var(--t-body);color:var(--grey);line-height:2}
.cover .chips{display:flex;gap:.7vw;flex-wrap:wrap;margin-top:2.2vh}
.cover .chips span{padding:.55vh .85vw;border:1px solid var(--line);border-radius:20px;font-size:var(--t-label);color:var(--blue);font-weight:700}

/* ---------- 7 场景总览 ---------- */
.scGrid{display:grid;grid-template-columns:repeat(4,1fr);gap:1.05vw;margin-top:.6vh}
.scCard{
  background:#fff;border:1px solid var(--line);border-radius:11px;padding:1.7vh 1.0vw;
  position:relative;overflow:hidden;transition:.22s;
}
.scCard:hover{transform:translateY(-3px);box-shadow:0 8px 26px rgba(0,47,167,.13);border-color:rgba(0,47,167,.34)}
.scCard .no{font-size:var(--t-note);font-weight:800;color:var(--cyan);letter-spacing:1px}
.scCard .nm{font-size:var(--t-h3);font-weight:800;color:var(--ink);margin-top:.5vh;line-height:1.3}
.scCard .ds{font-size:var(--t-label);color:var(--grey);margin-top:.62vh;line-height:1.62}
.scCard::after{content:'';position:absolute;left:0;top:0;width:3px;height:100%;background:linear-gradient(180deg,var(--blue),var(--cyan))}

/* ---------- 场景页编号 ---------- */
.scHead{display:flex;align-items:center;gap:.9vw;margin-bottom:1.5vh}
.scHead .big{font-size:min(4.4vw,6.6vh);font-weight:800;color:rgba(0,47,167,.13);line-height:1;letter-spacing:-1px}
.scHead .nm{font-size:var(--t-sec);font-weight:800;letter-spacing:-.5px}
.kv{display:flex;flex-direction:column;gap:0}
.kv .kvrow{display:flex;gap:1.1vw;padding:1.1vh 0;border-bottom:1px solid var(--line2)}
.kv .kvrow:last-child{border-bottom:none}
.kv .k{width:9.6vw;flex:0 0 9.6vw;font-weight:700;color:var(--ink);font-size:var(--t-note);line-height:1.6}
.kv .v{flex:1;color:var(--ink2);font-size:var(--t-note);line-height:1.72}
.kv .s{flex:0 0 auto;color:var(--grey);font-size:calc(var(--t-label)*.94);white-space:nowrap;padding-top:.18em}

/* ---------- 时间线（方案 A 流程） ---------- */
.tl{position:relative;flex:1;display:flex;align-items:center;min-height:0}
.tl svg{width:100%;height:100%;overflow:visible}
.flowline{fill:none;stroke:var(--blue);stroke-width:2;stroke-dasharray:9 11;animation:flowA 2.6s linear infinite}
.flowline.c2{stroke:var(--cyan);animation-duration:3.2s}
@keyframes flowA{to{stroke-dashoffset:-200}}
.tlnode{fill:#fff;stroke:var(--blue);stroke-width:2.4}
.tlnode.hi{fill:var(--blue);stroke:var(--cyan)}
.tlY{font-size:1.42vh;font-weight:800;fill:var(--blue);font-family:var(--fnt)}
.tlT{font-size:1.06vh;fill:var(--grey);font-family:var(--fnt)}

/* ---------- 页脚 / 导航 ---------- */
.footbar{
  position:absolute;bottom:0;left:0;right:0;height:4.4vh;min-height:30px;z-index:60;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 2.4vw;font-size:var(--t-label);color:var(--grey);
  background:linear-gradient(180deg,rgba(255,255,255,0),rgba(255,255,255,.9));
}
.footbar .fm{color:var(--ink2);font-weight:600}
.footbar .fm em{font-style:normal;color:var(--blue);font-weight:800}
.footbar .fsl{color:var(--blue);font-weight:700;letter-spacing:1.4px}
.pgnum{font-variant-numeric:tabular-nums;font-weight:700;color:var(--blue)}
.navbtn{
  position:absolute;top:50%;transform:translateY(-50%);z-index:70;
  width:3.1vh;height:3.1vh;min-width:26px;min-height:26px;border-radius:50%;border:1px solid var(--line);
  background:rgba(255,255,255,.9);color:var(--blue);cursor:pointer;
  display:flex;align-items:center;justify-content:center;font-size:1.5vh;font-weight:800;
  opacity:0;transition:.22s;
}
.slide:hover ~ .navbtn, .deck:hover .navbtn{opacity:.72}
.navbtn:hover{opacity:1;background:var(--blue);color:#fff}
.navbtn.prev{left:1.1vw}
.navbtn.next{right:1.1vw}
.progress{position:absolute;bottom:0;left:0;height:2px;background:linear-gradient(90deg,var(--blue),var(--cyan));z-index:80;transition:width .3s}

/* ---------- 缩略导航（按 N 呼出） ---------- */
.toc{
  position:fixed;inset:0;z-index:200;background:rgba(10,16,40,.93);backdrop-filter:blur(10px);
  display:none;padding:6vh 6vw;overflow-y:auto;
}
.toc.on{display:block}
.toc h3{color:#fff;font-size:2.1vh;margin-bottom:2.6vh;font-weight:800;letter-spacing:.6px}
.toc .tgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(15vw,1fr));gap:1.1vh}
.toc a{
  display:block;color:rgba(255,255,255,.76);text-decoration:none;font-size:1.32vh;
  padding:1.05vh .8vw;border-radius:7px;border:1px solid rgba(255,255,255,.13);
  line-height:1.45;transition:.16s;
}
.toc a:hover{background:rgba(0,212,255,.16);color:#fff;border-color:rgba(0,212,255,.5)}
.toc a b{color:var(--cyan);font-weight:800;margin-right:.4em}

/* ---------- 编辑提示 ---------- */
[contenteditable]:focus{outline:2px solid rgba(0,212,255,.75);outline-offset:2px;border-radius:3px}
.editHint{
  position:fixed;bottom:5vh;right:1.4vw;z-index:90;
  background:rgba(0,47,167,.9);color:#fff;font-size:1.12vh;padding:.75vh .9vw;
  border-radius:7px;letter-spacing:.4px;opacity:0;transition:.3s;pointer-events:none;
}
.editHint.on{opacity:1}

@media print{
  body{overflow:visible;background:#fff}
  .deck{height:auto;overflow:visible}
  .slide{position:relative;display:flex !important;page-break-after:always;height:100vh}
  .navbtn,.toc,.editHint,.progress{display:none !important}
}
"""

# ============================================================
#  JS（4 套动画 + 翻页 + 导航）
# ============================================================
JS = r"""
/* ============ 翻页 ============ */
var CUR = 0, TOT = document.querySelectorAll('.slide').length;
function show(i){
  if(i<0||i>=TOT) return;
  document.querySelectorAll('.slide')[CUR].classList.remove('on');
  CUR = i;
  var s = document.querySelectorAll('.slide')[CUR];
  s.classList.add('on');
  var pg = s.querySelector('.pgnum');
  if(pg) pg.textContent = (CUR+1) + ' / ' + TOT;
  document.getElementById('prog').style.width = ((CUR+1)/TOT*100) + '%';
  // ★ 修复：display:none 时 Canvas 尺寸为 0，翻页后必须重设尺寸并重绘
  s.querySelectorAll('canvas').forEach(function(cv){
    if(cv.__molResize){ cv.__molResize(); }
  });
  // 重放入场动画
  s.querySelectorAll('.bar').forEach(function(b,i2){
    b.style.animation='none'; void b.offsetWidth;
    b.style.animation='growBar .9s cubic-bezier(.2,.8,.3,1) both';
    b.style.animationDelay = (0.06*i2)+'s';
  });
  s.querySelectorAll('.growPath').forEach(function(p){
    var L=p.getTotalLength();
    p.style.strokeDasharray=L; p.style.strokeDashoffset=L;
    p.style.transition='none'; void p.getBBox();
    setTimeout(function(){ p.style.transition='stroke-dashoffset 2.4s ease-in-out'; p.style.strokeDashoffset=0; },60);
  });
}
document.addEventListener('keydown', function(e){
  if(e.key==='ArrowRight'||e.key==='PageDown'||e.key===' ')  { e.preventDefault(); show(CUR+1); }
  if(e.key==='ArrowLeft' ||e.key==='PageUp')                  { e.preventDefault(); show(CUR-1); }
  if(e.key==='Home'){ show(0); }
  if(e.key==='End'){ show(TOT-1); }
  if(e.key==='n'||e.key==='N'){ document.getElementById('toc').classList.toggle('on'); }
  if(e.key==='Escape'){ document.getElementById('toc').classList.remove('on'); }
});
document.getElementById('prevBtn').onclick=function(){ show(CUR-1); };
document.getElementById('nextBtn').onclick=function(){ show(CUR+1); };
document.getElementById('toc').onclick=function(e){ if(e.target===this) this.classList.remove('on'); };

/* 触屏滑动 */
(function(){
  var x0=null;
  document.addEventListener('touchstart',function(e){ x0=e.touches[0].clientX; },{passive:true});
  document.addEventListener('touchend',function(e){
    if(x0===null) return;
    var dx=e.changedTouches[0].clientX-x0;
    if(Math.abs(dx)>52){ show(dx<0?CUR+1:CUR-1); }
    x0=null;
  },{passive:true});
})();

/* ============ 方案 A/B/D：Canvas 分子粒子网络 ============ */
function molNet(cv, opt){
  opt = opt || {};
  var N = opt.n || 58, DLINK = opt.d || 132, ALPHA = opt.a || 0.30, SP = opt.sp || 0.42;
  var ctx = cv.getContext('2d'), W, H, pts = [], mouse={x:-999,y:-999}, RA = false;
  function resize(){
    var r = cv.parentElement.getBoundingClientRect(), dpr = window.devicePixelRatio||1;
    W = cv.width = Math.max(1, r.width*dpr); H = cv.height = Math.max(1, r.height*dpr);
    cv.style.width=r.width+'px'; cv.style.height=r.height+'px';
  }
  function init(){
    pts=[]; var dpr=window.devicePixelRatio||1;
    for(var i=0;i<N;i++) pts.push({
      x:Math.random()*W, y:Math.random()*H,
      vx:(Math.random()-.5)*SP*dpr, vy:(Math.random()-.5)*SP*dpr,
      r:(Math.random()*2.1+1.3)*dpr
    });
  }
  function draw(){
    if(!cv.offsetParent){ RA=false; return; }
    ctx.clearRect(0,0,W,H);
    var dpr=window.devicePixelRatio||1, D2=DLINK*dpr;
    for(var i=0;i<N;i++){
      var p=pts[i]; p.x+=p.vx; p.y+=p.vy;
      if(p.x<0||p.x>W) p.vx*=-1;
      if(p.y<0||p.y>H) p.vy*=-1;
      var dx=p.x-mouse.x*dpr, dy=p.y-mouse.y*dpr, md=Math.sqrt(dx*dx+dy*dy);
      if(md<130*dpr && md>0.1){ p.x-=dx/md*1.1; p.y-=dy/md*1.1; }
      for(var j=i+1;j<N;j++){
        var q=pts[j], ax=p.x-q.x, ay=p.y-q.y, d=Math.sqrt(ax*ax+ay*ay);
        if(d<D2){
          ctx.beginPath(); ctx.strokeStyle='rgba(0,47,167,'+(ALPHA*(1-d/D2))+')';
          ctx.lineWidth=1*dpr; ctx.moveTo(p.x,p.y); ctx.lineTo(q.x,q.y); ctx.stroke();
        }
      }
    }
    for(var k=0;k<N;k++){
      var pt=pts[k];
      ctx.beginPath();
      ctx.fillStyle = (k%4===0)?'rgba(0,212,255,.86)':'rgba(0,47,167,.62)';
      ctx.arc(pt.x,pt.y,pt.r,0,6.284); ctx.fill();
    }
    RA = requestAnimationFrame(draw);
  }
  cv.parentElement.addEventListener('mousemove',function(e){
    var r=cv.getBoundingClientRect(); mouse.x=e.clientX-r.left; mouse.y=e.clientY-r.top;
  });
  cv.parentElement.addEventListener('mouseleave',function(){ mouse.x=-999; mouse.y=-999; });
  resize(); init();
  // ★ 暴露 resize，供 show() 在翻页（display 由 none→flex）后重设尺寸
  cv.__molResize = function(){ resize(); init(); if(!RA){ RA=true; draw(); } };
  new IntersectionObserver(function(es){
    es.forEach(function(en){
      if(en.isIntersecting && !RA){ RA=true; draw(); }
      else if(!en.isIntersecting && RA){ cancelAnimationFrame(RA); RA=false; }
    });
  }).observe(cv);
  window.addEventListener('resize',function(){ resize(); init(); });
}

/* ============ 方案 C：DNA 双螺旋入场生长 ============ */
function helix(root){
  var svg = root.querySelector('svg');
  if(!svg || svg.dataset.done) return;
  svg.dataset.done='1';
  var VW=1000, VH=340, NS='http://www.w3.org/2000/svg';
  var mid=VH/2, amp=(root.dataset.amp?+root.dataset.amp:96), turns=(root.dataset.turns?+root.dataset.turns:3.1);
  var p1=[],p2=[],rungs=[];
  for(var x=0;x<=VW;x+=4){
    var t=x/VW*Math.PI*2*turns;
    var y1=mid-Math.sin(t)*amp, y2=mid+Math.sin(t)*amp;
    p1.push([x,y1]); p2.push([x,y2]);
    if(x%42===0) rungs.push([x,y1,y2]);
  }
  function toPath(p){
    var d='M'+p[0][0].toFixed(1)+','+p[0][1].toFixed(1);
    for(var i=1;i<p.length;i++) d+=' L'+p[i][0].toFixed(1)+','+p[i][1].toFixed(1);
    return d;
  }
  rungs.forEach(function(r){
    var ln=document.createElementNS(NS,'line');
    ln.setAttribute('x1',r[0]); ln.setAttribute('x2',r[0]);
    ln.setAttribute('y1',r[1].toFixed(1)); ln.setAttribute('y2',r[2].toFixed(1));
    ln.setAttribute('stroke','#1E5BFF'); ln.setAttribute('stroke-width','2');
    ln.setAttribute('opacity','.34');
    svg.appendChild(ln);
  });
  [[p1,'#002FA7'],[p2,'#00D4FF']].forEach(function(pair,idx){
    var pa=document.createElementNS(NS,'path');
    pa.setAttribute('d',toPath(pair[0]));
    pa.setAttribute('fill','none'); pa.setAttribute('stroke',pair[1]);
    pa.setAttribute('stroke-width','3.4'); pa.setAttribute('stroke-linecap','round');
    pa.setAttribute('class','growPath');
    pa.setAttribute('opacity', idx?'0.85':'1');
    svg.appendChild(pa);
    var L=pa.getTotalLength();
    pa.style.strokeDasharray=L; pa.style.strokeDashoffset=L;
    setTimeout(function(){
      pa.style.transition='stroke-dashoffset 2.6s ease-in-out';
      pa.style.strokeDashoffset=0;
    },120+idx*260);
  });
}

/* ============ 初始化 ============ */
document.addEventListener('DOMContentLoaded', function(){
  // 分子网络：任何带 data-mol 的 host
  document.querySelectorAll('[data-mol]').forEach(function(h){
    var cv=document.createElement('canvas');
    h.appendChild(cv);
    molNet(cv, { n:+h.dataset.n||58, d:+h.dataset.d||132, a:+h.dataset.a||0.30, sp:+h.dataset.sp||0.42 });
  });
  // 双螺旋
  document.querySelectorAll('[data-helix]').forEach(helix);
  show(0);
});
"""

# ============================================================
#  页面构造工具
# ============================================================
LOGO_B64 = ''
try:
    import base64
    lp = os.path.expanduser('~/work/公司资料/logo_transparent.png')
    if os.path.exists(lp):
        LOGO_B64 = base64.b64encode(open(lp,'rb').read()).decode()
except Exception:
    pass

def topbar(mid=''):
    return f"""<div class="topbar">
  <div class="tb-left"><div class="tb-logo" role="img" aria-label="宝锐生物 BIORI BIOTECH"></div><span class="tb-slogan" contenteditable="true">守护生命健康<b>·</b>缔造美好生活</span></div>
  <div class="tb-mid">{mid}</div>
  <div class="tb-right">诊断原料 · 客户宣讲</div>
</div>"""

def footbar(n):
    return f"""<div class="footbar"><span class="fm">珠海宝锐生物科技有限公司 · <em>把体系交给我们，把时间留给您的团队</em></span><span class="fsl" contenteditable="true">守护生命健康 · 缔造美好生活</span><span class="pgnum">{n}</span></div>"""

def ul(items):
    return '<ul class="ul">' + ''.join(f'<li contenteditable="true">{esc(x)}</li>' for x in items) + '</ul>'

def kv(items, with_src=True):
    rows = []
    for it in items:
        s = f'<span class="s">{esc(it["src"])}</span>' if with_src and it.get('src') else ''
        rows.append(f'<div class="kvrow"><span class="k" contenteditable="true">{esc(it["k"])}</span><span class="v" contenteditable="true">{esc(it["v"])}</span>{s}</div>')
    return '<div class="kv">' + ''.join(rows) + '</div>'

def bartable(chart, h='30vh', hl_last=False):
    """纯 CSS 柱状图（div 高度百分比）"""
    series = chart['series']; labels = chart['labels']
    mx = max(v for s in series for v in s['vals']) or 1
    groups = []
    for gi, lab in enumerate(labels):
        bars = []
        for si, s in enumerate(series):
            v = s['vals'][gi]
            ph = v / mx * 88
            cls = 'bar c' if si == 0 else ('bar' if si < len(series)-1 else 'bar g')
            bars.append(f'<div class="{cls}" style="height:{ph:.2f}%" title="{esc(s["name"])}: {v}"></div>')
        groups.append(f'<div class="bargrp">{"".join(bars)}<span class="blabel" contenteditable="true">{esc(lab)}</span></div>')
    lg = ''.join(f'<span><i style="background:{["#002FA7","#00D4FF","#9aa4b2","#1E5BFF","#6e6e73"][i%5]}"></i>{esc(s["name"])}</span>' for i, s in enumerate(series))
    return f'<div class="chart" style="height:{h}">{"".join(groups)}</div><div class="legend">{lg}</div>'

def dtable(rows, headers):
    th = ''.join(f'<th>{esc(h)}</th>' for h in headers)
    trs = []
    for r in rows:
        tds = ''.join(f'<td class="k" contenteditable="true">{esc(c)}</td>' if i == 0 else f'<td contenteditable="true">{esc(c)}</td>' for i, c in enumerate(r))
        trs.append(f'<tr>{tds}</tr>')
    return f'<table class="dt"><thead><tr>{th}</tr></thead><tbody>{"".join(trs)}</tbody></table>'

def card(title, body, cls=''):
    return f'<div class="card {cls}"><div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">{esc(title)}</div>{body}</div>'

def stage_page(num, h2, sub, mol_opt='data-mol data-n="52" data-d="150" data-a="0.16"'):
    return f"""<div class="canvasHost" {mol_opt}></div>
<div class="st-num">{num}</div>
<div class="stageTxt">
  <h2 contenteditable="true">{esc(h2)}</h2>
  <div class="st-sub" contenteditable="true">{esc(sub)}</div>
  <div class="u"></div>
</div>"""

def scene_head(no, name, kicker=''):
    return f"""<div class="scHead"><span class="big">{no}</span><span class="nm" contenteditable="true">{esc(name)}</span></div>
{f'<div class="kicker" contenteditable="true">{esc(kicker)}</div>' if kicker else ''}"""

# ============================================================
#  P1  封面（方案 D）
# ============================================================
add('封面', f"""<div class="canvasHost" data-mol data-n="64" data-d="145" data-a="0.13"></div>
<div class="cover">
  <div class="logoRow">
    <div class="tb-logo" role="img" aria-label="宝锐生物 BIORI BIOTECH"></div>
    <span class="cslogan" contenteditable="true">守护生命健康<b>·</b>缔造美好生活</span>
  </div>
  <h1 contenteditable="true">诊断原料整体解决方案<br><em>从原料到体系的深度协同</em></h1>
  <div class="csub" contenteditable="true">分子诊断 · 七大热点应用场景 · 数据说话</div>
  <div class="cline"></div>
  <div class="cmeta" contenteditable="true">珠海宝锐生物科技有限公司 ｜ 2026</div>
  <div class="chips">
    <span contenteditable="true">2012 年成立</span>
    <span contenteditable="true">国家高新技术企业</span>
    <span contenteditable="true">专精特新企业</span>
    <span contenteditable="true">省分子诊断原料酶工程技术研究中心</span>
  </div>
</div>""", cls='stage')

# ============================================================
#  P2  今天只讲三件事
# ============================================================
add('议题', f"""{topbar('本次交流')}
<div class="grow" style="justify-content:center">
  <div class="kicker" contenteditable="true">AGENDA · 三件事</div>
  <h2 class="sec" contenteditable="true">今天只讲三件事</h2>
  <div class="rule"></div>
  <div class="grid3" style="margin-top:2.6vh">
    <div class="card blue">
      <div style="font-size:min(3.4vw,5vh);font-weight:800;color:var(--blue);line-height:1">01</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .8vh" contenteditable="true">行业在往哪走</div>
      <div class="lead" contenteditable="true">快速化、冻干化、NA 控制、多重直扩、出海合规 —— 五个趋势正在重排原料选型标准。</div>
    </div>
    <div class="card blue">
      <div style="font-size:min(3.4vw,5vh);font-weight:800;color:var(--blue);line-height:1">02</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .8vh" contenteditable="true">为什么要深度协同</div>
      <div class="lead" contenteditable="true">原料不是一次性买卖。联合开发、工艺共建、放大量产，才能把交期和成本真正压下来。</div>
    </div>
    <div class="card blue">
      <div style="font-size:min(3.4vw,5vh);font-weight:800;color:var(--blue);line-height:1">03</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .8vh" contenteditable="true">七个场景怎么落地</div>
      <div class="lead" contenteditable="true">每个场景先讲你的需求与卡点，再给宝锐的实测数据 —— 能不能用，数据说话。</div>
    </div>
  </div>
</div>
{footbar(2)}""")

# ============================================================
#  P3  目录（方案 A 流程）
# ============================================================
toc_items = [('一','行业痛点与趋势','P4–P10 · 5 个趋势 + 三大痛点'),
             ('二','产业链协同 · 深度合作','P11–P16 · 从买原料到共建体系'),
             ('三','宝锐能力与成功案例','P17–P25 · 十余年积累'),
             ('四','七大热点应用场景','P26–P39 · 需求分析 + 宝锐数据')]
add('目录', f"""{topbar('目录')}
<div class="grow" style="justify-content:center">
  <div class="kicker" contenteditable="true">CONTENTS</div>
  <h2 class="sec" contenteditable="true">目录</h2>
  <div class="rule cy"></div>
  <div style="display:flex;flex-direction:column;gap:1.35vh;margin-top:1.4vh">
    {''.join(f'''<div style="display:flex;align-items:center;gap:1.5vw;padding:1.55vh 1.3vw;border:1px solid var(--line);border-radius:11px;background:linear-gradient(90deg,rgba(0,47,167,.028),transparent)">
      <span style="font-size:min(2.2vw,3.3vh);font-weight:800;color:var(--blue);min-width:3.4vw" contenteditable="true">{n}</span>
      <div style="flex:1"><div style="font-size:var(--t-h3);font-weight:800" contenteditable="true">{t}</div>
      <div class="src-tag" contenteditable="true">{d}</div></div>
      <span style="width:2.6vw;height:2px;background:linear-gradient(90deg,var(--blue),var(--cyan));border-radius:2px"></span>
    </div>''' for n,t,d in toc_items)}
  </div>
</div>
{footbar(3)}""")

# ============================================================
#  第一部分 · 行业痛点与趋势（P4–P10）
# ============================================================
add('一 · 承接页', stage_page('01', '行业正在发生什么', '五个趋势 + 三个最痛的卡点'),
    cls='stage', nav='一 · 行业痛点与趋势 · 承接页')

ind = DATA['industry']
add('趋势大盘', f"""{topbar('行业趋势')}
<div class="grow">
  <div class="kicker" contenteditable="true">INDUSTRY · 趋势大盘</div>
  <h3 class="h3" contenteditable="true">五个趋势，正在重排原料选型标准</h3>
  <div class="rule"></div>
  <div class="grid2" style="margin-top:.8vh">
    <div>{dtable([[t['no']+' '+t['t'], t['d']] for t in ind['trends']], ['趋势','对原料的要求'])}</div>
    <div class="card blue">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.3vh" contenteditable="true">三个最痛的卡点</div>
      {ul([p['t']+'：'+p['d'] for p in ind['painpoints'][:3]])}
      <div style="margin-top:1.6vh;padding-top:1.4vh;border-top:1px dashed var(--line)">
        <span class="pill cy" contenteditable="true">这三个卡点，本质都是原料问题</span>
      </div>
    </div>
  </div>
</div>
{footbar(5)}""", nav='一 · 5 个趋势 + 三大痛点')

# --- P6 三大核心痛点（汇聚式） ---
add('三大痛点', f"""{topbar('行业痛点')}
<div class="grow">
  <div class="kicker" contenteditable="true">PAIN POINTS</div>
  <h3 class="h3" contenteditable="true">你的产线，正在被这三件事拖住</h3>
  <div class="rule"></div>
  <div class="grid3" style="margin-top:1vh">
    {''.join(f'''<div class="card">
      <div style="width:2.9vh;height:2.9vh;min-width:26px;min-height:26px;border-radius:8px;background:linear-gradient(135deg,var(--blue),var(--blue2));display:flex;align-items:center;justify-content:center;color:#fff;font-weight:800;font-size:1.42vh;margin-bottom:1.3vh">{i+1}</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:.8vh" contenteditable="true">{p['t']}</div>
      <div class="lead" contenteditable="true">{p['d']}</div>
    </div>''' for i,p in enumerate(ind['painpoints'] if len(ind['painpoints'])==3 else ind['painpoints'][:3]))}
  </div>
</div>
{footbar(6)}""", nav='一 · 三大核心痛点')

# --- P7–P10 趋势详解（4 页） ---
trend_pages = [
 ('趋势① 快速化', '30 分钟内出结果，卡点在酶的催化效率',
  ['急诊、发热门诊、POCT 场景要求 30 min 内完成扩增检测',
   '提速会牺牲灵敏度与特异性 → 需要酶本征催化效率足够高',
   '快速程序对酶耐热性与热启动封闭效果要求更严'],
  [('扩增速率','不低于 1 kb/10s，最快 30 min 内完成检测','PPT2 P22'),
   ('RT 快速','qRT-PCR 检测用时低至 20 min','PPT2 P21'),
   ('催化效率','Rob TaqQS 3.0 的 Kcat/Km = 5.191，为竞品1 的 3.61 倍','PPT1 P25')]),
 ('趋势② 冻干化 + 全预混', '免冷链、免分装，让试剂走到基层与海外',
  ['冷链成本高、断链风险大，基层与出海市场难以承受',
   '冻干后需保持活性与复溶性，保护剂配方是关键',
   '量产放大与批间稳定性，决定能否真正替代进口'],
  [('常温稳定性','产品已完成常温放置 18 个月稳定性验证','PPT2 冻干平台'),
   ('全预混稳定性','全预混 37℃ 7 天稳定；抗吸湿冻干保护剂','PPT2 新品页'),
   ('获批成果','冻干型超灵敏/高灵敏直扩试剂、全血直扩分型试剂','PPT2 P11')]),
 ('趋势③ NA 控制 + 复杂样本直扩', '准入门槛 + 免提取，两个方向同时加压',
  ['重组酶宿主核酸残留（E.coli 基因组）直接带来假阳性',
   '全血/血浆/拭子/粪便免提取直扩，对抗抑制能力要求极高',
   'UNG/dUTP 防污染体系配合，全流程才能可控'],
  [('宿主残留','低至约 1×10⁻³ copies/U','PPT2 P31'),
   ('致病菌检测','NTC 47 复管阴性，8 种常见条件致病菌均未检出','PPT2 P31'),
   ('直扩加样','全血 ≤5% ｜ 血浆、血清 ≤30%','PPT2 P22')]),
 ('趋势④ 多重检测 + 出海合规 + 国产替代', '一管多靶标、批间稳定、供应链安全',
  ['呼吸道多联检、耐药突变分型要求一管多靶标',
   '出海注册以批间稳定性为门票，换批即需重新验证',
   '集采与内卷下，原料国产化是成本唯一出路'],
  [('多重体系','MMCA 多重熔解曲线法；峰形好、高特异性','PPT2 P21'),
   ('批间稳定','工艺稳定技术先进，有效控制批间差异','PPT2 P19'),
   ('配套酶','温启动 Bst 酶、温启动逆转录酶（国内首款）','PPT2 P11')]),
]
for i,(ttl, stl, needs, datas) in enumerate(trend_pages):
    pno = 7+i
    add(ttl, f"""{topbar('行业趋势')}
<div class="grow">
  <div class="kicker" contenteditable="true">TREND {i+1} / 5</div>
  <h3 class="h3" contenteditable="true">{ttl}</h3>
  <div class="sub" contenteditable="true">{stl}</div>
  <div class="rule"></div>
  <div class="row" style="margin-top:.6vh">
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.3vh" contenteditable="true">客户端的真实需求</div>
      {ul(needs)}
    </div>
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.3vh" contenteditable="true">宝锐对应能力</div>
      {dtable([[k,v,s] for k,v,s in datas], ['能力','指标 / 事实','来源'])}
    </div>
  </div>
</div>
{footbar(pno)}""", nav=f'一 · 趋势{i+1}')

# ============================================================
#  第二部分 · 产业链协同（P11–P16）
# ============================================================
add('二 · 承接页', stage_page('02', '为什么要和我们深度协同', '从「买原料」到「共建体系」'),
    cls='stage', nav='二 · 产业链协同 · 承接页')

add('产业链位置', f"""{topbar('产业链协同')}
<div class="grow">
  <div class="kicker" contenteditable="true">VALUE CHAIN</div>
  <h3 class="h3" contenteditable="true">诊断原料在产业链上的位置</h3>
  <div class="rule cy"></div>
  <div class="tl" data-tl>
    <svg viewBox="0 0 1000 200" preserveAspectRatio="xMidYMid meet">
      <path class="flowline" d="M60,100 L250,100"/>
      <path class="flowline c2" d="M330,100 L520,100"/>
      <path class="flowline" d="M600,100 L790,100"/>
      <path class="flowline c2" d="M870,100 L960,100"/>
      <circle class="tlnode hi" cx="60"  cy="100" r="9"/><text class="tlY" x="60"  y="140" text-anchor="middle">上游</text><text class="tlT" x="60"  y="162" text-anchor="middle">酶 / 抗体 / 探针</text>
      <circle class="tlnode"    cx="290" cy="100" r="9"/><text class="tlY" x="290" y="140" text-anchor="middle">宝锐</text><text class="tlT" x="290" y="162" text-anchor="middle">原料智造 · 工艺开发</text>
      <circle class="tlnode"    cx="560" cy="100" r="9"/><text class="tlY" x="560" y="140" text-anchor="middle">中游</text><text class="tlT" x="560" y="162" text-anchor="middle">试剂盒 / 仪器厂商</text>
      <circle class="tlnode"    cx="830" cy="100" r="9"/><text class="tlY" x="830" y="140" text-anchor="middle">下游</text><text class="tlT" x="830" y="162" text-anchor="middle">医院 / ICL / 疾控 / 海关</text>
    </svg>
  </div>
  <div class="card blue" style="margin-top:1.4vh">
    <div class="lead" contenteditable="true"><b>我们不在你的下游，我们在你的上游。</b>原料的批间差、交期、放大量产能力，会沿着这条链一路放大到终端结果 —— 所以协同必须发生在研发早期，而不是采购环节。</div>
  </div>
</div>
{footbar(12)}""", nav='二 · 产业链位置')

# --- P13 三个真实困境 ---
add('三个困境', f"""{topbar('产业链协同')}
<div class="grow">
  <div class="kicker" contenteditable="true">PAIN · 采购视角</div>
  <h3 class="h3" contenteditable="true">换一个原料，为什么这么难</h3>
  <div class="rule"></div>
  <div class="grid3" style="margin-top:1vh">
    <div class="card"><div class="pill" contenteditable="true">困境 1</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .7vh" contenteditable="true">交期不可控</div>
      <div class="lead" contenteditable="true">进口原料周期长、断供风险高；国产切换又要重新验证，周期同样长。项目等不起。</div></div>
    <div class="card"><div class="pill" contenteditable="true">困境 2</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .7vh" contenteditable="true">定制化没人接</div>
      <div class="lead" contenteditable="true">特殊体系、特殊浓度、特殊冻干形态 —— 大厂不接小单，小厂做不了工艺。</div></div>
    <div class="card"><div class="pill" contenteditable="true">困境 3</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.1vh 0 .7vh" contenteditable="true">放大就失控</div>
      <div class="lead" contenteditable="true">实验室级别能用，一到 g 级/批的放大量产，批间差就压不住，注册材料要重做。</div></div>
  </div>
  <div class="card blue" style="margin-top:1.6vh">
    <div class="lead" contenteditable="true"><b>这三个困境的共同解法是同一个：把供应商变成研发伙伴。</b>越早介入，验证成本越低，量产越稳。</div>
  </div>
</div>
{footbar(13)}""", nav='二 · 三个真实困境')

# --- P14 协同模式 ---
add('协同模式', f"""{topbar('产业链协同')}
<div class="grow">
  <div class="kicker" contenteditable="true">CO-DEVELOPMENT</div>
  <h3 class="h3" contenteditable="true">宝锐的三种协同模式</h3>
  <div class="rule cy"></div>
  <div class="grid3" style="margin-top:1vh">
    <div class="card"><div style="font-size:var(--t-h3);font-weight:800;color:var(--blue)" contenteditable="true">① 联合开发</div>
      <div class="lead" style="margin-top:.9vh" contenteditable="true">从体系设计阶段介入，共同定义酶的性能指标（催化效率 / 抗抑制 / 耐热），而非在采购阶段比价。</div></div>
    <div class="card"><div style="font-size:var(--t-h3);font-weight:800;color:var(--blue)" contenteditable="true">② 定制化</div>
      <div class="lead" style="margin-top:.9vh" contenteditable="true">按客户体系定制浓度、缓冲体系、冻干形态与包装规格；支持从 mg 级到 g 级的梯度放大。</div></div>
    <div class="card"><div style="font-size:var(--t-h3);font-weight:800;color:var(--blue)" contenteditable="true">③ 工艺共建</div>
      <div class="lead" style="margin-top:.9vh" contenteditable="true">开放冻干服务平台：原料筛选验证 → 冻干工艺优化 → 冻干量产，三步走通。</div></div>
  </div>
  <div class="card" style="margin-top:1.5vh">
    <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">冻干服务平台 · 三步走通</div>
    {dtable([['第一步','原料筛选验证','确认原料可冻干性与配方兼容性'],
             ['第二步','冻干工艺优化','保护剂配方 · 冻干曲线 · 复溶性'],
             ['第三步','冻干量产','批间稳定性 · 常温储运验证']], ['阶段','内容','交付'])}
  </div>
</div>
{footbar(14)}""", nav='二 · 三种协同模式')

# --- P15 协同价值对照 ---
add('协同价值', f"""{topbar('产业链协同')}
<div class="grow">
  <div class="kicker" contenteditable="true">VALUE</div>
  <h3 class="h3" contenteditable="true">从「买原料」到「共建体系」，差在哪</h3>
  <div class="rule"></div>
  <div style="margin-top:.6vh">
    {dtable([
      ['关系定位','一次性买卖，比价采购','长期协同，共担指标'],
      ['介入时点','采购环节（体系已定型）','研发早期（体系可调）'],
      ['性能指标','按目录规格交付','按你的体系反向定制'],
      ['放大能力','mg 级样品','mg → g 级梯度放大'],
      ['批间差','换批需重新验证','工艺稳定，有效控制批间差异'],
      ['冻干','自行摸索或外委','冻干平台全流程支持'],
      ['风险','断供 / 涨价由你承担','供应安全 + 国产替代'],
    ], ['维度','传统采购','深度协同'])}
  </div>
  <div class="card blue" style="margin-top:1.5vh">
    <div class="lead" contenteditable="true"><b>结论：</b>深度协同省下的不是采购单价，而是<b>重复验证的时间</b>与<b>放大失败的代价</b> —— 这两项在试剂注册周期里往往比原料本身贵得多。</div>
  </div>
</div>
{footbar(15)}""", nav='二 · 协同价值对照')

# --- P16 合作方式 + 4 步走通 ---
add('合作方式', f"""{topbar('产业链协同')}
<div class="grow">
  <div class="kicker" contenteditable="true">HOW TO START</div>
  <h3 class="h3" contenteditable="true">怎么开始 —— 三种方式 + 四步走通</h3>
  <div class="rule cy"></div>
  <div class="row" style="margin-top:.6vh">
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">三种合作方式</div>
      <div style="display:flex;flex-direction:column;gap:1.2vh">
        <div class="card"><div style="font-weight:800;color:var(--blue);font-size:var(--t-note)" contenteditable="true">目录采购</div><div class="lead" style="margin-top:.5vh" contenteditable="true">标准货号直接下单，适合已验证体系的快速放量。</div></div>
        <div class="card"><div style="font-weight:800;color:var(--blue);font-size:var(--t-note)" contenteditable="true">定制开发</div><div class="lead" style="margin-top:.5vh" contenteditable="true">按体系定制指标与规格，签署联合开发协议。</div></div>
        <div class="card"><div style="font-weight:800;color:var(--blue);font-size:var(--t-note)" contenteditable="true">战略绑定</div><div class="lead" style="margin-top:.5vh" contenteditable="true">年度框架 + 供应保障 + 联合注册支持。</div></div>
      </div>
    </div>
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">四步走通</div>
      {dtable([['01','样品测试','免费提供试用样品与应用支持'],
               ['02','性能验证','按你的体系做平行比对，数据留档'],
               ['03','小试中试','mg → g 级放大，确认批间稳定性'],
               ['04','量产供货','签订供货协议，纳入年度供应保障']], ['步骤','阶段','内容'])}
    </div>
  </div>
</div>
{footbar(16)}""", nav='二 · 合作方式 + 4 步走通')

# ============================================================
#  第三部分 · 宝锐能力与成功案例（P17–P25）
# ============================================================
add('三 · 承接页', stage_page('03', '十余年，我们积累了什么', '研发平台 · 制造体系 · 冻干能力 · 服务经验'),
    cls='stage', nav='三 · 宝锐能力 · 承接页')

co = DATA['company']
add('公司概况', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">COMPANY</div>
  <h3 class="h3" contenteditable="true">珠海宝锐生物科技有限公司</h3>
  <div class="sub" contenteditable="true">自 2012 年成立以来，专注为生物科技领域的工业与科研用户提供卓越品质的生物原料与试剂解决方案</div>
  <div class="rule"></div>
  <div class="row" style="margin-top:.6vh">
    <div class="col">
      <div class="grid2">
        <div class="card blue"><div style="font-size:min(2.6vw,3.9vh);font-weight:800;color:var(--blue)">2012</div><div class="src-tag" contenteditable="true">公司成立</div></div>
        <div class="card blue"><div style="font-size:min(2.6vw,3.9vh);font-weight:800;color:var(--blue)">14 年</div><div class="src-tag" contenteditable="true">原料研发与客户服务</div></div>
        <div class="card blue"><div style="font-size:min(2.6vw,3.9vh);font-weight:800;color:var(--blue)">{esc(co['facility']['gmp'])}</div><div class="src-tag" contenteditable="true">GMP 车间（总计 {esc(co['facility']['total'])}）</div></div>
        <div class="card blue"><div style="font-size:min(2.6vw,3.9vh);font-weight:800;color:var(--blue)">3 大</div><div class="src-tag" contenteditable="true">业务板块：诊断原料 / 生命科学 / 生物医药</div></div>
      </div>
    </div>
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.1vh" contenteditable="true">资质与平台</div>
      {ul(co['tags'])}
      <div style="font-size:var(--t-h3);font-weight:800;margin:1.5vh 0 1.1vh" contenteditable="true">六大技术平台</div>
      <div style="display:flex;gap:.5vw;flex-wrap:wrap">
        {''.join(f'<span class="pill" contenteditable="true">{esc(p)}</span>' for p in co['platforms'])}
      </div>
    </div>
  </div>
</div>
{footbar(18)}""", nav='三 · 公司概况')

# --- P19 发展历程（方案 A 时间线动画） ---
milestones = [
  ('2012','公司成立'), ('2015','诊断原料业务线建立'), ('2018','省工程技术研究中心'),
  ('2020','冻干服务平台建成'), ('2022','专精特新企业'), ('2024','生物医药板块拓展'), ('2026','七大场景体系成型'),
]
add('发展历程', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">MILESTONES</div>
  <h3 class="h3" contenteditable="true">发展历程</h3>
  <div class="rule cy"></div>
  <div class="tl" style="flex:1;margin-top:1.2vh">
    <svg viewBox="0 0 1200 260" preserveAspectRatio="xMidYMid meet">
      <path class="flowline" d="M40,130 L1160,130"/>
      {''.join(f'''<circle class="tlnode{' hi' if i==0 or i==len(milestones)-1 else ''}" cx="{40+i*(1120//(len(milestones)-1))}" cy="130" r="8"/>
      <text class="tlY" x="{40+i*(1120//(len(milestones)-1))}" y="{72 if i%2==0 else 200}" text-anchor="middle">{y}</text>
      <text class="tlT" x="{40+i*(1120//(len(milestones)-1))}" y="{92 if i%2==0 else 220}" text-anchor="middle">{t}</text>''' for i,(y,t) in enumerate(milestones))}
    </svg>
  </div>
</div>
{footbar(19)}""", nav='三 · 发展历程')

# --- P20 荣誉与质量体系 ---
add('荣誉质量', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">QUALITY</div>
  <h3 class="h3" contenteditable="true">荣誉与质量体系</h3>
  <div class="rule"></div>
  <div class="grid2" style="margin-top:.6vh">
    <div class="card">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">企业资质</div>
      {ul(co['tags'])}
    </div>
    <div class="card">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.2vh" contenteditable="true">质量与工艺</div>
      {ul(['工艺稳定技术先进，有效控制批间差异（PPT2 P19）',
           '全流程质控，覆盖蛋白进化 → 重组表达 → 工艺开发 → 应用验证',
           '产品已完成常温放置 18 个月稳定性验证（冻干平台）',
           '外源核酸酶残留、外源核酸残留纳入酶性能表征项目（PPT1 P26 表征清单）'])}
    </div>
  </div>
</div>
{footbar(20)}""", nav='三 · 荣誉与质量')

# --- P21 制造与研究平台 ---
add('制造研发', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">CAPACITY</div>
  <h3 class="h3" contenteditable="true">制造 · 研发 · 冻干 · 服务</h3>
  <div class="rule cy"></div>
  <div class="grid4" style="margin-top:1vh">
    <div class="card"><div class="pill" contenteditable="true">制造</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">{esc(co['facility']['gmp'])}</div>
      <div class="led" style="font-size:var(--t-note);color:var(--grey);line-height:1.7" contenteditable="true">GMP 车间；公司总建筑面积 {esc(co['facility']['total'])}</div></div>
    <div class="card"><div class="pill" contenteditable="true">研发</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">6 大平台</div>
      <div style="font-size:var(--t-note);color:var(--grey);line-height:1.7" contenteditable="true">{esc(' · '.join(co['platforms']))}</div></div>
    <div class="card"><div class="pill cy" contenteditable="true">冻干</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">全流程服务</div>
      <div style="font-size:var(--t-note);color:var(--grey);line-height:1.7" contenteditable="true">原料筛选验证 → 工艺优化 → 量产</div></div>
    <div class="card"><div class="pill cy" contenteditable="true">服务</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">应用支持</div>
      <div style="font-size:var(--t-note);color:var(--grey);line-height:1.7" contenteditable="true">按客户体系做平行比对与问题排查</div></div>
  </div>
</div>
{footbar(21)}""", nav='三 · 制造与研发平台')

# --- P22 研发平台 · 酶的进化研究（方案 C 双螺旋） ---
add('研发平台', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">R & D PLATFORM</div>
  <h3 class="h3" contenteditable="true">研发平台 · 酶的进化研究</h3>
  <div class="rule cy"></div>
  <div class="row" style="margin-top:.4vh;flex:1">
    <div class="col" style="display:flex;flex-direction:column;justify-content:center">
      {ul(['依托蛋白进化、重组表达、抗体开发、适配体筛选、工艺开发、应用研究六大平台',
           '对聚合酶、逆转录酶进行定向进化与理性设计，筛选高温耐受、高抗抑制突变体',
           '酶性能表征覆盖：聚合酶性能 / 反转录酶性能 / 外切酶活性 / 功能性验证 / 外源核酸酶残留 / 外源核酸残留 / 理化性质 / IC50 模型'])}
    </div>
    <div class="col" style="min-height:0;display:flex;align-items:center;justify-content:center">
      <div style="width:100%;height:44vh;position:relative" data-helix data-amp="92" data-turns="3.2">
        <svg viewBox="0 0 1000 340" preserveAspectRatio="xMidYMid meet" style="width:100%;height:100%"></svg>
      </div>
    </div>
  </div>
  <div class="card blue" style="margin-top:1.2vh"><div class="lead" contenteditable="true"><b>十余年只做一件事：把酶的性能指标一项项压到极致。</b>每一项指标都有实测数据支撑（见第四部分七个场景）。</div></div>
</div>
{footbar(22)}""", cls='', nav='三 · 研发平台（双螺旋）')

# --- P23 冻干服务与仪器 ---
add('冻干服务', f"""{topbar('关于宝锐')}
<div class="grow">
  <div class="kicker" contenteditable="true">LYO SERVICE</div>
  <h3 class="h3" contenteditable="true">冻干服务与仪器</h3>
  <div class="rule"></div>
  <div class="grid3" style="margin-top:1vh">
    <div class="card"><div class="pill" contenteditable="true">第一步</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">原料筛选验证</div>
      <div class="lead" contenteditable="true">确认原料可冻干性与配方兼容性，避免后期返工</div></div>
    <div class="card"><div class="pill" contenteditable="true">第二步</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">冻干工艺优化</div>
      <div class="lead" contenteditable="true">保护剂配方 · 冻干曲线 · 复溶性验证</div></div>
    <div class="card"><div class="pill cy" contenteditable="true">第三步</div>
      <div style="font-size:var(--t-h3);font-weight:800;margin:1vh 0 .7vh" contenteditable="true">冻干量产</div>
      <div class="lead" contenteditable="true">批间稳定性 · 常温储运验证 · 量产放大</div></div>
  </div>
  <div class="card" style="margin-top:1.4vh">
    <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.1vh" contenteditable="true">可冻干原料品类齐全</div>
    <div style="display:flex;gap:.6vw;flex-wrap:wrap">
      {''.join(f'<span class="pill" contenteditable="true">{x}</span>' for x in ['Taq 聚合酶','MMLV 逆转录酶','UNG','Tth','探针','单酶','预混液'])}
    </div>
    <div class="lead" style="margin-top:1.2vh" contenteditable="true">产品已完成<b>常温放置 18 个月稳定性验证</b>；全预混 37℃ 7 天稳定；抗吸湿冻干保护剂，冻干环境湿度控制要求低。</div>
  </div>
</div>
{footbar(23)}""", nav='三 · 冻干服务与仪器')

# --- P24 / P25 成功案例（占位骨架，待素材） ---
for k in (1,2):
    add(f'案例 {k}', f"""{topbar('成功案例')}
<div class="grow">
  <div class="kicker" contenteditable="true">CASE STUDY {k}</div>
  <h3 class="h3" contenteditable="true">成功案例 {k}</h3>
  <div class="rule cy"></div>
  <div class="card blue" style="margin-top:1vh">
    <div style="display:flex;align-items:center;gap:.8vw;margin-bottom:1.4vh">
      <span class="pill cy" contenteditable="true">待补充素材</span>
      <span class="src-tag" contenteditable="true">本页为占位骨架 —— 客户信息需授权后方可使用，暂不填写具体客户名</span>
    </div>
    {dtable([['客户类型','【待填：如 某头部 IVD 企业 / 某省疾控 / 某 ICL 集团】'],
             ['应用场景','【待填：如 冻干型呼吸道多联检 / 全血直扩分型】'],
             ['关键指标','【待填：如 批间 CV < 5% / 常温 18 个月活性保留 > 90%】'],
             ['协同方式','【待填：联合开发 / 定制化 / 工艺共建】'],
             ['合作成果','【待填：如 成功注册上市 / 供货份额提升 / 成本下降】']], ['维度','内容'])}
  </div>
  <div class="card" style="margin-top:1.3vh"><div class="lead" contenteditable="true"><b>说明：</b>本页预留给你认为可对外披露的案例。若采用匿名单例，可写为「某头部 IVD 企业 · 冻干体系 · 批间 CV&lt;5%」这类不含客户名的表述。</div></div>
</div>
{footbar(24+k)}""", nav=f'三 · 成功案例 {k}（待素材）')

# ============================================================
#  第四部分 · 七大热点场景（P26–P39）
# ============================================================
add('四 · 承接页', stage_page('04', '七大热点应用场景', '分子诊断流水线 · 抗抑制直扩 · 快速扩增 · 多重溶解曲线 · 单管全预混 · 无核酸残留 · 冻干'),
    cls='stage', nav='四 · 七大场景 · 承接页')

# --- P26 七场景总览 ---
add('七场景总览', f"""{topbar('七大热点场景')}
<div class="grow">
  <div class="kicker" contenteditable="true">SEVEN SCENARIOS</div>
  <h3 class="h3" contenteditable="true">七个场景，逐个用数据对话</h3>
  <div class="rule"></div>
  <div class="scGrid" style="margin-top:1vh">
    {''.join(f'''<div class="scCard">
      <div class="no">0{i}</div>
      <div class="nm" contenteditable="true">{esc(s['name'])}</div>
      <div class="ds" contenteditable="true">{esc(s['need']['subtitle'])}</div>
    </div>''' for i,s in enumerate(DATA['scenarios'],1))}
    <div class="scCard" style="background:linear-gradient(150deg,rgba(0,47,167,.06),rgba(0,212,255,.04))">
      <div class="no">→</div>
      <div class="nm" contenteditable="true">需求分析 + 宝锐数据</div>
      <div class="ds" contenteditable="true">每个场景 2 页：先讲你的卡点，再给实测数据</div>
    </div>
  </div>
</div>
{footbar(26)}""", nav='四 · 七场景总览')

# --- P27–P39 场景页（每场景：需求页 + 数据页） ---
pno = 27
for idx, sc in enumerate(DATA['scenarios'], 1):
    nd = sc['need']; dt = sc['data']
    # 需求页
    add(f'场景{idx} 需求', f"""{topbar('七大热点场景')}
<div class="grow">
  {scene_head('0%d' % idx, sc['name'] + ' · 需求分析', 'SCENARIO %d / 7' % idx)}
  <div class="sub" contenteditable="true">{esc(nd['subtitle'])}</div>
  <div class="rule"></div>
  <div class="row" style="margin-top:.6vh">
    <div class="col">
      <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1.3vh" contenteditable="true">客户端的需求与卡点</div>
      {ul(nd['points'])}
    </div>
    <div class="col" style="display:flex;align-items:center">
      <div class="card blue" style="width:100%">
        <div style="font-size:var(--t-h3);font-weight:800;margin-bottom:1vh;color:var(--blue)" contenteditable="true">这个场景要什么</div>
        <div class="lead" contenteditable="true">需要原料在<b>该场景的关键指标</b>上达到可量产、可注册、批间稳定的水平 —— 下一页给宝锐实测数据。</div>
      </div>
    </div>
  </div>
</div>
{footbar(pno)}""", nav=f'四 · 场景{idx} 需求')
    pno += 1
    # 数据页
    body = ''
    if dt.get('chart'):
        ch = dt['chart']
        if ch['type'] == 'bar':
            body = bartable(ch, h='29vh')
        elif ch['type'] == 'kv':
            body = dtable([list(r) for r in ch['rows']], ['项目','实测结果'])
    blocks = []
    if body: blocks.append(f'<div style="margin-bottom:1.2vh">{body}</div>')
    if dt.get('items'):
        blocks.append(dtable([[i['k'], i['v'], i.get('src','')] for i in dt['items']], ['项目','宝锐实测 / 事实','来源']))
    if dt.get('note'):
        blocks.append(f'<div class="card blue" style="margin-top:1.2vh"><div class="lead" contenteditable="true">{esc(dt["note"])}</div></div>')
    add(f'场景{idx} 数据', f"""{topbar('七大热点场景')}
<div class="grow">
  {scene_head('0%d' % idx, sc['name'] + ' · 宝锐数据', 'SCENARIO %d / 7 · DATA' % idx)}
  <h3 class="h3" style="margin-bottom:.4vh" contenteditable="true">{esc(dt['title'])}</h3>
  <div class="rule cy" style="margin:1vh 0 1.4vh"></div>
  {''.join(blocks)}
</div>
{footbar(pno)}""", nav=f'四 · 场景{idx} 数据')
    pno += 1

# --- P40 七场景货号速查 ---
add('货号速查', f"""{topbar('七大热点场景')}
<div class="grow">
  <div class="kicker" contenteditable="true">SKU MAP</div>
  <h3 class="h3" contenteditable="true">七场景 × 推荐体系货号速查</h3>
  <div class="rule"></div>
  <div style="margin-top:.8vh">
    {dtable([[c['scenario'], c['sku'], c['name'], c['note']] for c in DATA['catalog']], ['场景','推荐货号','产品名称','关键说明'])}
  </div>
  <div class="card blue" style="margin-top:1.3vh"><div class="lead" contenteditable="true">标注 <b>FM / FMD</b> 前缀为可冻干货号，可直接用于冻干体系开发。</div></div>
</div>
{footbar(40)}""", nav='四 · 货号速查')

# --- P41 结语（方案 D + Logo/Slogan） ---
add('结语', f"""<div class="canvasHost" data-mol data-n="46" data-d="165" data-a="0.14"></div>
<div class="st-num">—</div>
<div class="stageTxt">
  <div class="tb-logo" role="img" aria-label="宝锐生物 BIORI BIOTECH" style="width:26vh;height:8.8vh;min-width:230px;min-height:78px;margin-bottom:3.4vh"></div>
  <h2 contenteditable="true">把体系交给我们，把时间留给您的团队</h2>
  <div class="u"></div>
  <div style="margin-top:3.6vh;font-size:min(2.5vw,3.8vh);font-weight:800;letter-spacing:5px;background:linear-gradient(96deg,var(--blue),var(--cyan));-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent" contenteditable="true">守护生命健康<b style="-webkit-text-fill-color:var(--cyan)">·</b>缔造美好生活</div>
  <div class="st-sub" style="margin-top:2.6vh" contenteditable="true">珠海宝锐生物科技有限公司 ｜ BIORI BIOTECH</div>
</div>""", cls='stage', nav='结语')

# ============================================================
#  组装输出
# ============================================================
toc_html = ''.join(
    f'<a href="#" onclick="event.preventDefault();document.getElementById(\'toc\').classList.remove(\'on\');show({i});"><b>{i+1}</b>{esc(p["nav"])}</a>'
    for i, p in enumerate(pages)
)
slides_html = ''.join(
    f'<section class="slide {p["cls"]}" data-i="{i}">{p["body"]}</section>'
    for i, p in enumerate(pages)
)
CSS = CSS.replace('LOGO_B64', LOGO_B64)

HTML = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>宝锐诊断原料 · 客户宣讲（科技风动画版）· 41 页</title>
<style>{CSS}</style>
</head>
<body>
<div class="deck">
{slides_html}
<button class="navbtn prev" id="prevBtn" title="上一页 ←">‹</button>
<button class="navbtn next" id="nextBtn" title="下一页 →">›</button>
<div class="progress" id="prog" style="width:0"></div>
</div>
<div class="toc" id="toc"><h3>目录 · 共 {len(pages)} 页（按 N 开关 / Esc 关闭）</h3><div class="tgrid">{toc_html}</div></div>
<div class="editHint" id="eh">✎ 全部文字可直接点击编辑</div>
<script>{JS}
document.addEventListener('click', function(e){{
  if(e.target.isContentEditable){{
    var h=document.getElementById('eh'); h.classList.add('on');
    clearTimeout(window.__eht); window.__eht=setTimeout(function(){{h.classList.remove('on')}},1600);
  }}
}});
</script>
</body>
</html>
"""

open(OUT, 'w', encoding='utf-8').write(HTML)
print('✓ 生成完成：%s' % OUT)
print('   页数：%d 页 ｜ 大小：%.1f KB' % (len(pages), len(HTML.encode('utf-8'))/1024))
print('   动画：封面+承接页=%d 处（方案D）｜ 流程页=%d 处（方案A）｜ 技术页=%d 处（方案C）' % (
    sum(1 for p in pages if p['cls']=='stage'),
    sum(1 for p in pages if '<svg' in p['body']),
    sum(1 for p in pages if 'data-helix' in p['body'])))
print()
for i,p in enumerate(pages,1):
    print('  P%-3d %s' % (i, p['nav']))
