#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""PCR 检测体系灵敏度：对外技术资料版（自包含 HTML）。"""
import base64
import math
from pathlib import Path

BASE = Path('/Users/liuxinyuan/Desktop/Hermes输出-工作类')
OUT = BASE / '知识沉淀' / 'PCR体系灵敏度_机理与调整_对外技术资料_20261102_V01.html'
LOGO = Path('/Users/liuxinyuan/work/公司资料/logo_transparent.png')
LOGO_URI = 'data:image/png;base64,' + base64.b64encode(LOGO.read_bytes()).decode('ascii')


def c(*nums):
    return '<sup>' + ''.join(f'[{n}]' for n in nums) + '</sup>'


def tbl(headers, rows, cls='', widths=None):
    cells = []
    for x, w in zip(headers, widths or [None] * len(headers)):
        style = ' style="width:%s"' % w if w else ''
        cells.append('<th%s>%s</th>' % (style, x))
    h = ''.join(cells)
    body = ''.join('<tr>' + ''.join('<td>%s</td>' % x for x in r) + '</tr>' for r in rows)
    return '<div class="tw"><table class="%s"><thead><tr>%s</tr></thead><tbody>%s</tbody></table></div>' % (cls, h, body)


def section(num, title, en, body):
    return f'<section class="chapter" id="s{num}"><div class="chap-head"><div class="chap-num">{num}</div><div><div class="chap-en">{en}</div><h2>{title}</h2></div></div>{body}</section>'


def keycards(items):
    return '<div class="keygrid">' + ''.join(f'<div class="keycard"><div class="kv">{v}</div><div class="kl">{l}</div><div class="kn">{n}</div></div>' for v, l, n in items) + '</div>'


def plain(title, text):
    return f'<div class="plain"><div class="plain-t">{title}</div><div>{text}</div></div>'


def boundary(text):
    return f'<div class="boundary"><b>数据边界</b>{text}</div>'


def formula(text, note=''):
    return f'<div class="formula">{text}{f"<span>{note}</span>" if note else ""}</div>'


# ---------------- figures ----------------
def poisson_svg():
    W, H = 760, 300
    left, right, top, bottom = 55, 18, 18, 42
    pw, ph = W - left - right, H - top - bottom
    pts = []
    for i in range(0, 101):
        lam = i / 10
        p = 1 - math.exp(-lam)
        x = left + lam / 10 * pw
        y = top + ph - p * ph
        pts.append(f'{x:.1f},{y:.1f}')
    grid = []
    for p in [0, 0.25, 0.5, 0.75, 1.0]:
        y = top + ph - p * ph
        grid.append(f'<line x1="{left}" y1="{y:.1f}" x2="{W-right}" y2="{y:.1f}" stroke="#D9E6EF"/>')
        grid.append(f'<text x="{left-8}" y="{y+4:.1f}" text-anchor="end" class="ax">{int(p*100)}%</text>')
    for lam in [0, 2, 4, 6, 8, 10]:
        x = left + lam / 10 * pw
        grid.append(f'<text x="{x:.1f}" y="{H-15}" text-anchor="middle" class="ax">{lam}</text>')
    x3 = left + 2.996 / 10 * pw
    y3 = top + ph - 0.95 * ph
    return f'''<svg viewBox="0 0 {W} {H}" class="fig-svg">
    <rect width="{W}" height="{H}" fill="#fff"/>
    {''.join(grid)}
    <line x1="{left}" y1="{top+ph}" x2="{W-right}" y2="{top+ph}" stroke="#91AEBF"/>
    <line x1="{left}" y1="{top}" x2="{left}" y2="{top+ph}" stroke="#91AEBF"/>
    <polyline points="{' '.join(pts)}" fill="none" stroke="#1F4E79" stroke-width="3"/>
    <line x1="{x3:.1f}" y1="{y3:.1f}" x2="{x3:.1f}" y2="{top+ph}" stroke="#D9822B" stroke-width="1.5" stroke-dasharray="5 4"/>
    <line x1="{left}" y1="{y3:.1f}" x2="{x3:.1f}" y2="{y3:.1f}" stroke="#D9822B" stroke-width="1.5" stroke-dasharray="5 4"/>
    <circle cx="{x3:.1f}" cy="{y3:.1f}" r="5" fill="#D9822B"/>
    <text x="{x3+8:.1f}" y="{y3-10:.1f}" class="ann">λ≈3.0 → 95%</text>
    <text x="{W/2}" y="{H-2}" text-anchor="middle" class="ax">每反应平均拷贝数 λ（Poisson）</text>
    <text x="14" y="{H/2}" transform="rotate(-90 14 {H/2})" text-anchor="middle" class="ax">检出概率</text>
    <text x="{left}" y="{top-5}" class="ax">P = 1 − e^(−λ)</text>
    </svg>'''


def clinical_svg():
    data = [
        ('SARS-CoV-2 RT-PCR', 89.0, 85.4, 91.8, '#1F4E79'),
        ('MRSA PCR', 92.5, 87.4, 95.9, '#2A7F8F'),
        ('HPV DNA · CIN2+', 91.6, 88.1, 94.1, '#2A9D8F'),
        ('Sepsis dPCR', 94.0, 85.0, 98.0, '#D9822B'),
        ('CT pooled multisite', 93.1, 90.5, 95.0, '#7B6FA8'),
        ('NG pooled multisite', 94.1, 90.9, 96.3, '#5B8DB8'),
    ]
    W, H = 760, 300
    left, right, top, row = 155, 40, 26, 38
    pw = W - left - right
    xmin, xmax = 80, 100
    def X(v): return left + (v - xmin) / (xmax - xmin) * pw
    parts = [f'<svg viewBox="0 0 {W} {H}" class="fig-svg"><rect width="{W}" height="{H}" fill="#fff"/>']
    for v in [80, 85, 90, 95, 100]:
        x = X(v)
        parts.append(f'<line x1="{x:.1f}" y1="{top-8}" x2="{x:.1f}" y2="{top+row*len(data)}" stroke="#E1EBF2"/>')
        parts.append(f'<text x="{x:.1f}" y="{top+row*len(data)+18}" text-anchor="middle" class="ax">{v}%</text>')
    for i, (name, v, lo, hi, col) in enumerate(data):
        y = top + i * row
        parts.append(f'<text x="{left-10}" y="{y+14}" text-anchor="end" class="lbl">{name}</text>')
        parts.append(f'<line x1="{X(lo):.1f}" y1="{y+14}" x2="{X(hi):.1f}" y2="{y+14}" stroke="{col}" stroke-width="2" opacity=".55"/>')
        parts.append(f'<line x1="{X(lo):.1f}" y1="{y+8}" x2="{X(lo):.1f}" y2="{y+20}" stroke="{col}" stroke-width="1.4"/>')
        parts.append(f'<line x1="{X(hi):.1f}" y1="{y+8}" x2="{X(hi):.1f}" y2="{y+20}" stroke="{col}" stroke-width="1.4"/>')
        parts.append(f'<circle cx="{X(v):.1f}" cy="{y+14}" r="5" fill="{col}"/>')
        parts.append(f'<text x="{X(v)+8:.1f}" y="{y+13}" class="val">{v:.1f}%</text>')
    parts.append('</svg>')
    return ''.join(parts)


def cleavage_svg():
    data = [('Taq-wt', 77.39, '#9DB7C8'), ('S-Taq', 76.87, '#9DB7C8'), ('Taq-E507K', 72.16, '#C7D2DA'), ('Taq388', 91.96, '#1F4E79'), ('TM-Taq', 88.19, '#2A9D8F')]
    W, H = 760, 280
    left, right, top, bottom = 55, 20, 25, 55
    pw, ph = W - left - right, H - top - bottom
    ymin, ymax = 60, 100
    bw = pw / len(data) * .52
    parts = [f'<svg viewBox="0 0 {W} {H}" class="fig-svg"><rect width="{W}" height="{H}" fill="#fff"/>']
    for v in [60, 70, 80, 90, 100]:
        y = top + ph - (v - ymin) / (ymax - ymin) * ph
        parts.append(f'<line x1="{left}" y1="{y:.1f}" x2="{W-right}" y2="{y:.1f}" stroke="#DCE8EF"/>')
        parts.append(f'<text x="{left-8}" y="{y+4:.1f}" text-anchor="end" class="ax">{v}%</text>')
    for i, (name, v, col) in enumerate(data):
        x = left + (i + .5) * (pw / len(data)) - bw / 2
        y = top + ph - (v - ymin) / (ymax - ymin) * ph
        parts.append(f'<rect x="{x:.1f}" y="{y:.1f}" width="{bw:.1f}" height="{top+ph-y:.1f}" fill="{col}" rx="2"/>')
        parts.append(f'<text x="{x+bw/2:.1f}" y="{y-7:.1f}" text-anchor="middle" class="val">{v:.1f}%</text>')
        parts.append(f'<text x="{x+bw/2:.1f}" y="{top+ph+19}" text-anchor="middle" class="lbl">{name}</text>')
    parts.append('</svg>')
    return ''.join(parts)


