/* 骏航免费 AI 体检 · 交互式留资问卷（国内主版 v2 · 6 题 · ~90 秒）
   对话式、一问一答、大字号；关键字用 Claude 橙(.kw)。result-gate + 分维度评分。 */
const { useState, useEffect } = React;

/* ★★★ 博宁填这里：企业微信「联系我」活码链接（后台→客户联系→联系我→生成链接，形如 https://work.weixin.qq.com/ct/…）
   填上后，结果页/落地页的「加企微」按钮点击即在微信内拉起“添加”。留空则点击滚动到二维码提示扫码。 */
const WECOM_ADD_URL = "";

function addWecom() {
  if (WECOM_ADD_URL) { window.open(WECOM_ADD_URL, "_blank", "noopener"); return; }
  const qr = document.querySelector(".ck-overlay .ck-qr") || document.querySelector(".ck-qr");
  if (qr) { qr.classList.add("qr-flash"); setTimeout(() => qr.classList.remove("qr-flash"), 1400); }
}

function kw(text) {
  if (text == null) return null;
  return String(text).split(/\*\*(.+?)\*\*/g).map((p, i) =>
    i % 2 ? <span className="kw" key={i}>{p}</span> : <React.Fragment key={i}>{p}</React.Fragment>
  );
}

const Q1 = {
  key: "q1", q: "你公司**主要做哪块**？", sub: "选一个，后面只问你这条线的事",
  opts: [
    { l: "主要做外贸 / 出海", branch: "trade" },
    { l: "主要内销 / 内部管理", branch: "eff" },
    { l: "两块都有", branch: "trade", sub: "先帮你看外贸这条线" },
  ],
};
// 付费意向探针（独立于诊断分；iv=意向分权重，qv=资格分）
const INTENT = [
  { key: "role", intent: 1, q: "这件事，你在公司里是**什么角色**？", sub: "帮我们安排对接的人",
    opts: [
      { l: "我就是老板 / 能直接拍板", qv: 3 },
      { l: "我主管这块，能强力推动", qv: 2 },
      { l: "我参与，但要老板点头", qv: 1 },
      { l: "我先帮领导了解一下", qv: 0, tire: true },
    ] },
  { key: "past", intent: 1, q: "为解决这个问题，你们**之前做过什么**？",
    opts: [
      { l: "花钱找过外部服务商 / 顾问，但不满意", iv: 25 },
      { l: "内部员工自己摸索过，没做起来", iv: 15 },
      { l: "想过，没真启动", iv: 5 },
      { l: "第一次认真考虑", iv: 0, tire: true },
    ] },
  { key: "timeline", intent: 1, q: "如果今天报告出来，你打算**什么时候真正启动**？",
    opts: [
      { l: "这个月内", iv: 35, tag: "now" },
      { l: "1–3 个月内", iv: 25, tag: "mid" },
      { l: "3–6 个月后，等某件事完成", iv: 10, tag: "later", delay: true },
      { l: "还没明确计划", iv: -15, tag: "someday", tire: true },
    ] },
  { key: "commit", intent: 1, q: "报告出来后，要不要顾问花 **30 分钟帮你解读怎么落地**？",
    opts: [
      { l: "要，我方便时直接约", iv: 20, commit: true },
      { l: "都行，你定时间", iv: 5 },
      { l: "先看报告再说", iv: 0 },
      { l: "不用跟进", iv: -20, tire: true },
    ] },
];
const TRADE = [
  { key: "q2", dim: "被搜到力", q: "用英文在谷歌搜你的**核心产品词**，你排第几？",
    opts: [{ l: "第一页", v: 3 }, { l: "前几页（2–4 页）", v: 2 }, { l: "找不到", v: 0 }, { l: "没独立站 / 没搜过", v: 0 }] },
  { key: "q3", dim: "AI 可见度", q: "海外客户越来越爱**问 AI 要供应商**——AI 给的名单里，**有没有你**？",
    sub: "不确定就用国内能打开的豆包 / DeepSeek，搜一下你的英文产品词试试", nudge: { text: "现在用豆包搜一下 →", url: "https://www.doubao.com/chat/" },
    opts: [{ l: "经常推我、描述也准", v: 3 }, { l: "有时提到我", v: 2 }, { l: "提了对手、没提我", v: 1 }, { l: "没提我 / 没试过", v: 0 }] },
  { key: "q4", dim: "内容更新力", q: "你网站的**英文博客 / 技术文章**，上次更新是什么时候？",
    opts: [{ l: "每月更新", v: 3 }, { l: "偶尔（半年一次）", v: 2 }, { l: "两年以上没动", v: 1 }, { l: "没有博客", v: 0 }] },
  { key: "q5", dim: "询盘转化力", q: "你的网站每月带来几条**海外询盘**？",
    opts: [{ l: "20 条以上", v: 3 }, { l: "6–20 条", v: 2 }, { l: "1–5 条", v: 1 }, { l: "0 条", v: 0 }] },
];
// 代价量化题（损失厌恶 + 喂意向分）
const COST_TRADE = { key: "cost", intent: 1, q: "你现在**一条有效海外询盘，大概花多少成本**拿到？", sub: "展会 / 平台 / 投流摊下来，估个区间就行",
  opts: [{ l: "2000 元以上", iv: 20 }, { l: "500–2000 元", iv: 14 }, { l: "500 元以内", iv: 6 }, { l: "没算过 / 不好说", iv: 8 }] };
