#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""全预混低温保存案例卡 v2 HTML（占位符替换，不用 % 格式化）"""
import json
import os
import sys

sys.path.insert(0, '/Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本')
import brand_kit as BK  # noqa: E402

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
d = json.load(open(os.path.join(W, '数据/premix_cases_v2.json'), encoding='utf-8'))
OUT = os.path.join(W, '销售分析/MD2091_全预混保存_客户案例卡_2026-10-20.html')
cases = d['cases']

TAG_CLS = {'2-8℃': 'k28', '4℃': 'k4', '24℃': 'khot', '室温': 'kroom', '-20℃': 'km20', '18个月': 'km20',
           '1个月': 'k1m', '3个月': 'k1m', '1周': 'k1m', '短时': 'k1m', '模拟运输': 'k1m', '冻融6次': 'kfz',
           '冻融': 'kfz', '全预混': 'kmix', '不冻结': 'kfz', '37℃加速': 'kacc', '加速': 'kacc',
           '正面': 'kpos', '负面': 'kneg', '需求': 'kwarn', '进行中': 'kwait', '温度未给': 'kwarn',
           '8℃运输': 'k4', '12个月': 'km20'}

n_clients = len({c['cust'] for c in cases})
n_pos = sum(1 for c in cases if '正面' in c['tags'])
n_neg = sum(1 for c in cases if '负面' in c['tags'])
n_28 = sum(1 for c in cases if '2-8℃' in c['tags'] or '4℃' in c['tags'])
n_m20 = sum(1 for c in cases if '-20℃' in c['tags'])

cards = []
for c in cases:
    tags = ''.join('<span class="tag ' + TAG_CLS.get(t, '') + '">' + t + '</span>' for t in c['tags'])
    cards.append(
        '<article class="case" data-tags="' + ' '.join(c['tags']) + '">'
        '<div class="ch"><div><div class="cust">' + c['cust'] + '</div>'
        '<div class="sub">' + c['title'] + ' ｜ <span class="sku">' + c['sku'] + '</span></div></div>'
        '<div class="date">' + c['date'] + '</div></div>'
        '<div class="tags">' + tags + '</div>'
        '<p class="verdict">' + c['verdict'] + '</p>'
        '<div class="meta"><b>条件</b>' + c['cond'] + '</div>'
        '<blockquote>' + c['quote'] + '</blockquote>'
        '<div class="foot"><span>记录人：' + (c['owner'] or '—') + '</span>'
        '<span>活动 ID：' + (c['wid'] or '—') + '</span>'
        '<span class="strength">证据强度：' + c['strength'] + '</span></div>'
        '<div class="risk"><b>引用边界</b>' + c['risk'] + '</div></article>')

MATRIX = [
 ('苏州天隆', '✓', '✓ 003003 / 003006 做到 1 个月', '? RNA 可妥协，无数据', '本页最强客户'),
 ('青岛嘉智', '✓', '✗ MD2091 检出不如 095203', '—', '4℃ 1 个月是终端硬指标'),
 ('上海之江', '✓', '△ 4℃ 三个月，待测', '—', '刚提需求'),
 ('圣湘生物', '✓', '✗ 多轮 4℃ 预混不达标', '△ −20+5℃ 已送测', '4℃ 是明确短板'),
 ('洛阳普泰', '✓', '—', '△ 要 −20℃ 18 个月数据', '数据未给'),
 ('龙阔（苏州）', '✓', '✗ 37℃ 1 个月加速也做不到', '✗ 我方两体系不满足', '需求最严'),
 ('湖南冠牧', '✓', '—', '✓ −20℃ 保存 OK；冻融循环 ✗', '效期 1 年'),
 ('广州维伯鑫', '✓', '—', '✓ 3 个月（未到 1 年）', '进行中'),
 ('广州易安', '✓', '—', '✓ 1 周（短时）', '短时'),
 ('上海美吉', '✓', '✓ 2–8℃ 2/4/6h（短时）', '✗ −20℃ 24h 检不出', '孔间差异待排查'),
 ('江苏硕世', '△ 单管', '✗ 24℃ 5d 掉 70%', '—', '属高温场景'),
 ('上海万子健', '✓', '△ 室温 12 个月', '—', '含引探'),
 ('苏州翊创', '△ 微流控', '—', '△ −20℃ 保存 / 8℃ 运输', '储存与运输分离'),
 ('宁波海尔施', '✓', '? 温度未给', '? 温度未给', 'MD2091 三款最好'),
 ('武汉正大康瑞', '✓', '—', '—', '37℃ 7D 通过'),
]
mrow = []
for r in MATRIX:
    cls = 'ok' if r[1].startswith('✓') else ('no' if r[1].startswith('✗') else 'mid')
    mrow.append('<tr><td class="c">' + r[0] + '</td><td class="ctr ' + cls + '">' + r[1] + '</td>'
                '<td>' + r[2] + '</td><td>' + r[3] + '</td><td class="note">' + r[4] + '</td></tr>')