# ---------------- reference list ----------------
REFS = [
    ('Bustin SA, et al. The MIQE guidelines: minimum information for publication of quantitative real-time PCR experiments. Clin Chem. 2009;55(4):611-622.', '10.1373/clinchem.2008.112797', 'A'),
    ('Bustin SA, et al. MIQE 2.0: revision of the minimum information for publication of quantitative real-time PCR experiments guidelines. Clin Chem. 2025.', '10.1093/clinchem/hvaf043', 'A'),
    ('Forootan A, Sjöback R, Björkman J, et al. Methods to determine limit of detection and limit of quantification in quantitative real-time PCR (qPCR). Biomol Detect Quantif. 2017;12:1-6.', '10.1016/j.bdq.2017.04.001', 'A'),
    ('Bustin S, Huggett J. qPCR primer design revisited. Biomol Detect Quantif. 2017;14:19-28.', '10.1016/j.bdq.2017.11.001', 'A'),
    ('Montgomery JL, Wittwer CT. Influence of PCR reagents on DNA polymerase extension rates measured on real-time PCR instruments. Clin Chem. 2014;60(2):334-340.', '10.1373/clinchem.2013.212829', 'A'),
    ('Turvey MW, Gabriel KN, Lee W, et al. Single-molecule Taq DNA polymerase dynamics. Sci Adv. 2022;8(10):eabl3522.', '10.1126/sciadv.abl3522', 'A'),
    ('Kuznetsova AA, et al. Structural and molecular kinetic features of activities of DNA polymerases. Int J Mol Sci. 2022;23(12):6373.', '10.3390/ijms23126373', 'A'),
    ('Cao Q, Zou Z, Cao D, et al. Mechanism and application of Taq DNA polymerase in TaqMan qPCR. Front Bioeng Biotechnol. 2026;14:1773703.', '10.3389/fbioe.2026.1773703', 'B*（厂商主导）'),
    ('Lu Z, et al. Protein engineering of Taq DNA polymerase for ultrafast PCR and its application in molecular diagnostics of genetically modified crops. J Agric Food Chem. 2026;74(1):1264-1275.', '10.1021/acs.jafc.5c12875', 'A/B'),
    ('Li Z, et al. A fusion of Taq DNA polymerase with the CL7 protein from Escherichia coli remarkably improves DNA amplification. Molecules. 2024;29(5):1145.', '10.3390/molecules29051145', 'A'),
    ('Sidstedt M, Rådström P, Hedman J. PCR inhibition in qPCR, dPCR and MPS—mechanisms and solutions. Anal Bioanal Chem. 2020;412:2009-2023.', '10.1007/s00216-020-02490-2', 'A'),
    ('Kuznetsova AA, et al. Engineering a thermostable reverse transcriptase for RT-PCR through rational design of Pyrococcus furiosus DNA polymerase. Biomolecules. 2025;15(11):1507.', '10.3390/biom15111507', 'A'),
    ('Lee SJ, et al. Development of a simple direct and hot-start PCR using Escherichia coli-expressing Taq DNA polymerase. Int J Mol Sci. 2023;24(14):11405.', '10.3390/ijms241411405', 'B'),
    ('Marando M, et al. Diagnostic sensitivity of RT-PCR assays on nasopharyngeal specimens for detection of SARS-CoV-2 infection: a systematic review and meta-analysis. Caspian J Intern Med. 2022;13:139-147.', '10.22088/cjim.13.0.139', 'A'),
    ('Aboud L, et al. Diagnostic accuracy of pooling urine, anorectal, and oropharyngeal specimens for the detection of Chlamydia trachomatis and Neisseria gonorrhoeae: a systematic review and meta-analysis. BMC Med. 2021;19:255.', '10.1186/s12916-021-02160-9', 'A'),
    ('Luteijn JM, et al. Diagnostic accuracy of culture-based and PCR-based detection tests for methicillin-resistant Staphylococcus aureus: a meta-analysis. Clin Microbiol Infect. 2011;17(2):146-154.', '10.1111/j.1469-0691.2010.03202.x', 'A'),
    ('Kelly H, et al. Diagnostic accuracy of cervical cancer screening strategies for high-grade cervical intraepithelial neoplasia (CIN2+/CIN3+) among women living with HIV: a systematic review and meta-analysis. EClinicalMedicine. 2022;53:101645.', '10.1016/j.eclinm.2022.101645', 'A'),
    ('Li Y, Ma M, Xu X, Li Q, Ji C. Value of digital PCR in the early diagnosis of sepsis: a systematic review and meta-analysis. J Crit Care. 2022;71:154138.', '10.1016/j.jcrc.2022.154138', 'A'),
    ('Šimundić AM. Measures of diagnostic accuracy: basic definitions. Biochem Med. 2009;19(3):203-211. PMC4975285.', 'PMC4975285', 'A'),
    ('RCSB PDB. Taq polymerase–DNA complexes 1TAU and 1TAQ.', 'PDB 1TAU / 1TAQ', 'A'),
    ('Polymerase chain reaction: basic protocol plus troubleshooting and optimization strategies. J Vis Exp. 2012;(63):e3998.', '10.3791/3998', 'C'),
    ('NEB. Guidelines for PCR optimization with Taq DNA polymerase（厂商技术资料，Mg²⁺ 1.5–2.0 mM；比总 dNTP 高 0.5–1 mM）. 访问 2026-11.', 'neb.com', 'C'),
    ('Thermo Fisher Scientific. PCR setup—six critical components to consider（厂商技术资料，Mg²⁺ 1–4 mM，0.5 mM 递增）. 访问 2026-11.', 'thermofisher.com', 'C'),
    ('Ampliqon / Thermo Fisher. Taq DNA polymerase stability data（厂商资料：95℃ 半衰期约 40 min；97.5℃ 约 9 min）. 访问 2026-11.', '厂商稳定性资料', 'C'),
]


# ---------------- tables ----------------
t_metrics = tbl(
    ['口径', '回答的问题', '常用公式/定义', '主要决定因素', '不能等同于'],
    [
        ['LOD95', '最低能可靠检出多少？', '95% 检出概率的最低浓度/拷贝数；CLSI EP17 与 qPCR logistic 方法', '取样、回收、扩增效率、背景、重复数', '临床灵敏度'],
        ['扩增效率 E', '每轮扩增放大多少？', 'E = 10^(−1/slope) − 1；90–110% 对应 slope ≈ −3.59 至 −3.10', '聚合酶、引探、Mg²⁺/dNTP、抑制剂', '检出限本身'],
        ['Ct / Cq', '达到阈值要几轮？', '与初始拷贝数的对数成反比；100% 效率下 1 个 Ct ≈ 2 倍模板', '初始拷贝数、效率、阈值设定', '灵敏度或临床灵敏度'],
        ['临床灵敏度', '有病的人里检出多少？', 'TP / (TP + FN)', '患病率、疾病谱、采样、参考标准', '分析灵敏度/LOD95'],
    ],
    'tbl-main', ['11%', '20%', '31%', '22%', '16%']
)

t_e = tbl(
    ['扩增效率 E', '标准曲线斜率', '含义', '备注'],
    [['90%', '−3.59', '每轮 1.90 倍', '仍可用，但低拷贝更易漏检'],
     ['100%', '−3.32', '每轮 2.00 倍', '理论理想值'],
     ['110%', '−3.10', '每轮 2.10 倍', '可能存在抑制物/引物二聚体干扰']],
    'tbl-small', ['18%', '20%', '26%', '36%']
)

t_poisson = tbl(
    ['每反应平均拷贝数 λ', '至少检出 1 个拷贝的概率', '解释'],
    [['1', '63.2%', '低拷贝重复间波动很大'],
     ['2', '86.5%', '仍有约 13.5% 的孔判阴性'],
     ['3', '95.0%', 'Poisson 理论的 95% 交点'],
     ['5', '99.3%', '接近但未到 100%'],
     ['10', '99.995%', '取样波动已很小']],
    'tbl-small', ['26%', '37%', '37%']
)

t_enzyme = tbl(
    ['参数', '定义/单位', '文献报告值或范围', '对 qPCR 的含义', '证据'],
    [['延伸速率', 'nt/s 或 kb/min', 'Taq 约 2 kb/min（≈33 nt/s）；CL7-Taq 约 4 kb/min（≈67 nt/s）', '决定延伸时间与短靶标可用性；不是 LOD 的唯一变量', f'{c(10)}'],
     ['持续合成能力 processivity', '单个结合事件掺入的 nt 数', 'Taq 较低；长于 1 kb 的产量下降；Sso7d/CL7 融合可提高', '影响长扩增子与低拷贝模板的完成率', f'{c(9,10)}'],
     ['催化效率 kcat/Km', 'M⁻¹s⁻¹', '受 dNTP、模板、温度、缓冲体系影响；文献值不宜跨条件横比', '低 dNTP 时决定延伸速率；比单独 kcat 更接近“效率”', f'{c(7)}'],
     ['5′核酸酶切割', 'PC/PY（切割量/产物量）', 'Taq-wt 77.39%；Taq388 91.96%；TM-Taq 88.19%（三基因平均）', '决定 TaqMan 每扩增子的信号产出', f'{c(8)}'],
     ['热失活', '95℃ 半衰期', '约 40 min；97.5℃ 约 9 min（厂商资料）', '95℃ 停留时间过长会损失活性酶', f'{c(24)}'],
     ['热启动激活', '20 min 后活性比例', '73–90%（文献实测，因酶而异）', '激活不完全会降低有效酶浓度', f'{c(5)}'],
     ['错配/保真度', '错误率', 'Taq 无 3′→5′ 校对，错配率高于高保真酶', '灵敏度与保真度常需权衡', f'{c(7)}']],
    'tbl-main', ['13%', '14%', '34%', '27%', '12%']
)