const COST_EFF = { key: "cost", intent: 1, q: "你刚勾的那些环节，**粗算每个月大概吃掉多少钱**？", sub: "估个区间就行，帮你算这笔账值不值得改",
  opts: [{ l: "50 万以上", iv: 25 }, { l: "20–50 万", iv: 20 }, { l: "5–20 万", iv: 14 }, { l: "5 万以内", iv: 6 }, { l: "没统计过，但感觉不少", iv: 8 }] };
const EFF = [
  { key: "q2", type: "multi", q: "你厂里下面哪些事**最耗人工 / 最容易出错 / 最让你头疼**？", sub: "能勾几个勾几个——勾得越多，可交给 AI 的面越广",
    opts: [
      { l: "同一批数据要重复填进好几个表 / 系统，对账乱" },
      { l: "订单 / 询价靠人工抄录、报价靠老师傅算，慢又怕错" },
      { l: "排产靠脑子记 / 微信通知，一插单或设备坏就乱、交期说不准" },
      { l: "质检靠人眼、记录不全，客诉来了追不到是哪批 / 哪台 / 哪料" },
      { l: "想知道当下生产 / 销售 / 利润，得等财务月底，平时看不到" },
      { l: "海外询盘 / 客户回复太慢，有单子飞走", hintTrade: true },
    ] },
  { key: "q3", dim: "数据台账", q: "同一批货 / 同一笔订单的信息，现在要录进**几个地方**？",
    opts: [{ l: "1 个，系统统一管", v: 3 }, { l: "2 个（系统 + Excel）", v: 2 }, { l: "3 个或以上（系统 + Excel + 微信群 + 纸）", v: 1 }, { l: "基本不入系统，靠纸质 / 口头", v: 0 }] },
  { key: "q4", dim: "订单报价", q: "客户发来的**询价单 / 订单**（PDF、邮件），到你们给出报价，通常多久？",
    opts: [{ l: "系统自动识别导入、当天报价", v: 3 }, { l: "业务员手工录、半天", v: 2 }, { l: "手工录还要等老师傅算成本、1 天+", v: 1 }, { l: "打印放纸质档、系统里没有", v: 0 }] },
  { key: "q5", dim: "生产计划", q: "你们的**生产排期**放在哪？客户问交期怎么答？",
    opts: [{ l: "系统里可查、自动算准交期", v: 3 }, { l: "Excel 表、凭经验估个基本准", v: 2 }, { l: "厂长 / 老板脑子里、口头微信传", v: 1 }, { l: "白板手写 / 拍脑袋报、常延期", v: 0 }] },
  { key: "q6", dim: "质量合规", q: "客户说某批货有问题，你们**多快能找到那批的质检记录**、定位到哪台 / 哪班 / 哪料？",
    opts: [{ l: "10 分钟内、系统可追全链路", v: 3 }, { l: "半天、翻纸质能查到", v: 2 }, { l: "1 天以上", v: 1 }, { l: "找不到 / 记录不全", v: 0 }] },
  { key: "q7", dim: "经营决策", q: "你（老板）想看工厂**今天或这周的生产 / 销售 / 利润**，多快能看到？",
    opts: [{ l: "实时，有看板", v: 3 }, { l: "第二天，让助理 / 财务整理", v: 2 }, { l: "只有月报，月底才知道", v: 1 }, { l: "基本看不到准数", v: 0 }] },
];

