// Helm — 保障規劃。① 保障時間軸 ② 長照準備金 ③ 重疾一次金 ④ 身故(需求法,含配偶/父母/子女)⑤ 保險 vs 投資 + 尾端壓測。
//   讀文件金庫(window.HELM.docs)+ 扶養設定(HelmPrefs "helm-dependents")。公式見 protection-engine.js(多 agent 辯論+紅隊驗證)。
//   教育性質試算,非投資/保險/稅務建議、不針對特定商品;所有假設可調。
(function () {
  const NS = window.HelmDesignSystem_9613a7;
  const { Button } = NS;
  const P = window.HelmProtect;
  const num = (P && P.num) || function (x) { return Number(String(x == null ? '' : x).replace(/[^0-9.\-]/g, '')) || 0; };
  function wan(n) { n = Math.round(num(n)); if (!n) return '0'; return Math.abs(n) >= 10000 ? (Math.round(n / 1000) / 10).toLocaleString('en-US', { maximumFractionDigits: 1 }) + '萬' : n.toLocaleString('en-US'); }

  const RISKS = [
    { key: '身故', label: '身故 / 壽險', color: '#3a9e72' },
    { key: '意外', label: '意外身故失能', color: '#d98a3d' },
    { key: '醫療', label: '住院醫療', color: '#3d7fd9' },
    { key: '重疾', label: '重大疾病 / 癌症', color: '#d04f4f', alt: '癌症' },
    { key: '長照', label: '長期照顧', color: '#9162d4' },
  ];

  function Timeline({ items, currentAge, planEnd }) {
    const AGE_MIN = currentAge, AGE_MAX = 105;
    const X0 = 12, X1 = 322, CW = X1 - X0;
    const xs = (a) => X0 + (Math.max(AGE_MIN, Math.min(AGE_MAX, a)) - AGE_MIN) / (AGE_MAX - AGE_MIN) * CW;
    const LABEL_H = 15, BAR_H = 13, ROW_GAP = 14, Y0 = 6;
    const pitch = LABEL_H + BAR_H + ROW_GAP;
    const chartBottom = Y0 + RISKS.length * pitch;
    const H = chartBottom + 24;

    const rows = RISKS.map(function (r) {
      const match = items.filter(function (i) { return i.risk === r.key || (r.alt && i.risk === r.alt); });
      if (!match.length) return { r: r, covered: false };
      let amount = 0, endAge = 0, lifelong = false, hasDaily = false;
      match.forEach(function (m) {
        amount += m.amount || 0;
        if (m.lifelong || m.endAge == null) lifelong = true;
        if (m.endAge != null && m.endAge > endAge) endAge = m.endAge;
        if (r.key === '醫療' && !m.amount) hasDaily = true;
      });
      if (lifelong) endAge = AGE_MAX;
      return { r: r, covered: true, amount: amount, endAge: endAge, lifelong: lifelong, hasDaily: hasDaily };
    });
    const cliffAges = rows.filter(function (d) { return d.covered && !d.lifelong && d.endAge < planEnd; }).map(function (d) { return d.endAge; });
    const cliff = cliffAges.length ? Math.min.apply(null, cliffAges) : null;
    const ticks = [currentAge, 65, 76, 85, 95, 105].filter(function (t, i, a) { return t >= AGE_MIN && a.indexOf(t) === i; });

    return (
      <svg viewBox={'0 0 340 ' + H} width="100%" role="img" aria-label="保障時間軸" style={{ display: 'block' }}>
        <line x1={xs(planEnd)} y1={Y0 - 2} x2={xs(planEnd)} y2={chartBottom} stroke="var(--line)" strokeWidth="1" strokeDasharray="2 3" />
        {cliff != null && <line x1={xs(cliff)} y1={Y0 - 2} x2={xs(cliff)} y2={chartBottom + 2} stroke="#d04f4f" strokeWidth="1.5" strokeDasharray="3 2" />}
        {rows.map(function (d, i) {
          const ly = Y0 + i * pitch + 11;
          const by = Y0 + i * pitch + LABEL_H;
          const right = d.covered ? (d.lifelong ? '終身' : (d.endAge ? '到 ' + d.endAge + ' 歲' : '')) : '未保';
          const amt = d.covered ? (d.amount ? wan(d.amount) : (d.hasDaily ? '日額型' : '')) : '';
          return (
            <g key={d.r.key}>
              <text x={X0} y={ly} fontSize="11" fill="var(--text-secondary)">{d.r.label}</text>
              <text x={X1} y={ly} fontSize="10.5" textAnchor="end" fill={d.covered ? 'var(--text-tertiary)' : '#d04f4f'}>{amt ? amt + ' · ' : ''}{right}</text>
              <rect x={X0} y={by} width={CW} height={BAR_H} rx="3" fill="var(--surface-sunken)" />
              {d.covered ? (
                <g>
                  <rect x={X0} y={by} width={xs(d.lifelong ? AGE_MAX : d.endAge) - X0} height={BAR_H} rx="3" fill={d.r.color} opacity="0.88" />
                  {!d.lifelong && d.endAge < AGE_MAX && (
                    <g>
                      <rect x={xs(d.endAge)} y={by} width={X1 - xs(d.endAge)} height={BAR_H} rx="3" fill="#d04f4f" opacity="0.12" />
                      <circle cx={xs(d.endAge)} cy={by + BAR_H / 2} r="3" fill="#d04f4f" />
                    </g>
                  )}
                </g>
              ) : (
                <rect x={X0} y={by} width={CW} height={BAR_H} rx="3" fill="none" stroke="#d04f4f" strokeWidth="1" strokeDasharray="3 2" opacity="0.6" />
              )}
            </g>
          );
        })}
        {ticks.map(function (t) {
          const isCliff = cliff != null && t === cliff;
          return (
            <g key={t}>
              <line x1={xs(t)} y1={chartBottom} x2={xs(t)} y2={chartBottom + 4} stroke="var(--line)" strokeWidth="1" />
              <text x={xs(t)} y={chartBottom + 15} fontSize="9.5" textAnchor="middle" fill={isCliff ? '#d04f4f' : 'var(--text-tertiary)'}>{t === currentAge ? '現在' : t}{isCliff ? '·斷崖' : (t === 95 ? '·規劃' : '')}</text>
            </g>
          );
        })}
      </svg>
    );
  }

  function Slider({ label, value, min, max, step, onChange, fmt }) {
    return (
      <div className="pl-sl">
        <div className="pl-sl__top"><span>{label}</span><b>{fmt ? fmt(value) : value}</b></div>
        <input type="range" min={min} max={max} step={step} value={value} className="pl-sl__range" onChange={function (e) { onChange(Number(e.target.value)); }} />
      </div>
    );
  }

  function Stepper({ label, value, min, max, onChange }) {
    return (
      <div className="pl-step">
        <span>{label}</span>
        <span className="pl-step__ctrl">
          <button type="button" onClick={function () { onChange(Math.max(min, value - 1)); }} disabled={value <= min} aria-label="減">−</button>
          <b>{value}</b>
          <button type="button" onClick={function () { onChange(Math.min(max, value + 1)); }} disabled={value >= max} aria-label="加">＋</button>
        </span>
      </div>
    );
  }

  function ProtectionScreen({ onClose, onOpenDocs }) {
    const H = window.HELM || {};
    const hasBirth = !!H.birth;
    const birthYear = hasBirth ? Number(String(H.birth).slice(0, 4)) : (new Date().getFullYear() - 35);   // 未設生日→用 35 歲「示意」(不用擁有者的年份),並在下方提示去設定
    const currentAge = (new Date().getFullYear()) - birthYear;
    const planEnd = P.DEFAULTS.planningEndAge;
    const items = P.timeline(H.docs || [], birthYear, planEnd);
    const docs = H.docs || [];

    const lifeSum = items.filter(function (i) { return i.risk === '身故'; }).reduce(function (s, i) { return s + (i.amount || 0); }, 0);
    const lifePremium = docs.filter(function (d) { return d.type === '壽險'; }).reduce(function (s, d) { return s + num(d.premium); }, 0);
    const totalPremium = docs.reduce(function (s, d) { return s + num(d.premium); }, 0);
    const critCurrent = items.filter(function (i) { return i.risk === '重疾' || i.risk === '癌症'; }).reduce(function (s, i) { return s + (i.amount || 0); }, 0);
    const annualExpenseHelm = Math.round(((H.cashflow && H.cashflow.expenseSum) || 0) * 12);
    const annualIncomeHelm = Math.round(((H.cashflow && H.cashflow.incomeSum) || 0) * 12);

    // 扶養設定(跨裝置記住)
    const [dep, setDep] = React.useState(function () {
      try { return Object.assign({ spouse: false, parents: 0, children: 0 }, JSON.parse((window.HelmPrefs && window.HelmPrefs.get('helm-dependents')) || '{}')); }
      catch (e) { return { spouse: false, parents: 0, children: 0 }; }
    });
    function saveDep(next) { setDep(next); try { window.HelmPrefs.set('helm-dependents', JSON.stringify(next)); } catch (e) {} }
    const hasDep = dep.spouse || dep.parents > 0 || dep.children > 0;

    const [ltc, setLtc] = React.useState({ monthly: P.DEFAULTS.ltcMonthlyCost, med: P.DEFAULTS.inflationMedical, years: P.DEFAULTS.ltcYears, start: P.DEFAULTS.careStartAge });
    const [income, setIncome] = React.useState(annualIncomeHelm || 600000);
    const [survivorCost, setSurvivorCost] = React.useState(annualExpenseHelm ? Math.round(annualExpenseHelm * 0.6 / 10000) * 10000 : 360000);
    const [supportYears, setSupportYears] = React.useState(25);
    const [debt, setDebt] = React.useState(Math.round(H.totalLiab || 0));
    const [retRate, setRetRate] = React.useState(0.044);
    const [critAge, setCritAge] = React.useState(85);
    const [critCost, setCritCost] = React.useState(2500000);
    const [dd, setDd] = React.useState(0.30);
    const [openArea, setOpenArea] = React.useState(null);          // 「怎麼補」展開的領域
    const [itypes, setItypes] = React.useState(null);              // 保險類型知識庫(insurance-types.json)
    const [openAct, setOpenAct] = React.useState(null);            // 行動清單展開的領域
    const [showDetail, setShowDetail] = React.useState(false);     // 完整試算(預設收起,別一次轟炸)
    const [copied, setCopied] = React.useState(false);
    React.useEffect(function () {
      fetch('insurance-types.json?v=147').then(function (r) { return r.json(); }).then(setItypes).catch(function () {});
    }, []);

    const reserve = P.ltcReserve({ currentAge: currentAge, ltcMonthlyCost: ltc.monthly, inflationMedical: ltc.med, ltcYears: ltc.years, careStartAge: ltc.start });
    const crit = P.criticalGap({ annualIncome: income, current: critCurrent });
    const eduFund = dep.children * 4000000;
    const lg = P.lifeGap({ dependents: hasDep, annualExpense: survivorCost, survivorRatio: 1, supportYears: supportYears, debt: debt, eduFund: eduFund, funeral: P.DEFAULTS.funeral, liquidAssets: H.liquid || 0, existingLifeCover: lifeSum });
    const inv = function (age) { return P.fvPremiumInvested(lifePremium || 11138, 20, 30, age, retRate); };
    const fs = P.forcedSaleLoss(critCost, dd);

    // 各領域目前「有沒有保」→「怎麼補」卡用(即時依你保單算,缺口變→推薦跟著變)
    const hasAccident = items.some(function (i) { return i.risk === '意外'; });
    const hasMedical = items.some(function (i) { return i.risk === '醫療'; });
    const ltcCurrent = items.filter(function (i) { return i.risk === '長照'; }).reduce(function (s, i) { return s + (i.amount || 0); }, 0);
    function gapOf(area) {
      if (area === 'life') return lg.noDep ? { txt: '無扶養 · 經濟責任低', gap: false } : (lg.gap > 0 ? { txt: '缺口約 ' + wan(lg.gap), gap: true } : { txt: '已足', gap: false });
      if (area === 'critical') return crit.gap > 0 ? { txt: '缺口約 ' + wan(crit.gap), gap: true } : { txt: '已足', gap: false };
      if (area === 'ltc') return ltcCurrent > 0 ? { txt: '已有部分保障', gap: false } : { txt: '準備金約 ' + wan(reserve) + ' · 目前未保', gap: true };
      if (area === 'medical') return hasMedical ? { txt: '已有住院醫療', gap: false } : { txt: '目前未保', gap: true };
      if (area === 'accident') return hasAccident ? { txt: '已有意外險', gap: false } : { txt: '目前未保', gap: true };
      return { txt: '', gap: false };
    }

    // 扶養描述(小抄用)
    const depDesc = [dep.spouse ? '配偶' : null, dep.children ? dep.children + ' 名子女' : null, dep.parents ? dep.parents + ' 名父母' : null].filter(Boolean).join('、') || '無';
    // ── 保障行動清單:各領域缺口排優先序 + 接上 insurance-types 的 action 內容 ──
    const STATUS = { gap: { t: '缺口', c: 'gap' }, partial: { t: '部分', c: 'partial' }, have: { t: '已備', c: 'have' }, na: { t: '不需', c: 'na' } };
    const planRows = (function () {
      if (!itypes || !itypes.areas) return [];
      var byArea = {}; itypes.areas.forEach(function (a) { byArea[a.area] = a; });
      function mk(area, status, line, urgency) { return { area: area, a: byArea[area], status: status, line: line, urgency: urgency }; }
      var rows = [
        mk('medical', hasMedical ? 'have' : 'gap', hasMedical ? '已有住院醫療——確認是不是「實支實付、保證續保」、雜費額度夠不夠。' : '目前沒有住院醫療。最基礎、最高頻會用到的一塊,建議優先補。', 90),
        mk('accident', hasAccident ? 'have' : 'gap', hasAccident ? '已有意外險。' : '用小錢撐高保額,失能(不是只有全殘)才是會拖垮家庭的風險。', 78),
        mk('critical', crit.gap > 0 ? 'gap' : 'have', crit.gap > 0 ? ('缺口約 ' + wan(crit.gap) + '(建議額度 ' + wan(crit.target) + '、目前 ' + (critCurrent ? wan(critCurrent) : '0') + ')。一次金確診即領,可付自費標靶/免疫藥。') : ('已有約 ' + wan(critCurrent) + ' 一次金保障。'), 70),
        (!hasDep
          ? mk('life', 'na', '沒有人靠你的收入生活,壽險需求很低、可以擺最後。', 5)
          : mk('life', lg.gap > 0 ? 'gap' : 'have', lg.gap > 0 ? ('缺口約 ' + wan(lg.gap) + ',有家庭責任、優先級很高,定期壽險補這段很便宜。') : '現有壽險已覆蓋家庭責任、已足。', 95)),
        mk('ltc', ltcCurrent > 0 ? 'partial' : 'gap', ltcCurrent > 0 ? '已有部分長照/失能保障。' : ('準備金約 ' + wan(reserve) + '(今值)、目前未保。最貴的長期工程——先用公共資源、再量力而為。'), 45),
      ].filter(function (r) { return r.a; });
      var ord = { gap: 0, partial: 1, have: 2, na: 3 };
      rows.sort(function (x, y) { return (ord[x.status] - ord[y.status]) || (y.urgency - x.urgency); });
      return rows;
    })();
    function buildCheatsheet() {
      var L = [];
      L.push('【我的保障盤點】');
      L.push('我:' + currentAge + ' 歲、' + (hasDep ? ('有扶養(' + depDesc + ')') : '目前無扶養'));
      L.push('');
      var gaps = planRows.filter(function (r) { return r.status === 'gap' || r.status === 'partial'; });
      if (gaps.length) {
        L.push('▍要補 / 要檢視的(照優先序):');
        gaps.forEach(function (r) {
          L.push('• ' + r.a.name + ':' + r.line);
          if (r.a.action && r.a.action.spec) L.push('   想要的規格:' + r.a.action.spec.split('。')[0] + '。');
        });
        L.push('');
      }
      var have = planRows.filter(function (r) { return r.status === 'have'; });
      if (have.length) { L.push('▍已經有的:' + have.map(function (r) { return r.a.name; }).join('、')); L.push(''); }
      var asks = [];
      gaps.forEach(function (r) { if (r.a.action && r.a.action.ask) r.a.action.ask.forEach(function (q) { asks.push('• ' + q); }); });
      if (asks.length) { L.push('▍簽約前我要問的:'); asks.slice(0, 9).forEach(function (q) { L.push(q); }); L.push(''); }
      L.push('(由 Helm 個人試算整理、非保險建議;保費與理賠以各公司最新條款為準)');
      return L.join('\n');
    }

    return (
      <div className="fpage" role="dialog" aria-modal="true" aria-label="保障規劃">
        <div className="fpage__panel">
          <header className="fpage__bar"><button className="fpage__cancel" onClick={onClose}><i className="ph ph-arrow-left" aria-hidden="true" />返回</button><span className="fpage__title">保障規劃</span><span aria-hidden="true" /></header>
          <div className="fpage__scroll"><div className="fpage__body">

            <p className="pl-disc">依你的保單(文件金庫)＋家庭/資產,排出「<b>先補什麼、找什麼規格、合理保費、怎麼開口</b>」。看懂後帶著下面的小抄去跟業務談。教育性質試算、<b>非保險建議,不針對特定商品</b>。</p>

            {!hasBirth && <div className="fx-advice fx-advice--mid"><span className="fx-advice__txt">⚠️ 你還沒設生日 —— 下面的年齡/時間軸先以 <b>35 歲示意</b>。到「設定 → 個人資料」填生日,才會算你真實的年齡與各項保障到幾歲。</span></div>}
            {!docs.length && <div className="fx-advice fx-advice--low"><span className="fx-advice__txt">你還沒登錄任何保單 —— 下面的缺口是<b>依你的家庭 + 資產</b>算的(現有保障當 0)。把保單丟進「文件金庫」,時間軸與「已有保障」會更完整。 <button type="button" className="pl-docbtn" onClick={onOpenDocs}><i className="ph ph-folder-open" aria-hidden="true" /> 前往文件金庫</button></span></div>}

            {/* 卡A 保障行動清單(新門面:個人化、排序、可執行)*/}
            {planRows.length > 0 && (
              <section className="fpage__card pl-plan">
                <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-list-checks" aria-hidden="true" /> 我的保障行動清單</span><span className="fpage__card-hint">依你的缺口排序</span></div>
                {planRows.map(function (r) {
                  var st = STATUS[r.status], open = openAct === r.area, act = r.a.action || {};
                  return (
                    <div key={r.area} className={'pl-act pl-act--' + st.c}>
                      <button type="button" className="pl-act__head" onClick={function () { setOpenAct(open ? null : r.area); }} aria-expanded={open}>
                        <span className={'pl-act__pill pl-act__pill--' + st.c}>{st.t}</span>
                        <span className="pl-act__name">{r.a.name}</span>
                        <i className={'ph ' + (open ? 'ph-caret-up' : 'ph-caret-down')} aria-hidden="true" />
                      </button>
                      <p className="pl-act__line">{r.line}</p>
                      {open && act.spec && (
                        <div className="pl-act__body">
                          {act.headsup && <p className="pl-act__heads"><i className="ph ph-warning-circle" aria-hidden="true" /> {act.headsup}</p>}
                          <p className="pl-act__row"><b>該找什麼</b>{act.spec}</p>
                          <p className="pl-act__row"><b>合理保費</b>{act.premiumQuick}</p>
                          <div className="pl-act__script"><span className="pl-act__tag"><i className="ph ph-chat-circle-text" aria-hidden="true" /> 跟業務可以這樣說</span>{act.script}</div>
                          <div className="pl-act__ask"><span className="pl-act__tag"><i className="ph ph-seal-question" aria-hidden="true" /> 簽約前一定要問</span><ul>{act.ask.map(function (q, i) { return <li key={i}>{q}</li>; })}</ul></div>
                        </div>
                      )}
                    </div>
                  );
                })}
                <p className="pl-hint">缺口金額由你的保單與扶養設定即時算出;保費/規格為 2026 類型概估、非特定商品報價。想自己調缺口數字,點最下面「完整試算」。</p>
              </section>
            )}

            {/* 卡B 跟業務談的小抄(可複製帶走)*/}
            {planRows.length > 0 && (
              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-clipboard-text" aria-hidden="true" /> 跟業務談的小抄</span></div>
                <p className="pl-note" style={{ marginTop: 0 }}>你的狀況、要補的、要問的,整理成一張清單——複製帶去談,你就知道自己要什麼、不會被牽著走。</p>
                <pre className="pl-cheat">{buildCheatsheet()}</pre>
                <button type="button" className="pl-copy" onClick={function () { try { navigator.clipboard.writeText(buildCheatsheet()); setCopied(true); setTimeout(function () { setCopied(false); }, 1800); } catch (e) {} }}>
                  <i className={'ph ' + (copied ? 'ph-check' : 'ph-copy')} aria-hidden="true" /> {copied ? '已複製' : '複製小抄'}
                </button>
              </section>
            )}

            {/* 卡1 時間軸 */}
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">保障時間軸 · 你現在 {currentAge} 歲</span></div>
              <Timeline items={items} currentAge={currentAge} planEnd={planEnd} />
              <p className="pl-note">紅點＝該保障結束的年齡;紅框＝目前未保。把保單補進文件金庫,這裡會自動更新。</p>
            </section>

            {/* 卡C 保險與稅(2026)*/}
            {itypes && itypes.tax && (
              <section className="fpage__card">
                <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-receipt" aria-hidden="true" /> {itypes.tax.title}</span></div>
                {itypes.tax.items.map(function (it, i) { return <div key={i} className="pl-tax"><div className="pl-tax__q">{it.q}</div><p className="pl-tax__a">{it.a}</p></div>; })}
                {itypes.tax.note && <p className="pl-hint">{itypes.tax.note}</p>}
              </section>
            )}

            {/* 完整試算 + 各類型比較:預設收起,別一次轟炸 */}
            <button type="button" className="pl-more" onClick={function () { setShowDetail(!showDetail); }} aria-expanded={showDetail}>
              <i className={'ph ' + (showDetail ? 'ph-caret-up' : 'ph-sliders-horizontal')} aria-hidden="true" /> {showDetail ? '收起完整試算' : '想自己調數字 · 看完整缺口試算與各類型比較'}
            </button>
            {showDetail && (<>

            {/* 卡2 長照 */}
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">長照準備金(最大的長期工程)</span></div>
              <div className="pl-big"><span className="pl-big__num">約 {wan(reserve)}</span><span className="pl-big__unit">今日現值</span></div>
              <p className="pl-note">若 {ltc.start} 歲起需照顧 {ltc.years} 年,這是<b>今天要等值準備</b>的金額(已折現)。是「真的發生時要全額拿出來」的數字,不是機率加權的平均。</p>
              <Slider label="每月照顧費" value={ltc.monthly} min={27000} max={70000} step={1000} onChange={function (v) { setLtc(Object.assign({}, ltc, { monthly: v })); }} fmt={function (v) { return (v / 10000).toFixed(1) + '萬/月'; }} />
              <Slider label="醫療/長照通膨" value={ltc.med} min={0.02} max={0.05} step={0.005} onChange={function (v) { setLtc(Object.assign({}, ltc, { med: v })); }} fmt={function (v) { return (v * 100).toFixed(1) + '%'; }} />
              <Slider label="需照顧年數" value={ltc.years} min={3} max={12} step={1} onChange={function (v) { setLtc(Object.assign({}, ltc, { years: v })); }} fmt={function (v) { return v + ' 年'; }} />
              <Slider label="幾歲開始" value={ltc.start} min={70} max={90} step={1} onChange={function (v) { setLtc(Object.assign({}, ltc, { start: v })); }} fmt={function (v) { return v + ' 歲'; }} />
              <p className="pl-warn"><i className="ph ph-info" aria-hidden="true" /> 這筆純靠投資較吃力:長照常在高齡、資產正消耗、又可能碰上股市回落時發生,被迫低點變現會打殘退休金。保險的價值在「不必在最壞時點賣資產」,而非報酬率。</p>
            </section>

            {/* 卡3 重疾 */}
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">重大疾病 / 癌症一次金</span></div>
              <div className="pl-row"><span>建議目標</span><b>{wan(crit.target)}</b></div>
              <div className="pl-row"><span>你目前</span><b className={critCurrent ? '' : 'pl-zero'}>{critCurrent ? wan(critCurrent) : '0(未保)'}</b></div>
              <div className="pl-row pl-row--gap"><span>缺口</span><b className={crit.gap ? 'pl-zero' : ''}>{wan(crit.gap)}</b></div>
              <Slider label="你的年收入(估)" value={income} min={300000} max={2000000} step={50000} onChange={setIncome} fmt={function (v) { return wan(v); }} />
              <p className="pl-note">台灣癌症自費新藥常達百萬(調查:逾 6 成自費破 30 萬、近 1/4 破 200 萬)。年輕時保費便宜,是工作期較划算補的缺口。</p>
            </section>

            {/* 卡4 身故(需求法) */}
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">身故 / 壽險(需求法)</span></div>
              <div className="pl-deps">
                <label className="pl-toggle"><input type="checkbox" checked={dep.spouse} onChange={function (e) { saveDep(Object.assign({}, dep, { spouse: e.target.checked })); }} /><span>有配偶</span></label>
                <Stepper label="受扶養父母" value={dep.parents} min={0} max={3} onChange={function (v) { saveDep(Object.assign({}, dep, { parents: v })); }} />
                <Stepper label="子女" value={dep.children} min={0} max={6} onChange={function (v) { saveDep(Object.assign({}, dep, { children: v })); }} />
              </div>
              {!hasDep ? (
                <p className="pl-ok"><i className="ph ph-check-circle" aria-hidden="true" /> 沒有受扶養人時,身故沒有「要替別人撐生活費」的責任,缺口趨近 0;現有壽險{lifeSum ? '(' + wan(lifeSum) + ')' : ''}通常已夠處理身後事。</p>
              ) : (
                <div>
                  <p className="pl-note" style={{ marginTop: 4 }}>你有需要照顧的家人,身故缺口要用「遺族需求法」算:把家人未來要用的錢加總,扣掉你已有的資產與壽險。</p>
                  <Slider label="遺族每年生活費" value={survivorCost} min={120000} max={1200000} step={20000} onChange={setSurvivorCost} fmt={function (v) { return wan(v) + '/年'; }} />
                  <Slider label="需支應年數" value={supportYears} min={5} max={40} step={1} onChange={setSupportYears} fmt={function (v) { return v + ' 年'; }} />
                  <Slider label="要還清的負債" value={debt} min={0} max={20000000} step={100000} onChange={setDebt} fmt={function (v) { return wan(v); }} />
                  <p className="pl-hint">負債預設帶入你的總負債{H.totalLiab ? '(' + wan(H.totalLiab) + ')' : ''}。若房貸已有「房貸壽險」會在身故時還清,可把房貸金額扣掉。{dep.children ? ' 已含子女教育金 ' + wan(eduFund) + '(每人 400 萬)。' : ''}</p>
                  <div className="pl-row" style={{ marginTop: 8 }}><span>家庭未來需求合計</span><b>{wan(lg.need)}</b></div>
                  <div className="pl-row"><span>你的流動資產 + 現有壽險</span><b>{wan(lg.resource)}</b></div>
                  <div className="pl-row pl-row--gap"><span>身故保障缺口</span><b className={lg.gap ? 'pl-zero' : ''}>{lg.gap ? wan(lg.gap) : '0 · 已足'}</b></div>
                  {lg.gap > 0 && <p className="pl-warn"><i className="ph ph-warning" aria-hidden="true" /> 你現有壽險 {wan(lifeSum)},距家人需要還差約 {wan(lg.gap)}。定期壽險補這段保費很低(這金額的定期壽險年繳通常幾千元),是有家庭後優先級很高的缺口。</p>}
                </div>
              )}
            </section>

            {/* 卡5 保險 vs 投資 + 尾端壓測 */}
            <section className="fpage__card">
              <div className="fpage__card-head"><span className="t-overline">保險 vs 投資</span></div>
              <p className="pl-note" style={{ marginTop: 0 }}>① 機會成本:你的<b>壽險</b>年繳約 {wan(lifePremium || 11138)}(全部保險合計約 {wan(totalPremium || 17240)}/年、半年繳一次約 {wan((totalPremium || 17240) / 2)})。把這筆壽險保費改拿去投資(20 年繳、30 歲起算)會是:</p>
              <div className="pl-row"><span>投資到 65 歲</span><b>{wan(inv(65))}</b></div>
              <div className="pl-row"><span>投資到 85 歲</span><b>{wan(inv(85))}</b></div>
              <div className="pl-row pl-row--gap"><span>保險端(身故保障)</span><b>{wan(lifeSum)} · 終身</b></div>
              <Slider label="假設年化報酬" value={retRate} min={0.02} max={0.08} step={0.002} onChange={setRetRate} fmt={function (v) { return (v * 100).toFixed(1) + '%'; }} />
              <p className="pl-hint">投資累積通常多很多(終身壽險的內部報酬率約只有 1~2%);但保險給的是「身故立刻給付」的保障與避險,不是報酬——兩者目的不同,不是誰取代誰。</p>

              <div className="fpage__card-head" style={{ marginTop: 16 }}><span className="t-overline">② 尾端壓力測試</span></div>
              <p className="pl-note" style={{ marginTop: 0 }}>萬一 {critAge} 歲一場重疾要自費 {wan(critCost)},又剛好碰到股市回落 {Math.round(dd * 100)}%:</p>
              <Slider label="重疾發生年齡" value={critAge} min={70} max={95} step={1} onChange={setCritAge} fmt={function (v) { return v + ' 歲'; }} />
              <Slider label="一次性自費" value={critCost} min={500000} max={5000000} step={100000} onChange={setCritCost} fmt={function (v) { return wan(v); }} />
              <Slider label="同期市場回落" value={dd} min={0.1} max={0.5} step={0.05} onChange={setDd} fmt={function (v) { return Math.round(v * 100) + '%'; }} />
              <div className="pl-comp">
                <div className="pl-comp__col pl-comp__col--good"><span className="pl-comp__h">有保險</span><b>理賠直接給 {wan(critCost)}</b><span>投資組合一毛不動,等市場回來</span></div>
                <div className="pl-comp__col pl-comp__col--bad"><span className="pl-comp__h">沒保險(自費)</span><b>被迫賣掉高點值 {wan(fs.peakValueSold)}</b><span>比理賠多燒約 {wan(fs.extraLoss)},這筆未來複利也沒了</span></div>
              </div>
              <p className="pl-hint">這就是為什麼長照/重疾不適合「純靠投資扛」:不是報酬問題,是「被迫在最壞時點變現」會永久打殘退休金。</p>
            </section>

            {/* 卡6 怎麼補缺口 · 市場上的選擇(類型層級教育,非特定商品) */}
            {itypes && itypes.areas && (
              <section className="fpage__card pl-fill">
                <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-list-checks" aria-hidden="true" /> 怎麼補缺口 · 市場上有哪些選擇</span></div>
                <p className="pl-note" style={{ marginTop: 0 }}>依你目前的缺口,市場上能補這塊的商品<b>類型</b>與怎麼挑——讓你看懂選項、能自己上網投保、不被單一業務員綁住。<b>這是類型教材,不是推薦特定公司商品(那要找有證照的人),也不是叫你買。</b></p>
                {itypes.areas.map(function (a) {
                  var g = gapOf(a.area);
                  var open = openArea === a.area;
                  return (
                    <div key={a.area} className={'pl-fill__area' + (g.gap ? ' pl-fill__area--gap' : '')}>
                      <button type="button" className="pl-fill__head" onClick={function () { setOpenArea(open ? null : a.area); }} aria-expanded={open}>
                        <span className="pl-fill__name">{a.name}</span>
                        <span className={'pl-fill__badge' + (g.gap ? ' pl-fill__badge--gap' : '')}>{g.txt}</span>
                        <i className={'ph ' + (open ? 'ph-caret-up' : 'ph-caret-down')} aria-hidden="true" />
                      </button>
                      {open && (
                        <div className="pl-fill__body">
                          {a.types.map(function (t, i) {
                            return (
                              <div key={i} className="pl-type">
                                <div className="pl-type__name">{t.name}</div>
                                <p className="pl-type__blurb">{t.blurb}</p>
                                {t.example && <div className="pl-type__eg"><span className="pl-type__tag"><i className="ph ph-first-aid-kit" aria-hidden="true" /> 真的理賠長這樣</span>{t.example}</div>}
                                {t.fit && <p className="pl-type__line"><b>適合</b>{t.fit}</p>}
                                {t.tradeoff && <p className="pl-type__line"><b>取捨</b>{t.tradeoff}</p>}
                                {t.premiumHint && <p className="pl-type__line"><b>大概保費</b>{t.premiumHint}</p>}
                                {t.benchmark && <div className="pl-type__bm"><span className="pl-type__tag"><i className="ph ph-target" aria-hidden="true" /> 額度抓多少算夠</span>{t.benchmark}</div>}
                                {t.compare && t.compare.length > 0 && <p className="pl-type__cmp"><b>挑選看這幾點</b>{t.compare.join('、')}</p>}
                              </div>
                            );
                          })}
                          {a.note && <p className="pl-fill__tip"><i className="ph ph-lightbulb" aria-hidden="true" /> {a.note}</p>}
                        </div>
                      )}
                    </div>
                  );
                })}
                <p className="pl-hint">保費級距、理賠條件僅為「類型」概估,實際以各公司最新保單條款與法規為準。本工具為教育試算、非保險招攬或建議、不針對特定商品。重大決定建議找 2~3 家(含保險經紀人)比較、看清條款、回官方核對。</p>
              </section>
            )}
            </>)}

            {/* 卡7 看懂保單 + 去哪查真商品(自己動手工具箱,教育非招攬) */}
            {itypes && (itypes.readPolicy || itypes.directory) && (
              <section className="fpage__card">
                {itypes.readPolicy && (
                  <div>
                    <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-magnifying-glass" aria-hidden="true" /> 看懂一張保單 · 條款紅旗</span></div>
                    <p className="pl-note" style={{ marginTop: 0 }}>{itypes.readPolicy.intro}</p>
                    <ol className="pl-rp">
                      {itypes.readPolicy.items.map(function (it, i) {
                        return (
                          <li key={i} className="pl-rp__item">
                            <div className="pl-rp__q"><span className="pl-rp__n">{i + 1}</span>{it.q}</div>
                            <p className="pl-rp__plain">{it.plain}</p>
                            {it.flag && <p className="pl-rp__flag"><i className="ph ph-flag" aria-hidden="true" /> {it.flag}</p>}
                          </li>
                        );
                      })}
                    </ol>
                    {itypes.readPolicy.outro && <p className="pl-hint">{itypes.readPolicy.outro}</p>}
                    <button type="button" className="pl-docbtn" onClick={onOpenDocs}><i className="ph ph-folder-open" aria-hidden="true" /> 前往文件金庫 · 把條款丟進來,我陪你讀</button>
                  </div>
                )}
                {itypes.directory && (
                  <div style={{ marginTop: itypes.readPolicy ? 18 : 0 }}>
                    <div className="fpage__card-head"><span className="t-overline"><i className="ph ph-stack" aria-hidden="true" /> 去哪查真商品 · 自己比</span></div>
                    <p className="pl-note" style={{ marginTop: 0 }}>{itypes.directory.intro}</p>
                    {itypes.directory.groups.map(function (g, gi) {
                      return (
                        <div key={gi} className="pl-dir__g">
                          <div className="pl-dir__t">{g.title}</div>
                          {g.items.map(function (it, i) {
                            return it.url
                              ? <a key={i} className="pl-where__row" href={it.url} target="_blank" rel="noopener noreferrer"><span className="pl-where__name">{it.name}<i className="ph ph-arrow-square-out" aria-hidden="true" /></span><span className="pl-where__note">{it.note}</span></a>
                              : <div key={i} className="pl-where__row"><span className="pl-where__name">{it.name}</span><span className="pl-where__note">{it.note}</span></div>;
                          })}
                        </div>
                      );
                    })}
                    {itypes.directory.sop && <p className="pl-fill__tip"><i className="ph ph-list-numbers" aria-hidden="true" /> {itypes.directory.sop}</p>}
                    <p className="pl-hint">這些是「去哪查」的指路,不是商品推薦。具體商品的保費、理賠、限制請一律以各公司最新條款為準;比較網站僅供入門認識、部分有業配。</p>
                  </div>
                )}
              </section>
            )}

            <p className="pl-foot">假設可調、數字即時更新。可直接拿給保險朋友核對——公式與預設值來自台灣官方資料(內政部生命表、主計總處、衛福部、健保署),並經多方試算交叉驗證。</p>

          </div></div>
        </div>
      </div>
    );
  }

  window.ProtectionScreen = ProtectionScreen;
})();