t_struct = tbl(
    ['结构要素', '功能', '改变后的动力学后果', '对 qPCR 的影响', '证据'],
    [['5′核酸酶结构域', '水解探针、5′flap 内切', '缺失（KlenTaq）→ 无切割、链置换增强', 'TaqMan 信号弱；适合探针熔解曲线', f'{c(8)}'],
     ['手掌 palm', '催化中心、二价金属离子', '突变可改变 kcat、dNTP 选择性', '影响延伸速率与错配识别', f'{c(7)}'],
     ['手指 fingers', 'dNTP 结合、诱导契合', '闭合动力学改变；单分子见 20 µs 瞬态闭合', '影响 dNTP 识别与催化节奏', f'{c(6)}'],
     ['拇指 thumb', 'DNA 结合、持续合成', '亲和力/电荷改变可提高 processivity', '长靶标、低拷贝完成率提高', f'{c(9,10)}']],
    'tbl-main', ['17%', '22%', '28%', '22%', '11%']
)

t_form = tbl(
    ['组分/条件', '常用筛选窗口', '对酶促反应的作用', '过量或不当的负向', '证据'],
    [['Mg²⁺（总）', 'Taq 约 1.5–2.0 mM；常规 1–4 mM，0.5 mM 递增', '辅因子；影响 dNTP 结合与催化', '过高可能降低保真度/特异性；与 dNTP 螯合', f'{c(22,23)}'],
     ['dNTP', '各 0.2 mM；Mg²⁺ 比总 dNTP 高 0.5–1 mM', '底物；决定延伸速率', '过量螯合 Mg²⁺、抑制反应', f'{c(22,23)}'],
     ['K⁺/Na⁺ 等一价离子', 'KCl 约 50 mM', '离子强度；影响 DNA 结合', '升高会持续降低延伸速率', f'{c(5,23)}'],
     ['BSA', '约 0.6%（文献体系）', '减少酶吸附、缓解血液抑制', '批次差异；不是万能解', f'{c(10)}'],
     ['DMSO', '常用 1–10%', '改善 GC 富集模板的变性', '高浓度抑制；可能降低特异性', f'{c(5)}'],
     ['甜菜碱/甲酰胺/丙二醇', '甜菜碱 0.5–2 M；甲酰胺 1–5%', '降低 Tm、缓解二级结构', '明显降低延伸速率', f'{c(5)}'],
     ['甘油', '5–10% 常见', '稳定酶、降低冰点', '高浓度黏度/抑制；对速率影响小', f'{c(5)}'],
     ['pH/缓冲液', 'Tris 约 pH 8.3–8.8（室温）', '维持电荷与活性', '温度/pH 漂移改变有效活性', f'{c(21,23)}']],
    'tbl-main', ['15%', '22%', '25%', '25%', '13%']
)

t_oligo = tbl(
    ['参数', '常用设计窗口', '机制', '过度/不足的后果', '证据'],
    [['引物长度', '18–24 nt', '平衡特异性与退火', '过短→非特异；过长→二级结构', f'{c(4)}'],
     ['引物 Tm / Ta', 'Tm 约 58–62℃；Ta 需实验确定', 'Ta 决定最大结合量，比 Tm 更关键', '只看 Tm 不看 Ta → 假阴性/非特异', f'{c(4)}'],
     ['探针长度/Tm', '18–25 nt；Tm 约 65–70℃，常高于引物 5–10℃', '保证延伸阶段仍结合并被切割', '过高→区分度下降；过低→信号弱', f'{c(23)}'],
     ['扩增子长度', 'qPCR 常用 70–200 bp；TaqMan 常 50–150 bp', '短扩增子受 processivity 限制小', '过长→效率下降、低拷贝漏检', f'{c(4,8)}'],
     ['GC 含量', '约 40–60%', '影响 Tm 与二级结构', '过高→二级结构；过低→Tm 不足', f'{c(4)}'],
     ['引物二聚体/发夹', 'ΔG 尽量低；3′端避免互补', '竞争引物/模板，产生非特异信号', '二聚体→假阳性/灵敏度下降', f'{c(4)}'],
     ['探针 5′端', '避免 5′G；报告基团/淬灭基团匹配', '影响切割识别与淬灭效率', '5′G 可能降低切割效率', f'{c(8,23)}'],
     ['修饰（MGB/LNA）', '按区分度需求选择', '提高 Tm/错配识别', '过度稳定→等位基因区分下降', f'{c(4,8)}']],
    'tbl-main', ['16%', '25%', '24%', '23%', '12%']
)

t_rt = tbl(
    ['环节', '关键参数', '对 RT-qPCR 灵敏度的影响', '常见调整', '证据'],
    [['逆转录', 'RT 的 processivity、热稳定性、RNase H 活性', 'RNA 二级结构/长片段可导致 RT 提前脱落', '提高 RT 温度、耐热 RT、优化引物', f'{c(12)}'],
     ['一步法 vs 两步法', 'RT 与 qPCR 同管/分管', '一步法减少转移损失但优化空间小；两步法可分别优化', '低拷贝 RNA 先比较两种模式', f'{c(1,2)}'],
     ['RNA 质量', '完整性、抑制剂、gDNA 残留', '降解/抑制会直接抬高 LOD', 'RNase-free、DNase、内参', f'{c(11)}'],
     ['引物策略', '基因特异引物/随机引物/Oligo(dT)', '决定 RT 起始效率与覆盖区域', '按靶标长度与 RNA 类型选择', f'{c(4)}']],
    'tbl-main', ['14%', '25%', '30%', '20%', '11%']
)

t_inhib = tbl(
    ['抑制剂/基质', '主要来源', '作用机制', '缓解手段', '证据'],
    [['血红素/血液', '全血、血卡', '与酶/核酸结合，抑制扩增', 'BSA、稀释、提取、耐抑制酶', f'{c(10,11)}'],
     ['肝素', '采血管', '多阴离子，竞争 DNA 结合', '肝素酶、提取、低浓度', f'{c(11)}'],
     ['EDTA', '采血管/缓冲液', '螯合 Mg²⁺', 'Mg²⁺ 滴定、脱盐', f'{c(11)}'],
     ['SDS/去污剂', '裂解液', '使酶变性', '稀释、纯化', f'{c(11)}'],
     ['腐殖酸', '土壤/环境样本', '抑制酶、淬灭荧光', '提取纯化、BSA、稀释', f'{c(11)}'],
     ['胍盐/乙醇', '提取残留', '抑制酶/影响退火', '洗涤、干燥、稀释', f'{c(11)}']],
    'tbl-main', ['17%', '17%', '29%', '26%', '11%']
)

t_intervention = tbl(
    ['调整杠杆', '主要作用参数', '方向', '可能收益', '负向/代价', '验证指标'],
    [['提高酶浓度', '有效酶量', '↑', '低拷贝延伸概率提高', '背景/引物二聚体、成本', 'LOD95、阴性对照'],
     ['提高 processivity', '持续合成', '↑', '长靶标/低拷贝完成率提高', '速度/保真度可能下降', '长扩增子效率'],
     ['提高 5′核酸酶切割', '信号/扩增子', '↑', 'TaqMan 信号更强', '非特异切割/背景', 'PC/PY、阴性对照'],
     ['热启动', '非特异延伸', '↓', '室温组装更稳、特异性提高', '激活不完全（20 min 仅 73–90%）', '激活曲线、Ct'],
     ['Mg²⁺ 滴定', '游离 Mg²⁺', '最优点', '活性/信号提高', '过量降低保真度/特异性', 'E、LOD95'],
     ['dNTP 调整', '底物/Mg 缓冲', '最优点', '延伸速率提高', '过量螯合 Mg²⁺/抑制', 'E、Mg²⁺ 滴定'],
     ['KCl/离子强度', 'DNA 结合', '最优点', '特异性提高', '过高抑制延伸', 'E、Ct'],
     ['BSA', '酶吸附/抑制剂', '↑（适量）', '抗抑制、低拷贝信号提高', '批次差异', '基质样本 LOD'],
     ['DMSO', 'GC 二级结构', '适量', 'GC 富集模板扩增改善', '高浓度抑制/特异性下降', 'E、熔解曲线'],
     ['甜菜碱/甲酰胺', 'Tm/二级结构', '适量', 'GC 富集模板改善', '延伸速率下降', 'E、Ct'],
     ['引物 Tm/Ta 匹配', '退火占据率', '优化', '特异性/效率提高', '只看 Tm 可能失败', 'E、熔解曲线'],
     ['短扩增子', 'processivity 限制', '↓ 长度', '效率/低拷贝检出提高', '太多短片段可能影响特异性', 'E、LOD95'],
     ['探针修饰', 'Tm/错配识别', '按需', '区分度提高', '过度稳定降低区分度', 'ΔCt、熔解曲线'],
     ['样本前处理', '抑制剂/回收', '优化', '有效模板提高', '回收损失/流程复杂', '回收率、LOD95']],
    'tbl-main', ['15%', '15%', '8%', '23%', '23%', '16%']
)