// ★ 付费意向引擎：三件事分开评 —— 想不想做(意向) vs 够不够格(资格) vs 现状(诊断)
function fakePhone(s) {
  const d = String(s || "").replace(/\D/g, "");
  if (!d) return false;
  if (/^1[3-9]\d{9}$/.test(d) === false && d.length >= 6) {
    // 含非手机号的微信号不判假；只对“像手机号但明显假”的判假
  }
  if (/^(\d)\1{5,}$/.test(d)) return true;            // 全相同
  if (["13800138000", "13800000000", "12345678901", "11111111111"].includes(d)) return true;
  if (/^1234567/.test(d)) return true;
  return false;
}
function assessIntent(ans, contact, url, extra) {
  let score = 0, qual = 0;
  const ev = {};
  let tire = 0; const flags = [];
  Object.keys(ans).forEach((k) => {
    const a = ans[k]; if (!a) return;
    if (typeof a.iv === "number") score += a.iv;
    if (typeof a.qv === "number") qual = Math.max(qual, a.qv);
    if (a.tire) { tire++; }
    if (a.commit) ev.commit = a.l;
    if (a.delay) ev.delay = true;
    if (a.tag) ev.timeline = a.l;
    if (typeof a.iv === "number" && a.iv >= 15 && !a.tag && !a.commit) ev.past = a.l;
  });
  // 行为信号（行为不会骗人）
  const ph = String(contact || "");
  if (fakePhone(ph)) { score -= 15; flags.push("手机号疑似假"); }
  else if (ph.trim().length >= 5) score += 5;
  const ex = String(extra || "").trim();
  if (ex.length > 30) score += 10; else if (ex.length >= 12) score += 5;
  if (url && url.trim().length > 4) score += 8;
  const pains = (function () { const pk = Object.keys(ans).find((k) => ans[k] && ans[k].sel); return pk ? ans[pk].sel.length : 0; })();
  if (pains) score += Math.min(pains * 2, 10);
  // 自填“其他”走心 +，敷衍 -
  Object.keys(ans).forEach((k) => { const a = ans[k]; if (a && a.other) { if ((a.text || "").length > 20) score += 6; else score -= 8; } });
  score = Math.max(0, Math.min(100, score));
  // 假意向特征（≥2 条 tire 命中 = 白嫖）
  const hasCommit = !!ev.commit;
  // 等级
  let level, levelLabel;
  const delayBuyer = ev.delay && score >= 25 && tire < 2;
  if (tire >= 2) { level = "cold"; levelLabel = "❄️ 弱意向 / 疑似白嫖"; }
  else if (delayBuyer) { level = "delay"; levelLabel = "🧊 延迟买家"; }
  else if (score >= 70) { level = "hot"; levelLabel = "🔥 强意向"; }
  else if (score >= 40) { level = "warm"; levelLabel = "🌡 中意向"; }
  else { level = "cold"; levelLabel = "❄️ 弱意向 / 疑似白嫖"; }
  // 资格 × 意向 四象限
  const hasIntent = level === "hot" || level === "warm";
  const hasQual = qual >= 2;
  const quad = level === "delay"
    ? (hasQual ? "🧊 延迟买家（有资格）→ 记录回访点，到点联系" : "🧊 延迟买家 → 培育，到点回访")
    : hasQual ? (hasIntent ? "S 真买家 → 4h 内约见" : "B 有钱有权但不急 → 等 Why Now") : (hasIntent ? "A- 想做没预算 → 培育、帮他说服老板" : "C 白嫖 → 冷池自助，不约");
  // 证据
  const evidence = [ev.timeline ? "时间线[" + ev.timeline + "]" : null, ev.past ? "过往[" + ev.past + "]" : null, ev.commit ? "预约[" + ev.commit + "]" : null].filter(Boolean).join(" · ");
  return { score, level, levelLabel, quad, evidence, tire, qual, action: level === "hot" ? "4 小时内约见（博宁亲跑）" : level === "warm" ? "跟进培育、发改进路线图" : level === "delay" ? "记录回访点，到点主动联系" : "进冷池，自助内容，不约见" };
}