mrow = ''.join(mrow)

HTML = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>全预混低温保存 · 客户案例卡</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#f2f5fa;color:#1b2430;font-family:-apple-system,"PingFang SC","Helvetica Neue",sans-serif;font-size:14px;line-height:1.65}
.wrap{max-width:1260px;margin:0 auto;padding:0 16px 40px}
h1{font-size:23px;margin:16px 0 4px;letter-spacing:-.3px}
.lede{color:#5f6b7a;font-size:13px;margin-bottom:12px}
.gap{background:#FDECEC;border:1px solid #F3B9B9;border-left:5px solid #C62828;border-radius:9px;padding:11px 14px;margin:10px 0 14px}
.gap b{color:#B42318}
.gap ul{margin:6px 0 0 18px;padding:0;font-size:12.7px;color:#7a2b2b}
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}
.kpi{background:#fff;border:1px solid #e2e8f2;border-radius:10px;padding:11px 13px}
.kpi .t{font-size:12px;color:#5f6b7a}.kpi b{display:block;font-size:23px;color:#1852B6;line-height:1.2;margin-top:2px}
.kpi span{font-size:11.5px;color:#8a94a3}
.bar{display:flex;flex-wrap:wrap;gap:7px;margin:6px 0 14px}
.chip{border:1px solid #d7e0ee;background:#fff;color:#33415a;border-radius:999px;padding:5px 13px;font-size:12.5px;cursor:pointer}
.chip.on{background:#1F4E79;border-color:#1F4E79;color:#fff}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.case{background:#fff;border:1px solid #e2e8f2;border-radius:11px;padding:13px 15px;display:flex;flex-direction:column}
.ch{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap}
.cust{font-size:15px;font-weight:800;color:#0E2E63}
.sub{font-size:12.2px;color:#5f6b7a;margin-top:2px}.sku{color:#1852B6;font-weight:600}
.date{font-size:12px;color:#8a94a3;white-space:nowrap;font-variant-numeric:tabular-nums}
.tags{margin:8px 0 6px;display:flex;flex-wrap:wrap;gap:5px}
.tag{font-size:10.8px;font-weight:700;border-radius:4px;padding:1.5px 7px;background:#EEF1F6;color:#4b5768}
.k28{background:#FDE7E7;color:#A42020}.k4{background:#FDEBE0;color:#9A4A12}.khot{background:#F6E4DC;color:#8A3A12}
.kroom{background:#EFE9F7;color:#4B2E83}.km20{background:#E3EEF9;color:#0F4C81}.k1m{background:#E7F2EA;color:#1B6A3A}
.kfz{background:#EAF3F7;color:#215A72}.kmix{background:#E4EEFB;color:#14497F}.kacc{background:#E8F0FE;color:#1B4EA8}
.kpos{background:#E4F5EA;color:#1B7A43}.kneg{background:#FBE3E3;color:#8C1D1D}.kwarn{background:#FFF3DA;color:#8A5A00}
.kwait{background:#EFEFEF;color:#5c5c5c}
.verdict{margin:4px 0 8px;font-size:13.6px;font-weight:600;color:#22303f}
.meta{font-size:12.4px;color:#44505f;background:#F7F9FC;border-radius:7px;padding:6px 9px;margin-bottom:8px}
.meta b{color:#1852B6;margin-right:6px}
blockquote{margin:0 0 9px;padding:8px 11px;border-left:3px solid #B9CCE6;background:#FAFBFD;color:#3b4757;font-size:12.3px;line-height:1.62;border-radius:0 7px 7px 0}
.foot{display:flex;flex-wrap:wrap;gap:12px;font-size:11.4px;color:#7b8797;margin-top:auto}
.strength{color:#1852B6;font-weight:600}
.risk{margin-top:8px;font-size:12.1px;color:#7a5211;background:#FFF9EC;border:1px solid #F3E3BF;border-radius:7px;padding:6px 9px}
.risk b{color:#8A5A00;margin-right:6px}
.sec{margin-top:20px;background:#fff;border:1px solid #e2e8f2;border-radius:11px;padding:14px 16px}
.sec h2{font-size:15.5px;margin:0 0 8px;padding-left:9px;border-left:3px solid #1F4E79}
.sec ol,.sec ul{margin:6px 0 0 20px;padding:0}.sec li{margin-bottom:7px;font-size:13px}
.note{background:#FFF6E5;border:1px solid #F3D9A6;border-radius:8px;padding:10px 12px;font-size:12.6px;margin-top:10px}
table.mx{width:100%;border-collapse:collapse;font-size:12.3px}
table.mx th{background:#1F4E79;color:#fff;padding:7px 8px;text-align:left;font-weight:600}
table.mx td{padding:6px 8px;border-bottom:1px solid #eef1f6;vertical-align:top}
table.mx td.c{font-weight:700;color:#0E2E63;white-space:nowrap}
table.mx td.ctr{font-weight:700;white-space:nowrap;text-align:center}
table.mx td.ok{color:#1B7A43}table.mx td.no{color:#B42318}table.mx td.mid{color:#8A5A00}
table.mx td.note{color:#7b8797;font-size:11.6px}
.tw{overflow-x:auto;-webkit-overflow-scrolling:touch;border-radius:8px}
.empty{display:none;padding:24px;text-align:center;color:#8a94a3;font-size:13px}
@media(max-width:860px){.grid{grid-template-columns:1fr}.kpis{grid-template-columns:1fr 1fr}h1{font-size:20px}}
@media(max-width:900px){.bior-tagwrap{display:none!important}}
.case,.meta,.foot,blockquote{min-width:0;overflow-wrap:anywhere}
</style></head><body><div class="wrap">
<h1>全预混低温保存 · 客户案例卡</h1>
<div class="lede">口径：全预混＝酶 + buffer + 引探（含模板）混合后保存；三档条件＝2–8℃（或 4℃）≥1 个月、−20℃ ≥1 年、附加冻融 3–6 次与 37℃ 加速。收录活动库中提到具体条件或结论的记录，逐条附原文与活动 ID。</div>

<div class="gap"><b>三个缺口（对外沟通前必须知道）</b>
<ul>
<li><b>−20℃ ≥1 年：0 家客户有实测结论</b>，最长只有维伯鑫 3 个月、易安 1 周。</li>
<li><b>2–8℃ 1 个月：只有苏州天隆有正面事实</b>，但落在 MD2104-003003 / PM2104-003006；MD2091 那条只写「表现较好」，未写明温度条件。</li>
<li><b>同时满足三项的客户：0 家</b>；龙阔已明确判定我方现有体系不能确保全预混 1 个月稳定。</li>
</ul></div>

<div class="kpis">
  <div class="kpi"><div class="t">案例条目</div><b>__N__</b><span>涉及 __NC__ 家客户</span></div>
  <div class="kpi"><div class="t">2–8℃ / 4℃ 条件</div><b>__N28__</b><span>含短时与需求</span></div>
  <div class="kpi"><div class="t">−20℃ 条件</div><b>__N20__</b><span>最长实测 3 个月</span></div>
  <div class="kpi"><div class="t">正面 / 负面</div><b>__NP__ / __NN__</b><span>其余为需求或进行中</span></div>
</div>

<div class="bar">
  <button class="chip on" data-f="all">全部 __N__</button>
  <button class="chip" data-f="2-8℃">2–8℃</button>
  <button class="chip" data-f="4℃">4℃</button>
  <button class="chip" data-f="-20℃">−20℃</button>
  <button class="chip" data-f="1个月">≥1 个月</button>
  <button class="chip" data-f="18个月">1 年及以上</button>
  <button class="chip" data-f="冻融">冻融</button>
  <button class="chip" data-f="全预混">全预混</button>
  <button class="chip" data-f="正面">正面</button>
  <button class="chip" data-f="负面">负面</button>
  <button class="chip" data-f="进行中">进行中</button>
</div>

<div class="grid" id="grid">__CARDS__</div>
<div class="empty" id="empty">该筛选下暂无案例</div>

<div class="sec"><h2>客户 × 三条件矩阵</h2>
<div class="tw"><table class="mx"><thead><tr><th style="width:13%">客户</th><th style="width:11%">全预混形态</th>
<th style="width:24%">2–8℃ / 4℃ ≥1 个月</th><th style="width:22%">−20℃ ≥1 年</th><th>说明</th></tr></thead>
<tbody>__MATRIX__</tbody></table></div>
<div class="note">图例：✓ 有正面数据（含阶段结论）；✗ 有负面数据；△ 只有要求或进行中；— 未提及；? 未验证。同一客户不同 SKU 结论可能不同，详见上方卡片。</div></div>

<div class="sec"><h2>怎么用这份案例（给流水线项目组）</h2>
<ol>
<li><b>能讲的</b>：天隆 2–8℃ 1 个月（003003/003006）、维伯鑫 −20℃ 3 个月、易安 −20℃ 1 周、正大康瑞 37℃ 7D 通过。</li>
<li><b>不能讲的</b>：把「MD2091 表现较好」说成「MD2091 已通过 2–8℃ 1 个月」——原文没有这个条件；把「−20℃ 保存 OK」说成「−20℃ 1 年通过」——冠牧只做了储存、且冻融循环后失效。</li>
<li><b>建议话术</b>：先给客户看正大康瑞（加速）+ 天隆（2–8℃ 1 个月，不同 SKU）两段事实，再主动说明「−20℃ 1 年我们正在补数据」，把节奏握在自己手里。</li>
</ol></div>

<div class="sec"><h2>要补的数据包（建议本周启动）</h2>
<ul>
<li><b>体系</b>：全预混（酶 + buffer + 引探 + 模板）；<b>条件</b>：2–8℃、4℃、−20℃ 三档 × 0/7/14/30 天，−20℃ 加 6/12 个月节点；<b>附加</b>：冻融 3/6 次、室温 4h、37℃ 3d。</li>
<li><b>指标</b>：Ct 偏移、Rn 衰减率、低浓度检出下限（1 copies/μL 梯度）。</li>
<li><b>主责 / 验收</b>：技术支持出数据，产品复核，刘新元验收；结果回填本页矩阵，替换 ? 与 △。</li>
</ul></div>

<div class="sec"><h2>数据来源与时效</h2>
<div class="tw"><table class="mx"><thead><tr><th style="width:34%">源</th><th>覆盖窗口</th><th style="width:22%">最后更新</th><th style="width:26%">局限</th></tr></thead>
<tbody>
<tr><td>飞书活动库 <code>daily_activities_mcp.json</code>（6,677 条）</td><td>2026-07-01 ~ 2026-10-20</td><td>2026-10-20 21:11</td><td>只回溯到 7 月上旬；为销售/技术转述</td></tr>
<tr><td>客户特殊要求库（2,865 条）</td><td>2025 年起 ERP 订单备注</td><td>2026-09-29 07:05</td><td>无 MD2091 温度类登记</td></tr>
<tr><td>客诉库（370 条）</td><td>2026-01-07 ~ 2026-10-20</td><td>2026-10-20 22:13</td><td>不区分保存条件</td></tr>
<tr><td>ERP 订单 <code>销售订单_20260101-20261020.xlsx</code></td><td>2026-01-04 ~ 2026-10-20</td><td>2026-10-20 22:0x</td><td>备注不含稳定性结论</td></tr>
<tr><td>书面测试反馈库 <code>test_feedback*.json</code></td><td>—</td><td>空库</td><td>0 条，无法作为书面证据</td></tr>
<tr><td>MD2091 说明书</td><td>—</td><td>—</td><td>−20℃ 长期 / 4℃ 3 个月，属原液指标</td></tr>
</tbody></table></div>
<div class="note">所有「正面 / 负面」均为客户或销售在活动记录中的表述，不是第三方检测报告；对外引用请写「客户反馈」。更早月份（3–6 月）在月报档，未纳入本版。</div></div>
__FOOTER__
</div>
<script>
(function(){
  var grid=document.getElementById('grid'), cards=[].slice.call(grid.querySelectorAll('.case'));
  var chips=[].slice.call(document.querySelectorAll('.chip')), empty=document.getElementById('empty');
  chips.forEach(function(ch){
    ch.addEventListener('click', function(){
      chips.forEach(function(c){ c.classList.remove('on'); });
      ch.classList.add('on');
      var f=ch.getAttribute('data-f'), shown=0;
      cards.forEach(function(card){
        var tags=(card.getAttribute('data-tags')||'').split(' ');
        var ok=(f==='all')||tags.indexOf(f)>=0;
        card.style.display=ok?'':'none';
        if(ok) shown++;
      });
      empty.style.display=shown?'none':'block';
    });
  });
})();
</script>
</body></html>"""

HTML = (HTML.replace('__N28__', str(n_28)).replace('__N20__', str(n_m20))
            .replace('__NP__', str(n_pos)).replace('__NN__', str(n_neg))
            .replace('__NC__', str(n_clients)).replace('__N__', str(len(cases)))
            .replace('__CARDS__', '\n'.join(cards)).replace('__MATRIX__', mrow)
            .replace('__FOOTER__', ''))
HTML = BK.inject(HTML, theme='dark', tag='全预混低温保存 · 客户案例',
                 footer_extra='内部资料 · 案例库 ｜ 原文可回溯至飞书活动')
open(OUT, 'w', encoding='utf-8').write(HTML)
print('✅', OUT, '%.0f KB' % (len(HTML.encode()) / 1024))