t_clin = tbl(
    ['疾病/场景', '检测方法', '合并灵敏度（95% CI）', '研究规模/参考标准', '证据'],
    [['SARS-CoV-2', '鼻咽拭子 RT-PCR', '89.0%（85.4–91.8）', '13 项研究、25 个 RT-PCR 方案；meta 分析', f'{c(14)}'],
     ['MRSA 定植/感染', 'PCR', '92.5%（87.4–95.9）', '29 篇文献、74 个准确度测量；PCR vs 培养', f'{c(16)}'],
     ['宫颈高级别病变', '14 型 HR-HPV DNA', '91.6%（88.1–94.1）', '38 项研究、18,737 名 HIV 感染女性；CIN2+ 组织学', f'{c(17)}'],
     ['脓毒症', '数字 PCR（血）', '94%（85–98）', '8 项研究、1,278 例；血培养为参考', f'{c(18)}'],
     ['CT 多部位混合', 'NAAT', '93.1%（90.5–95.0）', '17 项研究；单部位 NAAT 为参考', f'{c(15)}'],
     ['NG 多部位混合', 'NAAT', '94.1%（90.9–96.3）', '17 项研究；单部位 NAAT 为参考', f'{c(15)}']],
    'tbl-main', ['17%', '17%', '23%', '31%', '12%']
)

t_doe = tbl(
    ['实验', '设计', '样本量/重复', '读出', '验收/解释'],
    [['LOD95', '3 个低浓度 × 20 重复 × 3 次运行', '每浓度 60 孔', '95% 检出率、概率曲线', '报告最低浓度与 CI；不得只用 1 次'],
     ['扩增效率', '5 个 10 倍稀释点 × 3 重复', '15 孔/次，至少 2 次', 'slope、E、R²', 'E 90–110%，R²≥0.99；异常点回查'],
     ['Mg²⁺/dNTP 矩阵', 'Mg 3 水平 × dNTP 2 水平', '3 重复 × 2 次', 'E、Ct、LOD95', '选最优点而非单点最优'],
     ['引探 Tm/Ta 梯度', 'Ta 梯度 3–5 点', '3 重复', 'Ct、熔解峰、阴性对照', '以 Ta 实验为准，不只看预测 Tm'],
     ['样本基质', '高/中/低抑制基质加标', '3 重复 × 3 浓度', '回收率、LOD95', '基质特异性结论'],
     ['临床灵敏度', '前瞻/回顾队列，盲法', '按目标人群估算', 'TP/FN、灵敏度/特异度', '遵循 STARD；报告参考标准与人群']],
    'tbl-main', ['16%', '27%', '19%', '24%', '14%']
)

t_evidence = tbl(
    ['编号', '来源', '类型/等级', '本报告用它支持', '限制'],
    [[f'[{i}]', r[0], r[2], use, lim] for i, (r, use, lim) in enumerate([
        (REFS[0], 'LOD/E/Ct/临床灵敏度定义', '报告规范，不是机理实验'),
        (REFS[1], '更新后的 qPCR 报告口径', '不提供酶学数据'),
        (REFS[2], 'LOD95、LoB/LoD 统计', '以特定 qPCR 体系为例'),
        (REFS[3], '引物设计、Ta、二级结构', '综述/方法学'),
        (REFS[4], '延伸速率与添加剂影响', '体外实时荧光体系'),
        (REFS[5], 'Taq 单分子构象动力学', '非 qPCR LOD 直接数据'),
        (REFS[6], '聚合酶结构/动力学框架', '综述，非 Taq 专属'),
        (REFS[7], 'TaqMan 切割、变体 PC/PY', '厂商主导，需独立互证'),
        (REFS[8], 'Taq 工程化、超快 qPCR', '特定 GM 作物体系'),
        (REFS[9], 'CL7-Taq 灵敏度/延伸/抗抑制', '特定融合酶体系'),
        (REFS[10], 'PCR 抑制机制与对策', '基质相关，需本地验证'),
        (REFS[11], '耐热 RT 工程', 'RT 章节，非 qPCR 全体系'),
        (REFS[12], '热启动 Taq 构建', '方法学，非 LOD 数据'),
        (REFS[13], 'SARS-CoV-2 临床灵敏度', '异质性高，非产品承诺'),
        (REFS[14], 'CT/NG 混合样本灵敏度', '混合采样，非单样本'),
        (REFS[15], 'MRSA PCR 灵敏度', '2011 年 meta 分析'),
        (REFS[16], 'HPV DNA CIN2+ 灵敏度', 'HIV 感染女性人群'),
        (REFS[17], '脓毒症 dPCR 灵敏度', 'dPCR，不是 qPCR'),
        (REFS[18], '临床灵敏度定义', '定义，不是 qPCR 性能'),
        (REFS[19], 'Taq 结构域/复合物', '结构示意，非按比例'),
        (REFS[20], 'PCR 优化方法学', '协议，非机理定量'),
        (REFS[21], 'Mg²⁺ 参考窗口', '厂商资料'),
        (REFS[22], 'PCR 组分参考窗口', '厂商资料'),
        (REFS[23], 'Taq 热稳定性', '厂商资料'),
    ], 1)],
    'tbl-main', ['6%', '34%', '17%', '25%', '18%']
)