function compute(branch, ans) {
  if (branch === "trade") {
    const torder = ["被搜到力", "AI 可见度", "内容更新力", "询盘转化力"];
    const dims = [1, 2, 3, 4].map((qi, i) => ({ n: torder[i], v: Math.round(((ans[qi] || {}).v || 0) / 3 * 100) }));
    const total = Math.round(dims.reduce((a, d) => a + d.v, 0) / dims.length);
    return decorate({ metric: "外贸获客力", total, dims, peer: "同规模外贸工厂", unit: "外贸独立站", locked: "发网址 → 出一份完整 URL 体检报告" }, ans, "trade");
  }
  const order = ["数据台账", "订单报价", "生产计划", "质量合规", "经营决策"];
  const dims = [2, 3, 4, 5, 6].map((qi, i) => ({ n: order[i], v: Math.round(((ans[qi] || {}).v || 0) / 3 * 100) }));
  const total = Math.round(dims.reduce((a, d) => a + d.v, 0) / dims.length);
  const pains = ans[1] && ans[1].sel ? ans[1].sel : [];
  return decorate({ metric: "AI 提效就绪度", total, dims, peer: "同规模制造工厂", unit: "工厂流程", locked: "5 维雷达 + 最该先动的一层 + 能省多少人工", pains }, ans, "eff");
}

// 等级(去羞辱) + 同行对比 + 最弱维度 + PAP话术 + CTA三段路由
function decorate(r, ans, branch) {
  const t = r.total;
  // 最弱维度(排最上、做搅动 · 木桶原理切入口)
  const weak = r.dims.slice().sort((a, b) => a.v - b.v)[0];
  r.weak = weak;
  if (branch === "eff") {
    // 木桶原理：状态标签取最弱层
    const w = weak.v;
    r.band = w <= 33 ? { g: "手工管", t: "AI 落地空间最大" } : w <= 66 ? { g: "系统管", t: "有系统但孤立" } : { g: "数据管", t: "可做更深优化" };
  } else {
    r.band = t <= 40 ? { g: "地基期", t: "改善空间最大" } : t <= 60 ? { g: "漏水期", t: "正在悄悄漏客户" } : t <= 80 ? { g: "成长期", t: "底子不错、可再压" } : { g: "领先期", t: "该破规模瓶颈" };
  }
  // 社会比较：不编百分比，只给“档位段”（红线）
  const pos = t < 40 ? "偏后段" : t <= 69 ? "中游" : "中上段";
  r.pct = Math.max(12, Math.min(92, t));
  r.pctText = `在我们服务过的同类企业里，你大致处于「${pos}」`;
  // PAP：命名 → 搅动 → 给希望
  r.pap = {
    name: `你当前${r.metric} **${t}/100**，处于**${r.band.g}**。`,
    agitate: `其中「**${weak.n}**」只有 ${weak.v}/100，是整条链路最大的**漏水点**。`,
    hope: t < 40
      ? "这恰恰说明你**改善空间最大**——同类企业从这一项补起，通常 6–8 周见到变化。"
      : t <= 69
      ? "好消息：这一项属于「**高影响、可快速改善**」，同类企业通常 6–8 周见到提升。"
      : "你已越过起步的坑，下一步是「**规模化瓶颈**」——怎么不加人把产出翻几倍。",
  };
  // CTA 三段路由（urgent 跨分支扫描 timeline）
  const urgent = Object.keys(ans).some((k) => ans[k] && ans[k].tag === "now");
  r.route = t >= 70
    ? { mode: "book", h: "建议约一次 **30 分钟深度诊断**", p: "你基础不错，我们把「怎么不加人翻几倍产出」讲给你听。本周时段有限，今天确认可优先安排。" }
    : t >= 40
    ? { mode: "pdf", h: urgent ? "建议约一次 **30 分钟诊断**" : "领一份**按你评分生成的改进路线图**", p: urgent ? "你说越快越好——加顾问，我们把最该补的 3 处直接列给你。" : "加企微即发，按你这次的得分定制，不是通用模板。" }
    : { mode: "soft", h: "先**聊聊你的情况**（免费）", p: "问题多不要紧，正说明值得做。加个企微，先帮你把最该先动的一处理出来，不催不推。" };
  // 企微钩子三条
  r.perks = ["这份体检的人工解读（10 分钟，语音/文字都行）", `针对你最弱项「${weak.n}」的 2–3 个具体改进建议`, "一份同行业真实案例（脱敏）"];
  return r;
}

function Intro({ onStart }) {
  return (
    <div className="ck-card">
      <div className="ck-badge">🛡️ 仅用于生成你的体检报告</div>
      <h2 className="ck-h1">免费 AI <span className="kw">获客 / 提效</span>体检</h2>
      <p className="ck-lead">几个问题，约 <b>2 分钟</b>。答完<b>立即看到得分</b>，告诉你最该改的地方在哪。</p>
      <ul className="ck-trust">
        <li>骏航已服务宁波多家制造 / 外贸企业，做 AI 获客与内部提效。</li>
        <li>你的信息只用于生成这份报告，{kw("**不电话轰炸、不外传**")}。</li>
      </ul>
      <button className="ck-btn" onClick={onStart}>开始体检 →</button>
    </div>
  );
}

function Question({ card, qstep, total, onPick, onBack }) {
  const [otherOpen, setOtherOpen] = useState(false);
  const [otherText, setOtherText] = useState("");
  const [sel, setSel] = useState([]);
  const isMulti = card.type === "multi";
  function submitOther() {
    const base = { l: "其他", other: true, text: otherText.trim() };
    if (card.opts[0].branch) base.branch = "trade";
    else if (card.opts[0].tag) base.tag = "mid";
    else { const vs = card.opts.map((o) => o.v).filter((v) => v != null); base.v = vs.length ? Math.round(vs.reduce((a, b) => a + b, 0) / vs.length) : 0; }
    onPick(base);
  }
  function toggle(o) { setSel((s) => (s.includes(o.l) ? s.filter((x) => x !== o.l) : [...s, o.l])); }
  function submitMulti() { onPick({ l: sel.join(" / "), multi: true, sel }); }
  const tradeHinted = isMulti && sel.some((l) => (card.opts.find((o) => o.l === l) || {}).hintTrade);
  return (
    <div className="ck-card">
      <button className="ck-back" onClick={onBack}>← 上一步</button>
      <div className="ck-qmeta">第 {qstep + 1} / {total} 题</div>
      <h2 className="ck-q">{kw(card.q)}</h2>
      {card.sub && <p className="ck-qsub">{card.sub}</p>}
      {card.nudge && <a className="ck-nudge" href={card.nudge.url} target="_blank" rel="noopener noreferrer">{card.nudge.text}</a>}
      {isMulti ? (
        <div className="ck-opts">
          {card.opts.map((o, i) => (
            <button className={"ck-opt ck-multi" + (sel.includes(o.l) ? " on" : "")} key={i} onClick={() => toggle(o)}>
              <span className="ck-box">{sel.includes(o.l) ? "✓" : ""}</span>
              <span className="ck-optl">{o.l}</span>
            </button>
          ))}
          {tradeHinted && <p className="ck-tradehint">最后一条更适合走「<b>外贸出海</b>」体检——这次先帮你看工厂提效，外贸我们另约。</p>}
          <button className="ck-btn ck-multibtn" disabled={!sel.length} onClick={submitMulti}>继续 →{sel.length ? `（已选 ${sel.length}）` : ""}</button>
        </div>
      ) : (
        <div className="ck-opts">
          {card.opts.map((o, i) => (
            <button className="ck-opt" key={i} onClick={() => onPick(o)}>
              <span className="ck-optl">{o.l}</span>
              {o.sub && <span className="ck-optsub">{o.sub}</span>}
              <span className="ck-optar">→</span>
            </button>
          ))}
          {!otherOpen ? (
            <button className="ck-opt ck-opt-other" onClick={() => setOtherOpen(true)}>
              <span className="ck-optl">其他 / 我自己说说…</span>
              <span className="ck-optar">＋</span>
            </button>
          ) : (
            <div className="ck-other">
              <textarea className="ck-textarea" autoFocus rows={3} value={otherText} onChange={(e) => setOtherText(e.target.value)} placeholder="把你的实际情况说得越细越好，体检结果也越准…" />
              <div className="ck-other-row">
                <span className="ck-other-hint">说得越细，我们越懂你</span>
                <button className="ck-mini" disabled={!otherText.trim()} onClick={submitOther}>填好了，继续 →</button>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function Lead({ name, setName, contact, setContact, url, setUrl, extra, setExtra, onSubmit, onBack }) {
  const ok = name.trim().length >= 1 && contact.trim().length >= 5;
  return (
    <div className="ck-card">
      <button className="ck-back" onClick={onBack}>← 上一步</button>
      <div className="ck-badge">最后一步</div>
      <h2 className="ck-h1">填手机 / 微信，<span className="kw">立即看得分</span></h2>
      <p className="ck-lead">报告这就生成好了。留个联系方式，方便把<b>完整报告</b>发给你。</p>
      <label className="ck-flabel">怎么称呼您 <span className="ck-req">必填</span></label>
      <input className="ck-input" value={name} onChange={(e) => setName(e.target.value)} placeholder="王总 / 李经理 / 张先生" />
      <label className="ck-flabel">手机 / 微信 <span className="ck-req">必填</span></label>
      <input className="ck-input" value={contact} onChange={(e) => setContact(e.target.value)} placeholder="手机号 / 微信号" inputMode="tel" />
      <label className="ck-flabel">官网 / 独立站网址 <span className="ck-opt2">选填 · 留了我们直接抓取，体检更准</span></label>
      <input className="ck-input" value={url} onChange={(e) => setUrl(e.target.value)} placeholder="https://你的网址（外贸客户强烈建议留）" inputMode="url" />
      <textarea className="ck-textarea lead" rows={3} value={extra} onChange={(e) => setExtra(e.target.value)} placeholder="再留一句：公司 / 最头疼的一件事（选填，越多我们越懂你）" />
      <button className="ck-btn" disabled={!ok} onClick={onSubmit}>查看我的体检得分 →</button>
      <p className="ck-fine">提交即同意骏航在本次体检及后续沟通范围内使用你填写的信息；我们不外传、不做骚扰式电话，符合《个人信息保护法》。</p>
    </div>
  );
}

function CountUp({ to }) {
  const [n, setN] = useState(0);
  useEffect(() => {
    let raf, start;
    const dur = 900;
    const tick = (ts) => {
      if (!start) start = ts;
      const p = Math.min((ts - start) / dur, 1);
      const eased = 1 - Math.pow(1 - p, 3);
      setN(Math.round(eased * to));
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [to]);
  return <b>{n}</b>;
}

function Result({ res, branch, ans }) {
  // 维度按弱→强排序，最弱排最上
  const dims = res.dims.slice().sort((a, b) => a.v - b.v);
  const color = (v) => (v < 40 ? "red" : v < 70 ? "amber" : "green");
  const route = res.route;
  const btnText = route.mode === "book" ? "约 30 分钟诊断 →" : route.mode === "pdf" ? "加企微领方案 →" : "先聊聊（免费）→";
  return (
    <div className="ck-card">
      {/* 第一层：免费露总分 */}
      <div className="ck-badge">你的初评结果</div>
      <div className="ck-score">
        <div className="ck-scoren"><CountUp to={res.total} /><span>/100</span></div>
        <div>
          <div className="ck-metric">{res.metric}</div>
          <div className="ck-grade">{res.band.g} · {res.band.t}</div>
        </div>
      </div>
      <div className="ck-bench"><span className="ck-benchbar"><i style={{ width: res.pct + "%" }} /></span><span className="ck-benchtx">{res.pctText}</span></div>
      {/* PAP：命名 → 搅动 → 给希望 */}
      <div className="ck-pap">
        <p className="ck-pap-name">{kw(res.pap.name)}</p>
        <p className="ck-pap-agitate">{kw(res.pap.agitate)}</p>
        <p className="ck-pap-hope"><span className="ck-hopeicon">＋</span>{kw(res.pap.hope)}</p>
      </div>
      {/* 第三层入口：分维度（最弱排最上，红黄绿） */}
      <div className="ck-dims">
        {dims.map((d, i) => (
          <div className="ck-dim" key={i}>
            <div className="ck-dimtop"><span>{i === 0 && <em className="ck-weak">最该先补</em>}{d.n}</span><b>{d.v}</b></div>
            <div className={"ck-bar " + color(d.v)}><span style={{ width: d.v + "%" }} /></div>
          </div>
        ))}
      </div>
      {/* 留资门后的锁层 */}
      <div className="ck-lock">
        <div className="ck-lockrows">
          <div className="ck-lockrow">完整分维度报告 + 根因</div>
          <div className="ck-lockrow">最该改的 3 处 + 具体建议</div>
          <div className="ck-lockrow">{res.locked}</div>
        </div>
        <div className="ck-lockmask"><span>🔒 加企微解锁</span></div>
      </div>
      {/* 企微建连 + 钩子（加了能得到什么） */}
      <div className="ck-unlock">
        <div className="ck-qr" onClick={addWecom}><img src="assets/wecom-qr.jpg" alt="企微二维码" /></div>
        <div className="ck-unlocktx">
          <div className="ck-unlockh">{kw(route.h)}</div>
          <p>{route.p}</p>
          <ul className="ck-perks">
            {res.perks.map((p, i) => <li key={i}>{p}</li>)}
          </ul>
          <button className="ck-unlockbtn" onClick={addWecom}>{btnText}</button>
        </div>
      </div>
      <p className="ck-fine ck-resfine">本报告只做「发现」，不承诺排名 / 收录 / 询盘 / 成交；具体改善以双方书面方案为准。</p>
    </div>
  );
}

function Checkup() {
  const [open, setOpen] = useState(false);
  const [phase, setPhase] = useState("intro");
  const [qstep, setQstep] = useState(0);
  const [branch, setBranch] = useState("trade");
  const [ans, setAns] = useState({});
  const [name, setName] = useState("");
  const [contact, setContact] = useState("");
  const [url, setUrl] = useState("");
  const [extra, setExtra] = useState("");
  const [forcedBranch, setForcedBranch] = useState(null);

  useEffect(() => {
    const o = (e) => {
      const br = e && e.detail ? e.detail : null;
      setPhase("intro"); setForcedBranch(br); setQstep(0);
      setBranch(br || "trade");
      setAns(br ? { 0: { branch: br } } : {});
      setName(""); setContact(""); setUrl(""); setExtra(""); setOpen(true);
    };
    window.addEventListener("open-checkup", o);
    window.openCheckup = (branch) => window.dispatchEvent(new CustomEvent("open-checkup", { detail: branch }));
    // 预览钩子：#result-trade / #result-eff 直接看结果页（仅用于评审，真实用户不触发）
    const demo = () => {
      const h = location.hash;
      if (h === "#result-trade" || h === "#result-eff") {
        const tr = h === "#result-trade";
        setBranch(tr ? "trade" : "eff");
        setAns(tr
          ? { 0: { l: "主要做外贸 / 出海", branch: "trade" }, 1: { l: "找不到", v: 0 }, 2: { l: "没提我 / 没试过", v: 0 }, 3: { l: "两年以上没动", v: 1 }, 4: { l: "1–5 条", v: 1 }, 5: { l: "2000 元以上", iv: 20 }, 6: { l: "我就是老板", qv: 3 }, 7: { l: "花钱找过顾问", iv: 25 }, 8: { l: "这个月内", iv: 35, tag: "now" }, 9: { l: "要，我方便时直接约", iv: 20, commit: true } }
          : { 0: { l: "主要内销 / 内部管理", branch: "eff" }, 1: { l: "重复填报 / 排产乱", multi: true, sel: ["同一批数据要重复填进好几个表 / 系统，对账乱", "排产靠脑子记 / 微信通知，一插单或设备坏就乱、交期说不准"] }, 2: { l: "3 个或以上", v: 1 }, 3: { l: "业务员手工录、半天", v: 2 }, 4: { l: "厂长 / 老板脑子里", v: 1 }, 5: { l: "1 天以上", v: 1 }, 6: { l: "只有月报", v: 1 }, 7: { l: "1–3 个月内", tag: "mid" } });
        setPhase("result"); setOpen(true);
      }
    };
    window.addEventListener("hashchange", demo); demo();
    return () => { window.removeEventListener("open-checkup", o); window.removeEventListener("hashchange", demo); };
  }, []);
  useEffect(() => {
    const k = (e) => { if (e.key === "Escape") setOpen(false); };
    window.addEventListener("keydown", k);
    return () => window.removeEventListener("keydown", k);
  }, []);

  const branchQs = (branch === "trade" ? TRADE : EFF).concat([branch === "trade" ? COST_TRADE : COST_EFF], INTENT);
  const L = branchQs.length;            // 诊断题 + 代价 + 意向题（trade=9, eff=11）
  const total = L + 1;                   // Q1 + 全部分支题
  const current = qstep === 0 ? Q1 : branchQs[qstep - 1];

  function pick(opt) {
    const na = { ...ans, [qstep]: opt };
    setAns(na);
    if (qstep === 0 && opt.branch) setBranch(opt.branch);
    if (qstep < L) setQstep(qstep + 1);
    else setPhase("lead");
  }
  function back() {
    if (phase === "lead") { setPhase("q"); setQstep(L); return; }
    if (phase === "q" && qstep > (forcedBranch ? 1 : 0)) setQstep(qstep - 1);
    else setPhase("intro");
  }

  const res = phase === "result" ? compute(branch, ans) : null;
  const progress = phase === "intro" ? 0 : phase === "q" ? (qstep + 1) / (total + 1) : phase === "lead" ? total / (total + 1) : 1;

  if (!open) return null;
  return (
    <div className="ck-overlay" onClick={(e) => { if (e.target === e.currentTarget) setOpen(false); }}>
      <div className="ck-modal">
        <header className="ck-head">
          <div className="ck-brand"><img src="assets/logo-jh.webp" alt="骏航" /><span>骏航 AI 体检</span></div>
          <div className="ck-prog"><span style={{ width: progress * 100 + "%" }} /></div>
          <button className="ck-close" onClick={() => setOpen(false)} aria-label="关闭">✕</button>
        </header>
        <div className="ck-scroll">
          {phase === "intro" && <Intro onStart={() => { if (forcedBranch) setQstep(1); setPhase("q"); }} />}
          {phase === "q" && <Question key={qstep} card={current} qstep={forcedBranch ? qstep - 1 : qstep} total={forcedBranch ? total - 1 : total} onPick={pick} onBack={back} />}
          {phase === "lead" && <Lead name={name} setName={setName} contact={contact} setContact={setContact} url={url} setUrl={setUrl} extra={extra} setExtra={setExtra} onSubmit={() => { saveSubmission(branch, ans, name, contact, url, extra); setPhase("result"); }} onBack={back} />}
          {phase === "result" && <Result res={res} branch={branch} ans={ans} />}
        </div>
      </div>
    </div>
  );
}

function saveSubmission(branch, ans, name, contact, url, extra) {
  let rec;
  try {
    const res = compute(branch, ans);
    const intent = assessIntent(ans, contact, url, extra);
    rec = {
      ts: new Date().toISOString(),
      branch: branch === "trade" ? "外贸出海" : "工厂AI落地",
      name: name,
      contact: contact,
      url: url,
      extra: extra,
      metric: res.metric,
      score: res.total,
      grade: res.band.g + " · " + res.pctText,
      dims: res.dims.map((d) => d.n + ":" + d.v).join(" / "),
      timeline: (function () { const tk = Object.keys(ans).find((k) => ans[k] && ans[k].tag); return tk ? ans[tk].l : ""; })(),
      pains: (function () { const pk = Object.keys(ans).find((k) => ans[k] && ans[k].sel); return pk ? ans[pk].sel.join("；") : ""; })(),
      answers: Object.keys(ans).sort((a, b) => a - b).map((k) => (ans[k].other ? "其他:" + ans[k].text : ans[k].l)).join(" | "),
      intentScore: intent.score,
      intentLevel: intent.levelLabel,
      intentEvidence: intent.evidence,
      quadrant: intent.quad,
      intentAction: intent.action,
      tireFlags: intent.tire,
      level: intent.level,
    };
  } catch (e) {
    return;
  }
  try {
    fetch("/api/lead", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(rec),
      keepalive: true,
    }).then((r) => { if (!r.ok) fallbackLocal(rec); }).catch(() => fallbackLocal(rec));
  } catch (e) {
    fallbackLocal(rec);
  }
}

function fallbackLocal(rec) {
  try {
    const k = "jh_checkup_submissions";
    const arr = JSON.parse(localStorage.getItem(k) || "[]");
    arr.push(rec);
    localStorage.setItem(k, JSON.stringify(arr));
  } catch (e) {}
}

Object.assign(window, { Checkup, jhAddWecom: addWecom });