# ---------------- HTML ----------------
HTML = f"""<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PCR 检测体系灵敏度：机理与调整 · 对外技术资料</title>
<style>
:root{{--navy:#123B5C;--blue:#1F4E79;--cyan:#0E8FA8;--teal:#2A9D8F;--orange:#D9822B;--bg:#F3F7FA;--ink:#17324A;--muted:#5C788B;--line:#D8E6EF;--card:#fff}}
*{{box-sizing:border-box}}
html{{scroll-behavior:smooth}}
body{{margin:0;background:var(--bg);color:var(--ink);font-family:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif;line-height:1.65}}
a{{color:var(--blue);text-decoration:none}}
.sidebar{{position:fixed;left:0;top:0;bottom:0;width:238px;background:#0E2C44;color:#D8EAF4;padding:24px 18px;overflow:auto;z-index:10}}
.sidebar .logo{{width:150px;background:#fff;border-radius:6px;padding:6px 8px;display:block;margin-bottom:18px}}
.sidebar .st{{font-size:11px;letter-spacing:.12em;color:#8FB6CB;margin-bottom:3px}}
.sidebar .sn{{font-size:14px;color:#fff;font-weight:600;line-height:1.4;margin-bottom:16px}}
.sidebar nav a{{display:block;color:#BBD4E2;font-size:12px;padding:6px 0 6px 12px;border-left:2px solid transparent;line-height:1.35}}
.sidebar nav a:hover{{color:#fff;border-left-color:#5BC0D6;background:rgba(255,255,255,.05)}}
.sidebar .sf{{position:absolute;bottom:18px;left:18px;right:18px;font-size:10px;color:#7FA6BA;line-height:1.5}}
main{{margin-left:238px;max-width:1040px;padding:0 34px 60px}}
.cover{{min-height:92vh;background:linear-gradient(135deg,#0B2C46 0%,#164A6E 55%,#1F6A82 100%);color:#fff;margin:0 -34px 30px;padding:50px 60px 40px;position:relative;overflow:hidden;display:flex;flex-direction:column}}
.cover:before{{content:"";position:absolute;right:-120px;top:-100px;width:520px;height:520px;border:1px solid rgba(123,209,224,.35);border-radius:50%}}
.cover:after{{content:"";position:absolute;right:40px;bottom:-170px;width:420px;height:420px;border:1px solid rgba(123,209,224,.16);border-radius:50%}}
.cover .ctop{{display:flex;align-items:center;gap:14px;position:relative;z-index:2}}
.cover .ctop img{{height:48px;background:#fff;border-radius:5px;padding:4px 7px}}
.cover .ctop span{{font-size:10px;letter-spacing:.18em;color:#A9D5E2}}
.cover .kicker{{margin-top:70px;font-size:11px;letter-spacing:.24em;color:#8ED3E1;position:relative;z-index:2}}
.cover h1{{font-size:54px;line-height:1.06;letter-spacing:-.03em;margin:10px 0 8px;color:#fff;position:relative;z-index:2}}
.cover h2{{font-size:20px;line-height:1.45;font-weight:500;color:#D6EDF5;margin:0;position:relative;z-index:2;max-width:700px}}
.cover .scope{{margin-top:18px;font-size:12px;color:#B8DCE8;position:relative;z-index:2}}
.cover .cmetrics{{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:auto;position:relative;z-index:2;padding-top:40px}}
.cover .cm{{border:1px solid rgba(142,211,225,.35);background:rgba(255,255,255,.06);border-radius:7px;padding:12px 13px}}
.cover .cm b{{display:block;font-family:Georgia,serif;font-size:25px;color:#fff;line-height:1.1}}
.cover .cm span{{display:block;font-size:10px;color:#A9D5E2;line-height:1.4;margin-top:5px}}
.cover .cfoot{{margin-top:24px;display:flex;justify-content:space-between;font-size:10px;color:#8FB6CB;position:relative;z-index:2}}
.chapter{{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:32px 38px;margin:0 0 26px;box-shadow:0 2px 10px rgba(18,59,92,.04)}}
.chap-head{{display:flex;gap:16px;align-items:flex-start;border-bottom:1px solid var(--line);padding-bottom:14px;margin-bottom:20px}}
.chap-num{{font-family:Georgia,serif;font-size:34px;color:#B9D3E1;line-height:1}}
.chap-en{{font-size:10px;letter-spacing:.16em;color:var(--cyan);font-weight:600;margin-bottom:2px}}
h2{{font-size:25px;line-height:1.3;margin:0;color:var(--navy);letter-spacing:-.01em}}
h3{{font-size:16px;color:var(--blue);margin:24px 0 8px;line-height:1.4}}
h4{{font-size:13px;color:var(--navy);margin:16px 0 6px}}
p{{margin:9px 0;font-size:14px}}
small,.small{{font-size:12px;color:var(--muted)}}
ul,ol{{margin:8px 0 10px;padding-left:22px}}
li{{margin:4px 0;font-size:13.5px}}
sup{{font-size:9px;color:var(--blue);font-weight:600}}
.lead{{font-size:15.5px;color:#284A5E;line-height:1.75}}
.keygrid{{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:18px 0}}
.keycard{{border:1px solid var(--line);border-top:3px solid var(--cyan);border-radius:7px;padding:12px 13px;background:#FBFDFE}}
.keycard .kv{{font-family:Georgia,serif;font-size:24px;color:var(--blue);line-height:1.1;font-weight:700}}
.keycard .kl{{font-size:11px;color:var(--muted);margin-top:3px}}
.keycard .kn{{font-size:10px;color:#8A9EAC;line-height:1.35;margin-top:5px}}
.plain{{border-left:4px solid var(--teal);background:#F1FAF8;padding:12px 15px;margin:16px 0;border-radius:0 6px 6px 0;font-size:13px;color:#2E5B51}}
.plain-t{{font-weight:700;color:#1E6B5E;margin-bottom:4px;font-size:12px;letter-spacing:.03em}}
.boundary{{border-left:3px solid var(--orange);background:#FFF8F0;padding:10px 14px;margin:14px 0;font-size:12px;color:#6E4A22;border-radius:0 5px 5px 0}}
.boundary b{{color:#A35A1E;margin-right:7px}}
.formula{{background:#F4F8FB;border:1px solid #D9E8F1;border-radius:6px;padding:12px 15px;margin:12px 0;font-family:"SFMono-Regular",Consolas,monospace;font-size:12.5px;color:#173F57;line-height:1.7;overflow-x:auto}}
.formula span{{display:block;font-family:"PingFang SC","Microsoft YaHei",sans-serif;color:var(--muted);font-size:11px;margin-top:4px}}
.tw{{width:100%;overflow-x:auto;margin:12px 0 18px}}
table{{width:100%;border-collapse:collapse;table-layout:fixed;font-size:11.5px;line-height:1.45}}
th{{background:var(--blue);color:#fff;text-align:left;padding:8px 8px;font-weight:600;vertical-align:bottom}}
td{{border-bottom:1px solid var(--line);padding:8px 8px;vertical-align:top;color:#34566B}}
tbody tr:nth-child(even) td{{background:#FAFDFE}}
.tbl-main{{min-width:680px}}
.tbl-small{{font-size:11px}}
.fig-svg{{width:100%;height:auto;display:block;border:1px solid var(--line);border-radius:7px;background:#fff;margin:8px 0 4px}}
.ax{{font-size:10px;fill:#6E8A9A;font-family:Arial,sans-serif}}
.lbl{{font-size:10px;fill:#34566B;font-family:"PingFang SC","Microsoft YaHei",sans-serif}}
.val{{font-size:10px;fill:#1F4E79;font-weight:700;font-family:Arial,sans-serif}}
.ann{{font-size:11px;fill:#A35A1E;font-weight:700;font-family:"PingFang SC","Microsoft YaHei",sans-serif}}
.figcap{{font-size:10.5px;color:var(--muted);margin:0 0 16px;line-height:1.45}}
.domain{{display:flex;align-items:stretch;gap:3px;margin:16px 0 6px;border-radius:7px;overflow:hidden;border:1px solid var(--line)}}
.domain div{{padding:13px 8px;text-align:center;font-size:11px;line-height:1.35;color:#fff}}
.domain .d1{{background:#7B6FA8;flex:1.5}}
.domain .d2{{background:#1F4E79;flex:2.2}}
.domain .d3{{background:#0E8FA8;flex:1.6}}
.domain .d4{{background:#2A9D8F;flex:1.6}}
.domain b{{display:block;font-size:12px;margin-bottom:2px}}
.domain small{{color:rgba(255,255,255,.8);font-size:9px}}
.arrowline{{display:grid;grid-template-columns:repeat(6,1fr);gap:6px;margin:12px 0}}
.arrowline div{{background:#F4F9FC;border:1px solid var(--line);border-radius:6px;padding:10px 9px;font-size:10.5px;line-height:1.4}}
.arrowline b{{display:block;color:var(--blue);font-size:11px;margin-bottom:2px}}
.tag{{display:inline-block;font-size:9px;padding:1px 6px;border-radius:10px;background:#E8F3F9;color:#1F4E79;font-weight:600;margin-right:3px}}
.tag.b{{background:#FFF2E2;color:#A35A1E}}
.tag.c{{background:#EEF0F8;color:#5A568A}}
.tag.g{{background:#F1F1F1;color:#666}}
.two{{display:grid;grid-template-columns:1fr 1fr;gap:16px}}
.note{{font-size:11px;color:var(--muted);line-height:1.5}}
.qbox{{background:#F7FBFD;border:1px solid #D9E8F1;border-radius:8px;padding:14px 16px;margin:14px 0}}
.qbox h4{{margin:0 0 6px;color:var(--blue)}}
.refs{{font-size:11px;line-height:1.55}}
.refs li{{margin:4px 0;color:#34566B}}
.refs .doi{{color:#6E8A9A;font-size:10px}}
.footer{{margin:30px 0 0;border-top:1px solid var(--line);padding-top:14px;font-size:10.5px;color:var(--muted);display:flex;gap:12px;align-items:center}}
.footer b{{color:var(--blue)}}
.footer .slogan{{margin-left:auto;color:#4D7C96}}
@media(max-width:900px){{.sidebar{{position:static;width:auto;height:auto;padding:14px 16px}}.sidebar nav{{display:none}}.sidebar .sf{{display:none}}main{{margin-left:0;padding:0 14px 30px}}.cover{{margin:0 -14px 20px;padding:32px 24px;min-height:0}}.cover h1{{font-size:36px}}.cover .cmetrics{{grid-template-columns:1fr 1fr}}.chapter{{padding:22px 18px}}.keygrid{{grid-template-columns:1fr 1fr}}.two{{grid-template-columns:1fr}}.cover .cfoot{{display:none}}}}
@media print{{
 @page{{size:A4;margin:14mm 13mm 16mm}}
 body{{background:#fff;font-size:10pt}}
 .sidebar{{display:none}}
 main{{margin:0;max-width:none;padding:0}}
 .cover{{min-height:267mm;margin:0 0 12mm;padding:24mm 18mm;page-break-after:always;border-radius:0}}
 .cover h1{{font-size:38pt}}
 .cover .cmetrics{{margin-top:24mm}}
 .chapter{{border:0;box-shadow:none;padding:0;margin:0 0 10mm;page-break-before:auto}}
 .chap-head{{page-break-after:avoid;page-break-inside:avoid}}
 h2,h3,h4{{page-break-after:avoid}}
 tr{{page-break-inside:avoid}}
 table{{font-size:8pt}}
 thead{{display:table-header-group}}
 th,td{{padding:4px 5px}}
 .tw{{overflow:visible}}
 .tbl-main{{min-width:0}}
 .fig-svg{{page-break-inside:avoid}}
 .plain,.boundary,.formula,.qbox{{page-break-inside:avoid}}
 .refs{{font-size:9.2px;line-height:1.35}}
 .refs li{{margin:1.5px 0}}
 .footer{{page-break-before:avoid;margin-top:8px;padding-top:8px}}
}}
</style>
</head>
<body>
<aside class="sidebar">
  <img class="logo" src="{LOGO_URI}" alt="宝锐生物 BIORI BIOTECH">
  <div class="st">TECHNICAL REVIEW</div>
  <div class="sn">PCR 检测体系灵敏度<br>机理与调整</div>
  <nav>
    <a href="#summary">阅读摘要</a>
    <a href="#s1">01 四个口径</a>
    <a href="#s2">02 串联模型</a>
    <a href="#s3">03 酶学参数</a>
    <a href="#s4">04 蛋白结构</a>
    <a href="#s5">05 配方</a>
    <a href="#s6">06 引物与探针</a>
    <a href="#s7">07 RT-qPCR</a>
    <a href="#s8">08 样本与抑制剂</a>
    <a href="#s9">09 正向/负向矩阵</a>
    <a href="#s10">10 临床灵敏度</a>
    <a href="#s11">11 验证设计</a>
    <a href="#s12">12 证据与缺口</a>
  </nav>
  <div class="sf">对外技术资料 · 2026-11<br>不绑定具体产品/货号</div>
</aside>

<main>
<section class="cover bior-header">
  <div class="ctop"><img src="{LOGO_URI}" alt="宝锐生物 BIORI BIOTECH"><span>BIORI BIOTECH · TECHNICAL REVIEW</span></div>
  <div class="kicker">PCR SENSITIVITY · FROM ENZYME KINETICS TO DETECTION THRESHOLD</div>
  <h1>PCR 检测体系灵敏度</h1>
  <h2>从酶促反应、蛋白结构到检测阈值的机理与调整</h2>
  <div class="scope">TaqMan qPCR 为主 · RT-qPCR 单列一章 · LOD95 / E / Ct / 临床灵敏度并置 · 不绑定具体货号</div>
  <div class="cmetrics">
    <div class="cm"><b>95%</b><span>LOD95 的判定概率<br>不是临床灵敏度</span></div>
    <div class="cm"><b>90–110%</b><span>扩增效率 E 的<br>常用参考窗口</span></div>
    <div class="cm"><b>≈3 copies</b><span>Poisson 95% 理论交点<br>不含提取/抑制损失</span></div>
    <div class="cm"><b>89–94%</b><span>临床灵敏度文献示例<br>随人群/参考标准变化</span></div>
  </div>
  <div class="cfoot"><span>2026-11-02 · V01</span><span>本资料用于技术交流，不构成产品性能承诺</span></div>
</section>

<section class="chapter" id="summary">
  <div class="chap-head"><div class="chap-num">00</div><div><div class="chap-en">READING SUMMARY</div><h2>阅读摘要：先定口径，再看链条</h2></div></div>
  <p class="lead">PCR 的“灵敏度”不是单一酶活指标，而是一条串联系统的总结果：样本与取样先设定统计下限，引物/探针决定能否特异退火，聚合酶决定能否高效延伸，5′核酸酶决定每个扩增子产出多少信号，仪器与阈值决定最终判读。酶学、配方与引探设计分别作用于这条链的不同节点。</p>
  <div class="keygrid">
    <div class="keycard"><div class="kv">1</div><div class="kl">先定口径</div><div class="kn">LOD95 是管内概率；E 是每轮放大倍数；Ct 是达到阈值的轮数；临床灵敏度是人群中的 TP/(TP+FN)。</div></div>
    <div class="keycard"><div class="kv">2</div><div class="kl">直接参数</div><div class="kn">kcat/Km(dNTP)、processivity、延伸速率、5′核酸酶切割效率、热失活半衰期。</div></div>
    <div class="keycard"><div class="kv">3</div><div class="kl">间接参数</div><div class="kn">游离 Mg²⁺、dNTP/Mg 比、离子强度、pH、添加剂、Tm/ΔG、二级结构、扩增子长度、抑制剂。</div></div>
    <div class="keycard"><div class="kv">4</div><div class="kl">不要跨口径</div><div class="kn">LOD 低不等于临床灵敏度高；临床灵敏度还取决于人群、疾病谱、采样与参考标准。</div></div>
  </div>
  <div class="plain"><div class="plain-t">给客户的一句话</div>“我们先把‘灵敏度’拆成 LOD95、扩增效率、Ct 和临床灵敏度四个口径，再分别看酶、配方和引探各自作用在哪一段；这样讨论才不会把管内性能和临床性能混在一起。”</div>
  {boundary('本报告中的文献数值来自不同实验体系，不能直接当作某一产品的承诺值；所有“参考窗口”都需要在客户自己的模板、仪器和样本基质上复验。')}
</section>

{section('1','一、四个口径：LOD95、E、Ct、临床灵敏度','FOUR METRICS', f'''
<p class="lead">同一个“灵敏度”常被用来指四件不同的事。把它们分开，是后面所有机理讨论的前提。</p>
{t_metrics}
<h3>1.1 LOD95：最低能可靠检出多少</h3>
<p>LOD 是“在给定概率下可检出”的最低量，qPCR 中常用 95% 检出概率（LOD95）。CLSI EP17 与 qPCR 专用统计方法都强调：低浓度端不是线性正态分布，不能直接用普通均值±SD；应按重复实验的检出率做 logistic 回归或概率拟合。{c(3)}</p>
{formula('LoB = mean(blank) + 1.645 × SD(blank)<br>LoD = LoB + 1.645 × SD(low)<br>LOD95：检出率 = 95% 的最低浓度', 'CLSI EP17 形式；qPCR 低浓度端需用重复检出率/逻辑回归，不宜直接套用线性公式。')}
<h3>1.2 扩增效率 E：每轮放大多少</h3>
{formula('E = 10^(−1/slope) − 1', 'slope 为标准曲线 Ct 对 log10(拷贝数) 的斜率。')}
{t_e}
<p>E 接近 100% 时，模板每轮翻倍；E 偏低说明反应受到抑制、引探不佳或酶效率不足。E 高不等于 LOD 低：低拷贝还受取样、回收和阈值判定影响。{c(1,3)}</p>
<h3>1.3 Ct：达到阈值要几轮</h3>
<p>Ct（Cq）与初始拷贝数的对数成反比。100% 效率下，1 个 Ct 约对应 2 倍模板；90% 效率下约 1.9 倍。Ct 提前可以是模板更多、效率更高，也可能是背景更高或非特异扩增，不能单独当作灵敏度。{c(1)}</p>
<h3>1.4 临床灵敏度：人群中的检出比例</h3>
{formula('临床灵敏度 = TP / (TP + FN)', 'TP：真阳性；FN：假阴性。它依赖患病率、疾病谱、采样与参考标准。')}
<p>临床灵敏度不是 LOD 的同义词。一个 LOD 很低的 assay，如果在目标人群里采样不到位、基质抑制强或疾病早期靶标不存在，临床灵敏度仍可能不高。{c(19)}</p>
{plain('客户解释', '“LOD95 回答的是‘管里最低能测到多少’，临床灵敏度回答的是‘在目标人群里能查出多少病人’。前者是分析性能，后者是诊断性能，必须分开验证。”')}
''')}

{section('2','二、串联模型：从样本到阈值','FROM SAMPLE TO THRESHOLD', f'''
<p class="lead">把 PCR 看成一条串联链：任何一段的损失都会向后传递；低拷贝时，最前面的取样与统计往往先设定下限。</p>
<div class="arrowline">
  <div><b>01 取样/回收</b>有效拷贝数、提取回收率、基质抑制</div>
  <div><b>02 变性/可及</b>GC、二级结构、片段完整性</div>
  <div><b>03 退火</b>Tm/ΔG、Ta、引物二聚体</div>
  <div><b>04 延伸</b>kcat/Km、processivity、延伸速率</div>
  <div><b>05 切割</b>5′核酸酶、探针可及性</div>
  <div><b>06 读出</b>背景、淬灭、阈值算法</div>
</div>
{formula('P_detect ≈ P_sample × P_access × P_anneal × P_extend × P_cleave × P_read', '概念式：各段不是独立可乘的严格概率，但用于说明“任一环节损失都会拉低总检出率”。')}
<h3>2.1 低拷贝的统计下限</h3>
<p>当每反应平均拷贝数为 λ 时，至少有一个拷贝的概率服从 Poisson：P = 1 − e^(−λ)。95% 检出对应 λ ≈ 3.0；这就是“约 3 个拷贝”的理论来源，不包含提取损失、抑制和扩增失败。{c(3)}</p>
{t_poisson}
{poisson_svg()}
<div class="figcap">图 1｜Poisson 取样概率：λ≈3.0 时达到 95% 检出；这是理论下限，不是任何 assay 的实测 LOD。</div>
<h3>2.2 取样体积与回收率</h3>
<p>若样本浓度为 1 copy/µL、取样 5 µL，平均 λ=5；但提取回收 50% 后 λ≈2.5，95% 检出已不成立。增加取样体积、提高回收率、减少抑制，往往比单纯提高酶量更直接。{c(3,11)}</p>
{boundary('“LOD 1 copy”必须写清是每反应、每 µL、还是每采样体积；不写口径的 LOD 不可比较。')}
''')}

{section('3','三、酶学：哪些参数直接决定延伸与切割','ENZYME KINETICS', f'''
<p class="lead">酶学的“直接参数”是催化循环内的可测量：底物结合、催化速率、持续合成、探针切割和热失活。它们决定“同样模板能不能在有限循环内达到阈值”。</p>
{formula('v = Vmax[S] / (Km + [S]) = kcat[E][S] / (Km + [S])', '低底物时 v ≈ (kcat/Km)[E][S]；kcat/Km 常比单独 kcat 更接近“低浓度下的效率”。')}
{t_enzyme}
<h3>3.1 kcat/Km 与 processivity 的分工</h3>
<p><b>kcat/Km</b> 描述低底物浓度下的催化效率，受 dNTP 结合、金属离子、模板匹配和构象闭合影响；<b>processivity</b> 描述一次结合能连续掺入多少核苷酸，决定长扩增子或低拷贝模板能否“一次走完”。两者不是同一个参数：酶可以很快但容易脱落，也可以很持续但催化慢。{c(7)}</p>
<h3>3.2 Taq 的实测与工程化数据</h3>
<ul>
<li>Taq 的延伸速率约 <b>2 kb/min</b>（≈33 nt/s）；CL7-Taq 融合酶约 <b>4 kb/min</b>（≈67 nt/s），并报告 <b>100×</b> 灵敏度提升。{c(10)}</li>
<li>理性设计 Taq 的 N 端核酸酶结构域（L208I/E209K/E210R）可使聚合酶活性提高约 <b>5×</b>；在特定 GM 作物 qPCR 中 LOD 约 <b>0.1 copies/µL</b>，并耐受 <b>30%</b> 粗植物裂解液。{c(9)}</li>
<li>Taq 的 5′核酸酶切割效率可用 PC/PY（切割量/产物量）衡量：Taq-wt <b>77.39%</b>，Taq388 <b>91.96%</b>，TM-Taq <b>88.19%</b>；dNTP 存在时切割效率显著下降，说明聚合酶活性优先于核酸酶活性。{c(8)}</li>
</ul>
{cleavage_svg()}
<div class="figcap">图 2｜Taq 变体的探针切割效率（PC/PY，三基因平均）。数据来自厂商主导研究，作为机制参考，不代表独立产品比较。{c(8)}</div>
<h3>3.3 TaqMan 的机制要点</h3>
<p>TaqMan 不是“探针一结合就被切”：聚合酶先延伸，遇到探针时通过 5′→3′ 核酸酶/5′flap 内切与链置换协同切割；dNTP 充足时聚合酶活性占优，切割被压低。KlenTaq 缺失 5′核酸酶结构域，不能有效切割，但链置换更强，约 <b>97%</b> 探针保持完整，而 Taq-wt 仅约 <b>4%</b>。{c(8)}</p>
{plain('客户解释', '“TaqMan 的信号不是酶越多越强，而是延伸和切割要配合。延伸太强、dNTP 太高，反而可能压低切割信号；这就是为什么有些体系‘酶活很好，但 TaqMan 信号不成比例’。”')}
{boundary('TaqMan 切割数据来自特定探针/模板/基因体系；不能直接外推到所有探针序列和所有 qPCR 仪器。')}
''')}

{section('4','四、蛋白结构：结构域如何映射到动力学','PROTEIN STRUCTURE', f'''
<p class="lead">Taq 属于 A 家族 DNA 聚合酶：N 端 5′核酸酶结构域、手掌（palm）催化中心、手指（fingers）dNTP 结合、拇指（thumb）DNA 结合。结构变化最终要落到“结合、催化、持续合成、切割、稳定性”五个可测参数上。</p>
<div class="domain">
  <div class="d1"><b>5′核酸酶</b><small>N 端 ~289 aa<br>探针切割/链置换</small></div>
  <div class="d2"><b>手掌 palm</b><small>催化中心<br>二价金属离子</small></div>
  <div class="d3"><b>手指 fingers</b><small>dNTP 结合<br>诱导契合</small></div>
  <div class="d4"><b>拇指 thumb</b><small>DNA 结合<br>持续合成</small></div>
</div>
<div class="figcap">图 3｜Taq 结构域示意（非按比例）。结构坐标参考 PDB 1TAU / 1TAQ。{c(20)}</div>
{t_struct}
<h3>4.1 构象动力学</h3>
<p>单分子研究显示，Taq 的手指结构域在 dNTP 结合时发生快速瞬态闭合（约 20 µs 量级）；即使互补底物，每次催化掺入前也平均有约 5 次瞬态闭合。温度敏感性主要来自“开放态”的速率，而不是闭合/催化本身。{c(6)} 这解释了为什么“提高温度”不只加快化学步骤，也改变蛋白构象 ensemble。</p>
<h3>4.2 结构改造的取舍</h3>
<p>缺失 5′核酸酶（KlenTaq）→ 切割消失、链置换增强；融合 DNA 结合蛋白（Sso7d/CL7）→ processivity、延伸速率、抗抑制提高；电荷突变（E507K）→ DNA 结合/抗抑制改善，但在某些体系中切割效率反而下降。{c(8,9,10)} 结构改造很少只改善一个参数，通常同时改变速度、保真度、持续合成和稳定性。</p>
{boundary('结构示意用于解释机制，不等同于高分辨率结构解析；具体突变的效果必须在目标模板与缓冲体系中实测。')}
''')}

{section('5','五、配方：游离 Mg²⁺、dNTP 与添加剂','FORMULATION', f'''
<p class="lead">配方的作用不是“给酶加营养”，而是改变酶与底物的有效状态：游离 Mg²⁺ 决定催化可用性，dNTP 既是底物又螯合 Mg²⁺，离子强度改变 DNA 结合，添加剂改变变性与二级结构。</p>
{t_form}
<h3>5.1 Mg²⁺ 与 dNTP 是联动变量</h3>
<p>总 Mg²⁺ 不等于游离 Mg²⁺。每个 dNTP 都螯合 Mg²⁺；dNTP 从 0.2 mM 提高到 0.4 mM（每种）时，总 dNTP 增加约 0.8 mM，会显著改变游离 Mg²⁺。因此“Mg²⁺ 最优”必须和 dNTP 浓度一起定义。{c(22,23)}</p>
{formula('游离 Mg²⁺ ≈ 总 Mg²⁺ − 被 dNTP/EDTA/柠檬酸等螯合的部分', '实操上以 Mg²⁺ 滴定 + E/Ct 响应找窗口，不靠单一公式。')}
<h3>5.2 添加剂：有正向，也有明确负向</h3>
<p>在一项实时荧光延伸速率实验中，DMSO 可使延伸速率最多提高 <b>33%</b>；甘油影响很小；甜菜碱、甲酰胺和 1,2-丙二醇随浓度升高而<b>降低</b>延伸速率；一价阳离子（Li⁺/Na⁺/K⁺/Cs⁺/NH₄⁺）持续<b>降低</b>延伸速率。{c(5)} 这与“添加剂越多越能救体系”的直觉相反：它们改变的是热力学/离子环境，存在明确的最优窗口。</p>
<h3>5.3 抗抑制：BSA 的定量例子</h3>
<p>野生型 Taq 在 <b>0.2%</b> 血液中即完全被抑制；加入 <b>0.6% BSA</b> 后可在 <b>2%</b> 血液中扩增。{c(10)} 这是“配方通过间接参数（酶吸附/抑制剂隔离）改善灵敏度”的典型例子，但 BSA 不能替代提取，也不能保证所有基质。</p>
{plain('客户解释', '“配方没有万能最优。Mg²⁺、dNTP、盐和添加剂是一个联动矩阵；我们通常先固定引探，再做 Mg²⁺×dNTP 的小矩阵，最后才看添加剂，避免一次改太多变量。”')}
''')}

{section('6','六、引物与探针：Tm 不是越高越好','PRIMERS & PROBES', f'''
<p class="lead">引探设计直接作用于“退火占据率、特异性、探针切割”。最关键的不是预测 Tm，而是实验退火温度 Ta 与缓冲体系的匹配。</p>
{t_oligo}
<h3>6.1 Ta 比 Tm 更关键</h3>
<p>引物设计综述明确指出：决定引物性能的关键变量是退火温度 Ta，而不是 Tm；最优 Ta 必须实验确定，且不同 master mix 之间不能直接外推。引物二聚体、模板二级结构、BLAST 未覆盖的错配杂合都会造成假阴性或非特异。{c(4)}</p>
<h3>6.2 探针设计的两难</h3>
<p>探针 Tm 高一些有利于延伸阶段保持结合，但过高会降低错配区分度；过低则信号弱、切割不足。常用“探针 Tm 高于引物 5–10℃”是设计起点，不是硬性保证；MGB/LNA 可提高 Tm，但也可能压缩区分窗口。{c(23,4)}</p>
<h3>6.3 扩增子长度的权衡</h3>
<p>短扩增子受 processivity 限制小、效率通常更高；但过短可能降低特异性或限制探针位置。qPCR 常用 70–200 bp，TaqMan 常 50–150 bp；最终以标准曲线 E 和 LOD95 验证。{c(4,8)}</p>
{plain('客户解释', '“Tm 只是设计起点。真正决定体系的是 Ta、缓冲体系和引探二级结构；同一对引物换一个 master mix，最优 Ta 可能就变了。”')}
''')}

{section('7','七、RT-qPCR：逆转录步骤可能先决定灵敏度','RT-qPCR', f'''
<p class="lead">RT-qPCR 比 qPCR 多了一段逆转录。对 RNA 靶标，RT 的 processivity、热稳定性、RNase H 活性和 RNA 二级结构，可能在 Taq 之前就设定了下限；因此本章单列，不混入 DNA qPCR 的结论。</p>
{t_rt}
<h3>7.1 一步法与两步法</h3>
<p>一步法减少转移损失、适合低拷贝与高通量；两步法可分别优化 RT 与 qPCR，适合困难 RNA 或需要独立控制 RT 温度的体系。两者没有绝对优劣，应以 LOD95 与重复性比较。{c(1,2)}</p>
<h3>7.2 耐热 RT 的意义</h3>
<p>提高 RT 反应温度可以打开 RNA 二级结构，但普通 RT 在高温下失活；耐热 RT 的理性设计（例如基于 Pfu  DNA 聚合酶骨架）是当前一条工程路线。{c(12)} 对客户体系，先判断“RT 限制”还是“qPCR 限制”：可用相同 RNA 输入比较一步法/两步法与不同 RT 温度。</p>
{boundary('RT-qPCR 的 LOD 不能直接由 DNA qPCR 的酶学数据推算；RT 步骤、RNA 完整性和 DNase 处理都要单独验证。')}
''')}

{section('8','八、样本基质与抑制剂：把“管内性能”拉回现实','SAMPLE MATRIX', f'''
<p class="lead">临床样本很少是纯水。血液、肝素、EDTA、SDS、腐殖酸、胍盐和乙醇都会通过不同机制降低有效酶活或荧光信号；这部分常比酶改造更决定实测 LOD。</p>
{t_inhib}
<h3>8.1 抑制的三种表现</h3>
<ul>
<li><b> Ct 延后/斜率变平：</b>扩增效率下降，低拷贝先漏检。</li>
<li><b>荧光基线抬高/淬灭：</b>信号被基质吸收或淬灭，阈值判定失真。</li>
<li><b>提取回收下降：</b>不是酶的问题，但表现为“灵敏度不够”。</li>
</ul>
<h3>8.2 处理顺序</h3>
<p>先判断是“抑制”还是“回收损失”：加标回收、稀释线性、内参与基质匹配标准曲线是三种最常用的排查。BSA、稀释、提取纯化和耐抑制酶都可作为手段，但顺序应先做基质排查，再改配方。{c(10,11)}</p>
{plain('客户解释', '“如果同一个体系在纯模板里 LOD 很好、到血清/粪便里变差，优先查基质抑制和提取回收，而不是先换酶。酶改造只能解决一部分抑制。”')}
''')}

{section('9','九、正向/负向调整矩阵：每个动作都有代价','LEVERS & TRADE-OFFS', f'''
<p class="lead">下表把“酶—配方—引探—样本”四类调整放在同一张矩阵里。方向箭头是机制上的预期，不是保证；最后一列是必须验证的指标。</p>
{t_intervention}
<h3>9.1 三条硬规则</h3>
<ol>
<li><b>先定口径：</b>改一个参数后，至少同时看 E、Ct 和 LOD95；只看 Ct 提前可能把背景/非特异误判为灵敏度提高。</li>
<li><b>一次只动一组：</b>Mg²⁺、dNTP、酶量、Ta 之间存在交互，单因素最优不等于组合最优。</li>
<li><b>把负向写进方案：</b>每个正向动作都配一个反证指标（阴性对照、熔解曲线、测序/电泳、基质加标）。</li>
</ol>
{boundary('矩阵中的“正向/负向”是条件依赖的：同一改动在不同模板、不同缓冲体系、不同仪器上可能反转。')}
''')}

{section('10','十、临床灵敏度：真实数据与参考区间','CLINICAL SENSITIVITY', f'''
<p class="lead">临床灵敏度是诊断准确性指标，不是酶学参数。下面列出已发表的真实数据，用来说明“量级与异质性”，不是给某一产品定目标。</p>
{t_clin}
{clinical_svg()}
<div class="figcap">图 4｜已发表临床灵敏度示例（点估计与 95% CI）。不同疾病、参考标准和人群不可直接横向比较。{c(14,15,16,17,18)}</div>
<h3>10.1 为什么不能用一个数概括</h3>
<p>同一检测方法在不同人群、不同采样部位、不同参考标准下，临床灵敏度会变化。例如 SARS-CoV-2 鼻咽拭子 RT-PCR 的合并灵敏度为 89.0%（85.4–91.8），这是 13 项研究、25 个方案的合并值，不代表每一个 assay。{c(14)} MRSA PCR 的合并灵敏度为 92.5%（87.4–95.9），参考标准是培养。{c(16)} HPV DNA 在 HIV 感染女性中 CIN2+ 的合并灵敏度为 91.6%（88.1–94.1）。{c(17)}</p>
<h3>10.2 参考数值怎么用</h3>
<ul>
<li><b>作为量级参考：</b>上述传染/肿瘤筛查场景多在 89–94% 区间；不是“合格线”。</li>
<li><b>作为对照设计：</b>临床验证应报告目标人群、参考标准、采样部位、阳性/阴性例数，而不是只报一个百分比。</li>
<li><b>与 LOD 分开：</b>LOD95 是分析性能；临床灵敏度要按 STARD 类框架做队列验证。{c(19)}</li>
</ul>
{formula('PPV = TP / (TP + FP)；NPV = TN / (TN + FN)', 'PPV/NPV 随患病率变化；同一 assay 在不同流行率下解释不同。')}
{plain('客户解释', '“文献里 qPCR 的临床灵敏度常见在 89–94% 这个量级，但这不是标准线。我们要看它测的是哪个人群、用什么参考标准、采样是否到位；这些条件变了，数字就会变。”')}
''')}

{section('11','十一、验证设计：把机理变成可验收的数据','VALIDATION & DOE', f'''
<p class="lead">对外解释需要“可复现的证据包”。验证可分为四层：分析灵敏度、扩增效率、条件矩阵、临床/基质验证。</p>
{t_doe}
<h3>11.1 最小证据包</h3>
<ul>
<li><b>LOD95：</b>至少 3 个低浓度、20 重复、3 次运行；报告 95% 检出浓度与置信区间。</li>
<li><b>E：</b>5 个 10 倍稀释点、3 重复、至少 2 次独立运行；报告 slope、E、R²。</li>
<li><b>条件矩阵：</b>Mg²⁺ × dNTP、Ta × 引探、酶量 × 基质；不要只做单因素。</li>
<li><b>临床/基质：</b>目标人群、参考标准、盲法、样本量；遵循 STARD/MIQE 报告。{c(1,19)}</li>
</ul>
{boundary('LOD95 的重复数和浓度点不足，会系统性高估灵敏度；只报“最低检出浓度”而不报检出率/重复数，不可比较。')}
''')}

{section('12','十二、证据表与缺口','EVIDENCE & GAPS', f'''
<p class="lead">本报告优先使用同行评审文献、结构数据库与方法学指南；厂商资料只用于“参考窗口”，并单独标注。下表是可追溯的证据清单。</p>
{t_evidence}
<h3>12.1 主要缺口</h3>
<ul>
<li><b>同一 qPCR 条件下的酶变体动力学数据少：</b>多数 kcat/Km/延伸速率来自引物延伸或体外荧光体系，不能直接等同 LOD。{c(5,7)}</li>
<li><b>配方最优值高度体系依赖：</b>Mg²⁺/dNTP/添加剂没有普适最优；必须做矩阵。{c(22,23)}</li>
<li><b>临床灵敏度不能由 LOD 推算：</b>需要目标人群与参考标准；本报告只给文献示例，不给产品承诺。{c(19)}</li>
<li><b>厂商主导的 TaqMan 切割数据需独立互证：</b>本报告将其标为 B*，只用于机制解释。{c(8)}</li>
</ul>
<h3>12.2 参考文献</h3>
<ol class="refs">
{''.join(f'<li>{r[0]} <span class="doi">DOI/ID: {r[1]}｜等级 {r[2]}</span></li>' for r in REFS)}
</ol>
<div class="footer bior-footer"><b>BIORI BIOTECH</b><span>珠海宝锐生物科技有限公司 · 0756-8699969 · marketing@biori.com</span><span class="slogan">守护生命健康，缔造美好生活</span></div>
''')}
</main>
</body>
</html>"""

OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(HTML, encoding='utf-8')
print('written', OUT, OUT.stat().st_size, 'refs', len(REFS))
