Skill: ramos-dev/family-financial-planner (version 1.2.0)

--- name: family-financial-planner version: 1.2.0 description: | 面向重大人生财务决策（辞职、创业、转行、全职带娃、换城市、Gap Year）的家庭财务全景分析与建模。 构建交互式 HTML 仪表盘，帮助用户评估家庭财务状况、计算生存跑道、模拟多种情景。 触发词：家庭财务分析、辞职财务分析、创业财务规划、资产负债分析、生存月数、跑道计算、 回归职场工资、止损线、辞职vs留任对比、够撑多久、够花多久、能不能辞职、辞职后怎么活、 能不能躺平、躺平多久、财务自由测算、家庭资产盘点、无收入能撑多久。 不适用于：日常记账、个人流水管理、投资理财收益计算、公司财务报表分析。 --- # 家庭财务全景分析 Skill ## Purpose 构建一个交互式单页 HTML 仪表盘，帮助用户在"辞职/创业/重大人生转折"场景下，全面评估家庭财务状况、 计算生存跑道、模拟多种情景，并给出可视化结论。 ## When to Use - 用户面临辞职/创业/转行/Gap Year/躺平等重大财务决策 - 需要建立家庭完整的资产负债表 - 需要计算无收入时的生存月数（"钱够花多久"） - 需要对比"立即行动 vs 延期行动"两个场景的财务差异 - 需要规划家庭 3-5 年的支出变化（尤其有小孩的家庭） - 需要估算"回去上班需要赚多少" - 需要建立财务止损机制 - 需要评估无息借款（亲友、垫资等）对财务安全的影响 ## Workflow ### Phase 1: 信息采集 通过自然对话收集用户的完整财务画像。**不要一次性抛出所有问题**，分批次、按优先级提问。 #### 第一轮（核心必需）： 1. **所在城市**：在哪个城市生活？（用于匹配社保参数、生活成本基线、公积金上限、通胀假设） 2. **决策场景**：辞职创业？换城市？全职带娃？出国留学？ 3. **时间窗口**：是否有两个对比时间点（如"现在 vs 3 个月后"）？ 4. **家庭结构**：几口人？小孩年龄？是否需要保姆/育儿嫂？ 5. **月收入**：税前工资、到手工资、被动收入（租金/理财） 6. **流动资产**：存款、股票/基金、理财产品（万元） 7. **固定资产**：房产估值（自住/投资分别列出） 8. **负债**：房贷（余额/月供/利率）、车贷、信用贷、其他 9. **月度刚性支出**：房贷/租金、保姆、伙食、孩子、水电物业、车、保险 #### 第二轮（精细化）： 10. **特殊负债与一次性支出**：是否有无息贷款需一次性归还（如亲友借款、购房垫资、消费分期等）？RSU/期权未归属？年终奖？离职补偿（N+1）？ 11. **创业/项目计划**：运营成本？几个月能见收入？ 12. **社保方案**：灵活就业社保？配偶社保？（参考 `references/city-profiles.md` 中该城市的灵活就业费用） 13. **通胀假设**：默认按城市取值（一线 3.5%、新一线 2.5~3.0%），可调整 14. **投资回报假设**：默认 2.5%（余额宝/国债级别保守假设） 15. **备用金月数**：默认 6 个月，有小孩建议 8 个月 ### Phase 2: 数据建模 读取 `references/financial-model-spec.md` 获取完整的计算模型规范， 读取 `references/city-profiles.md` 获取城市社保参数、灵活就业费用和生活成本基线。 根据用户所在城市： 1. 加载对应的 `CITY_SSI_PROFILES[城市]` 作为社保/公积金计算参数 2. 加载 `CITY_FLEX_SOCIAL[城市]` 作为辞职后灵活就业社保参考 3. 加载 `CITY_LIVING_COST[城市]` 作为生活支出基线（用户没给出具体数字时的起点） 4. 使用城市特定通胀率（一线 3.5%、新一线 2.5~3%） 核心计算模块包括： - 资产负债净值计算 - 月度收支模型（在职 vs 离职） - 通胀调整的 N 年支出时间线 - 两种场景对比（立即行动 vs 延期行动） - 等额本息贷款摊销 - 动态生存模拟（含投资回报+通胀） - 反推税前工资（中国个税累进制） ### Phase 3: HTML 仪表盘生成 读取 `assets/dashboard-template.html` 获取完整的 HTML 模板，然后： 1. **替换所有 `__PLACEHOLDER__` 占位符**为用户的实际数据 2. **裁剪不适用的 Tab**（如用户没有 RSU 则移除 Tab 1；没有创业计划则移除 OPC 分析） 3. **适配家庭结构**（如双职工 vs 单职工、有无小孩、有无保姆） 4. 确保所有 SVG 图表、对比表格、情景分析与数据一致 ### Phase 4: 盲区检查 模型完成后，主动检查以下常见财务盲区： | 盲区 | 检查项 | 默认处理 | |------|--------|----------| | 通胀 | 支出是否按年通胀调整？ | 3.5%/年复合 | | 配偶社保 | 配偶如果不上班，灵活社保费？ | 纳入月支出 | | 公积金 | 离职后公积金贷款谁还？余额多少？ | 立即自付 | | 无息贷款 | 是否有需触发条件一次性归还的借款（亲友、垫资、分期等）？ | 纳入离职即付成本 | | 备用金 | 备用金是否足够？有小孩建议 8 个月 | 8 个月 | | 投资回报 | 闲置资金是否有理财收益？ | 2.5%/年 | | 止损线 | 是否设了"必须找工作"的资产底线？ | 备用金 + 求职期开销 | | 职场空窗 | 空窗 1-5 年对薪资的影响？ | 按行业估算折损 | ### Phase 5: 输出与交付 1. 生成完整的单文件 HTML 仪表盘（所有 CSS/JS 内联，零外部依赖，可直接用浏览器打开） 2. 给出关键结论摘要（3-5 条核心发现） 3. 如用户需要，生成一段"给家人看的通俗总结" ## Key Design Principles 1. **单文件交付**：所有 CSS/JS 内联在一个 HTML 文件中，零依赖，可以直接用浏览器打开 2. **Carbon Design System**：使用 IBM Carbon Design 风格，专业、克制、数据优先 3. **多城市适配**：内置 12 个主要城市的社保/公积金/生活成本参数，自动按城市配置 4. **中国税务惯例**：个税七级累进、社保/公积金比例按城市差异化、RSU 税务处理 5. **中国股市配色**：涨红跌绿 6. **数据驱动**：所有显示数字由 JS 变量计算派生，修改一个参数全局联动 7. **Tab 化组织**：每个分析维度一个 Tab，避免信息过载 ## Reference Files - `references/financial-model-spec.md` — 完整的计算模型规范（公式、逻辑、边界条件） - `references/city-profiles.md` — 12 城市社保参数、灵活就业费用、生活成本基线 - `assets/dashboard-template.html` — HTML 仪表盘模板（含所有 Tab 结构和 CSS）

path: assets/dashboard-template.html
content: <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>家庭财务全景分析 · __USER_NAME__</title>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
  /* ===== Carbon Design System Tokens ===== */
  :root {
    --cds-background: #f4f4f4;
    --cds-background-hover: #e8e8e8;
    --cds-layer-01: #ffffff;
    --cds-layer-02: #f4f4f4;
    --cds-layer-hover-01: #e8e8e8;
    --cds-border-subtle: #e0e0e0;
    --cds-border-strong: #8d8d8d;
    --cds-text-primary: #161616;
    --cds-text-secondary: #525252;
    --cds-text-placeholder: #a8a8a8;
    --cds-text-helper: #6f6f6f;
    --cds-text-on-color: #ffffff;
    --cds-link-primary: #0f62fe;
    --cds-link-primary-hover: #0043ce;
    --cds-interactive: #0f62fe;
    --cds-support-error: #da1e28;
    --cds-support-success: #198038;
    --cds-support-warning: #f1c21b;
    --cds-support-info: #0043ce;
    /* Financial semantic (中国股市：涨红跌绿) */
    --fin-up: #da1e28;
    --fin-down: #198038;
    --fin-danger: #da1e28;
    --fin-success: #198038;
    --fin-warning: #f1c21b;
    --fin-info: #0043ce;
    --fin-purple: #8a3ffc;
    --fin-teal: #009d9a;
    --fin-magenta: #d02670;
    /* Spacing */
    --cds-spacing-03: 8px;
    --cds-spacing-04: 12px;
    --cds-spacing-05: 16px;
    --cds-spacing-06: 24px;
    --cds-spacing-07: 32px;
    /* Compat aliases */
    --primary: var(--cds-interactive);
    --primary-light: #edf5ff;
    --success: var(--fin-success);
    --warning: #b28600;
    --danger: var(--fin-danger);
    --text-title: var(--cds-text-primary);
    --text-body: var(--cds-text-secondary);
    --text-secondary: var(--cds-text-helper);
    --bg: var(--cds-background);
    --card: var(--cds-layer-01);
    --border: var(--cds-border-subtle);
  }

  * { margin:0; padding:0; box-sizing:border-box; }

  body {
    font-family: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif;
    background: var(--cds-background);
    color: var(--cds-text-primary);
    line-height: 1.5;
    min-height: 100vh;
    font-size: 14px;
    -webkit-font-smoothing: antialiased;
  }

  /* Carbon Header */
  .app-header {
    background: #161616;
    color: var(--cds-text-on-color);
    padding: var(--cds-spacing-07) var(--cds-spacing-06) var(--cds-spacing-06);
    text-align: center;
    border-bottom: 3px solid var(--cds-interactive);
  }
  .app-header h1 { font-size: 28px; font-weight: 600; letter-spacing: -0.02em; margin-bottom: 4px; }
  .app-header p { font-size: 14px; opacity: 0.7; }
  .app-header .date-tag {
    display: inline-block;
    background: rgba(255,255,255,0.1);
    padding: 4px 16px;
    font-size: 12px;
    margin-top: 8px;
    font-family: 'IBM Plex Mono', monospace;
  }

  .container { max-width: 1200px; margin: 0 auto; padding: var(--cds-spacing-06) var(--cds-spacing-05) 80px; }

  /* Carbon Tabs */
  .tab-nav {
    display: flex;
    gap: 0;
    background: var(--cds-layer-01);
    margin-bottom: var(--cds-spacing-06);
    border-bottom: 2px solid var(--cds-border-subtle);
    position: sticky;
    top: 0;
    z-index: 10;
    flex-wrap: wrap;
  }
  .tab-btn {
    padding: 12px 16px;
    border: none;
    background: none;
    font-size: 14px;
    font-weight: 500;
    color: var(--cds-text-secondary);
    cursor: pointer;
    transition: all 0.15s;
    min-width: 80px;
    border-bottom: 3px solid transparent;
    margin-bottom: -2px;
    white-space: nowrap;
  }
  .tab-btn.active {
    color: var(--cds-text-primary);
    border-bottom-color: var(--cds-interactive);
    font-weight: 600;
  }
  .tab-btn:hover:not(.active) {
    color: var(--cds-text-primary);
    background: var(--cds-background-hover);
  }
  .tab-panel { display: none; }
  .tab-panel.active { display: block; }

  /* Carbon Card / Tile */
  .card {
    background: var(--cds-layer-01);
    padding: var(--cds-spacing-06);
    margin-bottom: var(--cds-spacing-05);
  }
  .card h2 {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: var(--cds-spacing-05);
    padding-bottom: var(--cds-spacing-04);
    border-bottom: 1px solid var(--cds-border-subtle);
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .card h3 {
    font-size: 14px;
    font-weight: 600;
    margin: var(--cds-spacing-05) 0 var(--cds-spacing-03);
  }

  /* Dashboard Stat Cards */
  .dashboard {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: var(--cds-spacing-05);
    margin-bottom: var(--cds-spacing-06);
  }
  .stat-card {
    background: var(--cds-layer-01);
    padding: var(--cds-spacing-05);
    text-align: left;
    border-left: 3px solid var(--cds-interactive);
    transition: background 0.15s;
  }
  .stat-card:hover { background: var(--cds-layer-hover-01); }
  .stat-card .label { font-size: 12px; color: var(--cds-text-helper); margin-bottom: var(--cds-spacing-03); text-transform: uppercase; letter-spacing: 0.32px; font-weight: 500; }
  .stat-card .value { font-size: 24px; font-weight: 600; }
  .stat-card .sub { font-size: 12px; color: var(--cds-text-helper); margin-top: 4px; }
  .positive { color: var(--fin-up); }
  .negative { color: var(--fin-down); }
  .neutral { color: var(--cds-interactive); }

  /* Data Table */
  .detail-table { width: 100%; border-collapse: collapse; margin-top: var(--cds-spacing-04); font-size: 14px; }
  .detail-table th, .detail-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--cds-border-subtle); vertical-align: top; }
  .detail-table th { background: var(--cds-layer-02); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.32px; border-bottom: 1px solid var(--cds-border-strong); }
  .detail-table tbody tr:hover td { background: var(--cds-background-hover); }
  .detail-table .total-row { font-weight: 600; background: var(--cds-layer-02); border-top: 2px solid var(--cds-border-strong); }

  /* Progress Bar */
  .progress-wrap { margin: 12px 0; }
  .progress-label { display: flex; justify-content: space-between; font-size: 12px; color: var(--cds-text-secondary); margin-bottom: 6px; }
  .progress-bar { height: 8px; background: var(--cds-border-subtle); overflow: hidden; }
  .progress-bar .fill { height: 100%; transition: width 0.5s ease; }
  .fill-safe { background: var(--fin-success); }
  .fill-warn { background: var(--fin-warning); }
  .fill-danger { background: var(--fin-danger); }

  /* Verdict / Notification */
  .verdict-box { padding: var(--cds-spacing-06); text-align: center; margin: var(--cds-spacing-06) 0; }
  .verdict-box.safe { background: #defbe6; border-left: 4px solid var(--fin-success); text-align: left; }
  .verdict-box.caution { background: #fff8e1; border-left: 4px solid #b28600; text-align: left; }
  .verdict-box.risky { background: #fff1f1; border-left: 4px solid var(--fin-danger); text-align: left; }
  .verdict-box .verdict-icon { font-size: 32px; margin-bottom: 8px; }
  .verdict-box .verdict-title { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
  .verdict-box .verdict-desc { font-size: 14px; color: var(--cds-text-secondary); line-height: 1.7; max-width: 800px; }

  /* Scenario Grid */
  .scenario-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--cds-spacing-05); }
  .scenario-card { padding: var(--cds-spacing-05); border-left: 3px solid var(--cds-border-subtle); background: var(--cds-layer-01); }
  .scenario-card h4 { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
  .scenario-card .scenario-val { font-size: 28px; font-weight: 600; margin: 6px 0; }

  /* Bar Chart */
  .wf-row { display: flex; align-items: center; margin-bottom: 6px; }
  .wf-label { width: 110px; font-size: 12px; text-align: right; padding-right: 12px; color: var(--cds-text-secondary); flex-shrink: 0; }
  .wf-bar-wrap { flex: 1; height: 24px; position: relative; }
  .wf-bar { height: 100%; position: absolute; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; color: #fff; min-width: 55px; transition: all 0.4s ease; }

  /* Info Banner */
  .info-banner { background: #edf5ff; border-left: 3px solid var(--cds-interactive); padding: var(--cds-spacing-05); margin-bottom: var(--cds-spacing-05); font-size: 14px; }
  .info-banner strong { font-weight: 600; }

  /* Tax Highlight */
  .tax-highlight { background: #fff8e1; border-left: 3px solid #b28600; padding: var(--cds-spacing-05); margin: var(--cds-spacing-05) 0; }
  .tax-highlight h4 { color: #b28600; font-size: 14px; font-weight: 600; margin-bottom: 8px; }
  .tax-highlight p { font-size: 14px; color: var(--cds-text-secondary); line-height: 1.7; }

  .key-number { display: inline-block; background: #edf5ff; padding: 2px 8px; font-weight: 600; color: var(--cds-interactive); font-size: 14px; font-family: 'IBM Plex Mono', monospace; }

  .split-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--cds-spacing-05); }

  /* Compare Table */
  .compare-table { width: 100%; border-collapse: collapse; margin-top: var(--cds-spacing-04); font-size: 14px; }
  .compare-table th, .compare-table td { padding: 12px 16px; text-align: center; border-bottom: 1px solid var(--cds-border-subtle); }
  .compare-table th { font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.32px; }
  .compare-table th:first-child, .compare-table td:first-child { text-align: left; }
  .compare-table .col-now { background: #fff1f1; }
  .compare-table .col-jul { background: #defbe6; }
  .compare-table .col-diff { background: #edf5ff; }
  .compare-table .header-now { background: var(--fin-danger); color: #fff; }
  .compare-table .header-jul { background: var(--fin-success); color: #fff; }
  .compare-table .header-diff { background: var(--cds-interactive); color: #fff; }
  .compare-table .header-label { background: var(--cds-layer-02); text-align: left; }
  .compare-table .total-row { font-weight: 600; background: var(--cds-layer-02); }
  .compare-table .highlight-row { background: #fff8e1; font-weight: 600; font-size: 15px; }

  .diff-badge { display: inline-block; padding: 2px 8px; font-size: 12px; font-weight: 600; font-family: 'IBM Plex Mono', monospace; }
  .diff-positive { background: #defbe6; color: var(--fin-success); }
  .diff-negative { background: #fff1f1; color: var(--fin-danger); }

  /* Big Comparison */
  .big-comparison { display: grid; grid-template-columns: 1fr 60px 1fr; gap: 0; margin: var(--cds-spacing-06) 0; }
  .big-comparison .comp-box { padding: var(--cds-spacing-06) var(--cds-spacing-05); text-align: center; }
  .big-comparison .comp-box.now-box { background: #fff1f1; border-left: 4px solid var(--fin-danger); }
  .big-comparison .comp-box.jul-box { background: #defbe6; border-left: 4px solid var(--fin-success); }
  .big-comparison .comp-vs { display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 700; color: var(--cds-text-placeholder); }
  .comp-box .comp-title { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
  .comp-box .comp-val { font-size: 36px; font-weight: 600; margin: 6px 0; }
  .comp-box .comp-sub { font-size: 12px; color: var(--cds-text-secondary); line-height: 1.6; }

  /* Responsive */
  @media (max-width: 672px) {
    .dashboard { grid-template-columns: 1fr 1fr; }
    .scenario-grid { grid-template-columns: 1fr; }
    .split-grid { grid-template-columns: 1fr; }
    .big-comparison { grid-template-columns: 1fr; gap: 10px; }
    .big-comparison .comp-vs { padding: 6px 0; }
    .tab-btn { font-size: 13px; padding: 10px 12px; }
  }
</style>
</head>
<body>

<!--
  ========================================
  TEMPLATE USAGE INSTRUCTIONS
  ========================================
  
  This is a structural template for the Family Financial Planner dashboard.
  
  To use this template:
  1. Replace all __PLACEHOLDER__ values in the <script> section with actual user data
  2. Remove any Tab sections that don't apply to the user's situation
  3. Adjust the tab-nav buttons to match the included tabs
  4. Add/modify family expense year plans based on user's family structure
  
  The template includes ALL possible tabs. Remove those not needed:
  - No stocks/RSU → remove Tab 1
  - No property → remove Tab 2, 3
  - No entrepreneurship → remove Tab 8 (OPC)
  - No two scenarios to compare → simplify Tab 5
  - No children → simplify Tab 7 (remove baby/nanny items)
  
  All __PLACEHOLDER__ markers indicate values to be replaced:
  - __VALUE__ = numeric value
  - __YUAN__ = amount in CNY yuan
  - __WAN__ = amount in 万 (10,000 CNY)
  - __PCT__ = percentage (e.g., 3.05 for 3.05%)
  - __DATE__ = date string
  - __TEXT__ = text string
  - __N__ = integer count
-->

<div class="app-header">
  <h1>家庭财务全景分析</h1>
  <p>__SCENARIO_DESCRIPTION__</p>
  <div class="date-tag">__DATE_TAG__</div>
</div>

<div class="container">

  <!-- Tab Navigation — adjust based on included tabs -->
  <div class="tab-nav">
    <button class="tab-btn active" onclick="switchTab(0)">资产总览</button>
    <!-- Add/remove tab buttons as needed -->
    <!-- <button class="tab-btn" onclick="switchTab(1)">股票 & RSU</button> -->
    <!-- <button class="tab-btn" onclick="switchTab(2)">房产 & 贷款</button> -->
    <!-- <button class="tab-btn" onclick="switchTab(3)">房贷成本</button> -->
    <button class="tab-btn" onclick="switchTab(4)">月度收支</button>
    <button class="tab-btn" onclick="switchTab(5)">场景对比</button>
    <button class="tab-btn" onclick="switchTab(6)">行动清单</button>
    <!-- <button class="tab-btn" onclick="switchTab(7)">家庭支出</button> -->
    <!-- <button class="tab-btn" onclick="switchTab(8)">创业分析</button> -->
    <!-- <button class="tab-btn" onclick="switchTab(9)">提前还贷</button> -->
    <button class="tab-btn" onclick="switchTab(10)" style="color:var(--fin-purple); font-weight:600;">盲区修正</button>
    <button class="tab-btn" onclick="switchTab(11)" style="color:var(--fin-magenta); font-weight:600;">回归职场</button>
  </div>

  <!-- Tab Panels — one div per tab -->
  <div class="tab-panel active" id="tab0"></div>
  <div class="tab-panel" id="tab1"></div>
  <div class="tab-panel" id="tab2"></div>
  <div class="tab-panel" id="tab3"></div>
  <div class="tab-panel" id="tab4"></div>
  <div class="tab-panel" id="tab5"></div>
  <div class="tab-panel" id="tab6"></div>
  <div class="tab-panel" id="tab7"></div>
  <div class="tab-panel" id="tab8"></div>
  <div class="tab-panel" id="tab9"></div>
  <div class="tab-panel" id="tab10"></div>
  <div class="tab-panel" id="tab11"></div>

</div>

<script>
// ===================== USER DATA — REPLACE ALL __PLACEHOLDERS__ =====================

// --- Exchange rates (if applicable) ---
// const HKD_CNY = __FX_RATE__;

// --- Stocks / Securities ---
// const totalStockCNY = __VALUE__;

// --- Properties ---
// const bjHouseValue = __WAN__;        // 自住房估值
// const bjMortgageRemain = __WAN__;    // 商贷余额
// const bjMonthlyMortgage = __YUAN__;  // 商贷月供
// const bjMortgageRate = __PCT__;      // 商贷利率

// --- Housing Fund Loan ---
// const gjjLoanTotal = __WAN__;
// const gjjLoanRemain = __WAN__;
// const gjjMonthlyPayment = __YUAN__;
// const gjjRate = __PCT__;
// const gjjMinPayment = __YUAN__;

// --- Investment Properties ---
// const xianHouseValue = __WAN__;
// const xianHouseRent = __YUAN__;      // 月租金

// --- Cash ---
// const cashSaving = __WAN__;

// --- Debts ---
// const interestFreeLoan = __WAN__;    // 需一次性归还的无息贷款（亲友借款/购房垫资/消费分期等）

// --- Model Parameters ---
const INFLATION_RATE = 0.035;   // 年通胀率
const EMERGENCY_MONTHS = 8;     // 紧急备用金月数
const INVEST_RETURN = 0.025;    // 投资年回报
const SPOUSE_SOCIAL = 0;        // 配偶灵活社保月缴（没有则为0）

// --- Salary (while employed) ---
// const salaryGross = __YUAN__;
// const salaryNet = __YUAN__;

// --- Living Expenses ---
// const nannyCost = __YUAN__;
// const familyFood = __YUAN__;
// const babyCost = __YUAN__;
// const utilityBills = __YUAN__;
// const miscLiving = __YUAN__;
// const monthlyInsurance = __YUAN__;
// const monthlyCarCost = __YUAN__;

// --- Business Operating Costs (if applicable) ---
// const opcMonthlyCost = __YUAN__;

// ===================== CALCULATIONS =====================
// Refer to references/financial-model-spec.md for all formulas.
// Implement each calculation module as needed based on included tabs.

// ===================== HELPERS =====================
function fmt(n) { return n.toLocaleString('zh-CN', {maximumFractionDigits:1}); }
function fmt2(n) { return n.toLocaleString('zh-CN', {maximumFractionDigits:2}); }
function fmtY(n) { return '¥' + n.toLocaleString('zh-CN', {maximumFractionDigits:0}); }
function fmtPct(n) { return n.toFixed(1) + '%'; }
function fmtWan(n) { return fmt(n) + '万'; }

function switchTab(idx) {
  document.querySelectorAll('.tab-btn').forEach((b,i) => b.classList.toggle('active', i===idx));
  document.querySelectorAll('.tab-panel').forEach((p,i) => p.classList.toggle('active', i===idx));
}

function barChart(items, maxVal) {
  return items.filter(i=>i.val>0).map(item => {
    const pct = maxVal > 0 ? (item.val / maxVal * 100) : 0;
    return `<div class="wf-row">
      <div class="wf-label">${item.label}</div>
      <div class="wf-bar-wrap">
        <div class="wf-bar" style="width:${Math.max(pct,10)}%; left:0; background:${item.color||'#0052D9'}; opacity:0.85">${item.text || fmtY(item.val)}</div>
      </div>
    </div>`;
  }).join('');
}

function progressBar(ratio, thresholds=[40,60]) {
  let cls = 'fill-safe', label = '健康';
  if (ratio > thresholds[1]) { cls = 'fill-danger'; label = '偏高'; }
  else if (ratio > thresholds[0]) { cls = 'fill-warn'; label = '需注意'; }
  return `<div class="progress-wrap">
    <div class="progress-label"><span>比率</span><span>${fmtPct(ratio)} — ${label}</span></div>
    <div class="progress-bar"><div class="fill ${cls}" style="width:${Math.min(ratio,100)}%"></div></div>
  </div>`;
}

// ===================== RENDER =====================
function render() {
  renderTab0();  // 资产总览 — 必须
  // renderTab1();  // 股票 & RSU — 按需
  // renderTab2();  // 房产 & 贷款 — 按需
  // renderTab3();  // 房贷成本 — 按需
  renderTab4();  // 月度收支 — 必须
  renderTab5();  // 场景对比 — 必须
  renderTab6();  // 行动清单 — 必须
  // renderTab7();  // 家庭支出时间线 — 按需
  // renderTab8();  // 创业分析 — 按需
  // renderTab9();  // 提前还贷 — 按需
  renderTab10(); // 盲区修正 — 建议
  renderTab11(); // 回归职场 — 建议
}

// --- TAB 0: 资产总览 ---
function renderTab0() {
  document.getElementById('tab0').innerHTML = `
    <div class="info-banner">
      <strong>基准数据</strong>：__BASE_DATA_DESCRIPTION__
    </div>

    <div class="dashboard">
      <!-- Stat cards: 总资产、总负债、净资产、资产负债率、流动资产、月度贷款 -->
      <!-- Implement based on user's actual data -->
    </div>

    <div class="card">
      <h2>资产构成明细</h2>
      <!-- barChart + detail-table -->
    </div>

    <div class="card">
      <h2>负债构成</h2>
      <!-- progressBar + detail-table -->
    </div>
  `;
}

// --- TAB 4: 月度收支 ---
function renderTab4() {
  document.getElementById('tab4').innerHTML = `
    <div class="split-grid">
      <div class="card" style="border-top:4px solid var(--success);">
        <h2 style="color:var(--success);">在职期间月度收支</h2>
        <!-- Income table + Expense table + Net savings -->
      </div>
      <div class="card" style="border-top:4px solid var(--danger);">
        <h2 style="color:var(--danger);">离职后月度收支</h2>
        <!-- Income (passive only) + Full expense table + Monthly deficit -->
      </div>
    </div>
    <div class="card">
      <h2>在职 vs 离职 — 月度现金流对比</h2>
      <!-- compare-table -->
    </div>
  `;
}

// --- TAB 5: 场景对比 ---
function renderTab5() {
  document.getElementById('tab5').innerHTML = `
    <!-- big-comparison: Scenario A vs Scenario B -->
    <!-- Detailed comparison table -->
    <!-- Asset layer analysis -->
    <!-- Income scenario analysis -->
  `;
}

// --- TAB 6: 行动清单 ---
function renderTab6() {
  document.getElementById('tab6').innerHTML = `
    <div class="card">
      <h2>待办行动清单</h2>
      <!-- Chronological action items with priority -->
    </div>
  `;
}

// --- TAB 10: 盲区修正 ---
function renderTab10() {
  // Refer to financial-model-spec.md Section 2.6 for calcRealSurvival
  document.getElementById('tab10').innerHTML = `
    <div class="card" style="border:2px solid var(--fin-purple); background:#F8F6FF;">
      <h2 style="border:none; font-size:20px; text-align:center; justify-content:center; color:var(--fin-purple);">
        盲区修正 · 通胀 / 备用金 / 投资回报
      </h2>
    </div>
    <!-- Before/after comparison table -->
    <!-- Inflation erosion table -->
    <!-- Dynamic survival simulation -->
  `;
}

// --- TAB 11: 回归职场 ---
function renderTab11() {
  // Refer to financial-model-spec.md Section 2.7 for reverseGrossSalary
  document.getElementById('tab11').innerHTML = `
    <div class="card" style="border:2px solid var(--fin-magenta); background:#FFF0F6;">
      <h2 style="border:none; font-size:20px; text-align:center; justify-content:center; color:var(--fin-magenta);">
        创业失败 · 回归职场工资分析
      </h2>
    </div>
    <!-- Min/Comfort/Good salary table per year -->
    <!-- Gap penalty table -->
    <!-- Stop-loss threshold analysis -->
  `;
}

// Implement remaining renderTabN() functions as needed.
// Refer to assets/dashboard-template.html comments and
// references/financial-model-spec.md for complete specifications.

render();
</script>
</body>
</html>


path: references/city-profiles.md
content: # 城市参数配置文件

本文档提供各城市的社保缴费比例、公积金上限、灵活就业社保费用及生活成本参考基线。
构建仪表盘时根据用户所在城市自动选取对应参数。

> **数据时效**：基于 2025 年度各地人社/税务部门公开信息。每年 7 月调基，
> 如当前日期在 7 月后，建议搜索最新数据更新。

---

## 1. 城市社保参数

### 1.1 在职员工社保（个人部分）

社保个人缴纳比例 = 养老(8%) + 医疗(2%左右) + 失业(0.2~0.5%)，各城市略有差异。

```javascript
const CITY_SSI_PROFILES = {
  // ============ 一线城市 ============
  "北京": {
    ssiBaseLower: 6843,    // 社保基数下限
    ssiBaseUpper: 36927,   // 社保基数上限
    ssiPersonalRate: 0.105, // 个人社保总比例 (8%+2%+0.5%)
    ssiCompanyRate: 0.265,  // 单位社保总比例 (16%+9.8%+0.5%+0.4%)
    hpfRate: 0.12,          // 公积金个人比例
    hpfBaseUpper: 35283,    // 公积金基数上限 (2025年)
    hpfBaseLower: 2420,     // 公积金基数下限
    medicalExtra: 3,        // 大额医疗互助（元/月）
    label: "北京",
  },
  "上海": {
    ssiBaseLower: 7850,
    ssiBaseUpper: 39250,
    ssiPersonalRate: 0.105,  // 8%+2%+0.5%
    ssiCompanyRate: 0.272,   // 16%+10.5%+0.5%+0.16%
    hpfRate: 0.07,           // 上海公积金 7%（大部分企业）
    hpfBaseUpper: 39250,
    hpfBaseLower: 2690,
    medicalExtra: 0,
    label: "上海",
  },
  "广州": {
    ssiBaseLower: 5642,
    ssiBaseUpper: 28210,
    ssiPersonalRate: 0.102,  // 8%+2%+0.2% (失业比例低)
    ssiCompanyRate: 0.208,   // 14%+5.5%+0.32%+0.85%+0.2%
    hpfRate: 0.12,
    hpfBaseUpper: 38082,
    hpfBaseLower: 2300,
    medicalExtra: 0,
    label: "广州",
  },
  "深圳": {
    ssiBaseLower: 2493,
    ssiBaseUpper: 25920,
    ssiPersonalRate: 0.103,  // 8%+2%+0.3%
    ssiCompanyRate: 0.224,   // 15%+6.2%+0.7%+0.5%+0.14%
    hpfRate: 0.05,           // 深圳公积金 5%（部分企业更高）
    hpfBaseUpper: 41190,
    hpfBaseLower: 2360,
    medicalExtra: 0,
    label: "深圳",
    note: "深圳社保基数较低,医疗分一二三档,此处按一档(最高)计算",
  },

  // ============ 新一线城市 ============
  "杭州": {
    ssiBaseLower: 4700,
    ssiBaseUpper: 25200,
    ssiPersonalRate: 0.105,  // 8%+2%+0.5%
    ssiCompanyRate: 0.242,   // 14%+9.5%+0.5%+0.2%
    hpfRate: 0.12,
    hpfBaseUpper: 35550,
    hpfBaseLower: 2280,
    medicalExtra: 4,         // 大病保险约50元/年
    label: "杭州",
  },
  "成都": {
    ssiBaseLower: 4560,
    ssiBaseUpper: 22800,
    ssiPersonalRate: 0.104,  // 8%+2%+0.4%
    ssiCompanyRate: 0.245,   // 16%+7.7%+0.6%+0.2%
    hpfRate: 0.12,
    hpfBaseUpper: 29380,
    hpfBaseLower: 2100,
    medicalExtra: 0,
    label: "成都",
  },
  "南京": {
    ssiBaseLower: 4879,
    ssiBaseUpper: 27216,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.256,
    hpfRate: 0.12,
    hpfBaseUpper: 33600,
    hpfBaseLower: 2280,
    medicalExtra: 10,
    label: "南京",
  },
  "武汉": {
    ssiBaseLower: 4494,
    ssiBaseUpper: 22470,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.25,
    hpfRate: 0.12,
    hpfBaseUpper: 29799,
    hpfBaseLower: 2010,
    medicalExtra: 7,
    label: "武汉",
  },
  "西安": {
    ssiBaseLower: 4281,
    ssiBaseUpper: 21405,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.256,
    hpfRate: 0.12,
    hpfBaseUpper: 28434,
    hpfBaseLower: 2160,
    medicalExtra: 0,
    label: "西安",
  },
  "苏州": {
    ssiBaseLower: 4879,
    ssiBaseUpper: 27216,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.255,
    hpfRate: 0.12,
    hpfBaseUpper: 34900,
    hpfBaseLower: 2280,
    medicalExtra: 5,
    label: "苏州",
  },
  "长沙": {
    ssiBaseLower: 4396,
    ssiBaseUpper: 21978,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.25,
    hpfRate: 0.12,
    hpfBaseUpper: 28236,
    hpfBaseLower: 1930,
    medicalExtra: 0,
    label: "长沙",
  },
  "重庆": {
    ssiBaseLower: 4359,
    ssiBaseUpper: 21793,
    ssiPersonalRate: 0.105,
    ssiCompanyRate: 0.24,
    hpfRate: 0.12,
    hpfBaseUpper: 29320,
    hpfBaseLower: 2100,
    medicalExtra: 5,
    label: "重庆",
  },
};
```

### 1.2 灵活就业社保（辞职后本人/配偶参保）

灵活就业人员只缴养老+医疗，无失业/工伤/生育。
缴费基数可选（通常在 60%~300% 社平工资之间），以下按最低基数估算月缴金额。

```javascript
const CITY_FLEX_SOCIAL = {
  // 格式: { pension: 养老月缴, medical: 医疗月缴, total: 合计 }
  // 均按最低基数（60%档）估算
  "北京": { pension: 1096, medical: 580, total: 1676, note: "养老20%×6843=1369,实际可选更低档" },
  "上海": { pension: 1256, medical: 792, total: 2048, note: "灵活就业养老20%×7850,医疗11.5%+3元" },
  "广州": { pension: 904,  medical: 418, total: 1322, note: "养老20%×5642,医疗含大病" },
  "深圳": { pension: 499,  medical: 452, total:  951, note: "深户养老最低基数低,非深户参照户籍地" },
  "杭州": { pension: 752,  medical: 434, total: 1186, note: "养老20%×4700,医疗含大病" },
  "成都": { pension: 730,  medical: 396, total: 1126, note: "养老20%×4560,医疗8.5%" },
  "南京": { pension: 781,  medical: 397, total: 1178, note: "养老20%×4879" },
  "武汉": { pension: 719,  medical: 360, total: 1079, note: "养老20%×4494" },
  "西安": { pension: 685,  medical: 343, total: 1028, note: "养老20%×4281" },
  "苏州": { pension: 781,  medical: 390, total: 1171, note: "养老20%×4879" },
  "长沙": { pension: 703,  medical: 352, total: 1055, note: "养老20%×4396" },
  "重庆": { pension: 697,  medical: 340, total: 1037, note: "养老20%×4359" },
};
```

---

## 2. 生活成本参考基线

以下数据为各城市三口之家（2 大人 + 1 幼儿）每月典型支出参考。
**这是参考起点**，实际建模时应根据用户的具体生活方式调整。

```javascript
const CITY_LIVING_COST = {
  // 格式: { rent, food, baby, utility, misc, nanny, car, insurance, total }
  // 单位: 元/月   
  // rent = 自住不填(0)，租房按中等地段两居室
  // car = 车险月均 + 油费/充电（无车则 0）
  // nanny = 住家保姆/育儿嫂 (可选)

  "北京": {
    rent: 0,         // 自住房默认 0（有房贷在贷款里算）
    food: 4000,      // 三口之家 + 偶尔外食
    baby: 3000,      // 奶粉/纸尿裤/儿童用品/医疗
    utility: 2000,   // 水电燃气物业网络话费
    misc: 2000,      // 服装/社交/日杂
    nanny: 6500,     // 育儿嫂（住家）
    car: 2300,       // 保险800 + 油费1500
    insurance: 1700, // 年缴2万/12
    total: 21500,
    inflationRate: 0.035, // 北京通胀较高
    label: "北京（一线）",
  },
  "上海": {
    rent: 0,
    food: 4200,
    baby: 3200,
    utility: 2100,
    misc: 2200,
    nanny: 7000,
    car: 2100,
    insurance: 1700,
    total: 22500,
    inflationRate: 0.035,
    label: "上海（一线）",
  },
  "广州": {
    rent: 0,
    food: 3500,
    baby: 2500,
    utility: 1800,
    misc: 1800,
    nanny: 5500,
    car: 2000,
    insurance: 1500,
    total: 18600,
    inflationRate: 0.03,
    label: "广州（一线）",
  },
  "深圳": {
    rent: 0,
    food: 3800,
    baby: 2800,
    utility: 1900,
    misc: 2000,
    nanny: 6000,
    car: 2000,
    insurance: 1600,
    total: 20100,
    inflationRate: 0.035,
    label: "深圳（一线）",
  },
  "杭州": {
    rent: 0,
    food: 3500,
    baby: 2500,
    utility: 1700,
    misc: 1800,
    nanny: 5500,
    car: 1800,
    insurance: 1500,
    total: 18300,
    inflationRate: 0.03,
    label: "杭州（新一线）",
  },
  "成都": {
    rent: 0,
    food: 3000,
    baby: 2200,
    utility: 1500,
    misc: 1500,
    nanny: 4500,
    car: 1600,
    insurance: 1200,
    total: 15500,
    inflationRate: 0.025,
    label: "成都（新一线）",
  },
  "南京": {
    rent: 0,
    food: 3500,
    baby: 2500,
    utility: 1700,
    misc: 1800,
    nanny: 5500,
    car: 1800,
    insurance: 1500,
    total: 18300,
    inflationRate: 0.03,
    label: "南京（新一线）",
  },
  "武汉": {
    rent: 0,
    food: 3000,
    baby: 2200,
    utility: 1500,
    misc: 1500,
    nanny: 4500,
    car: 1600,
    insurance: 1200,
    total: 15500,
    inflationRate: 0.025,
    label: "武汉（新一线）",
  },
  "西安": {
    rent: 0,
    food: 2800,
    baby: 2000,
    utility: 1400,
    misc: 1500,
    nanny: 4000,
    car: 1500,
    insurance: 1000,
    total: 14200,
    inflationRate: 0.025,
    label: "西安（新一线）",
  },
  "长沙": {
    rent: 0,
    food: 2800,
    baby: 2000,
    utility: 1300,
    misc: 1400,
    nanny: 4200,
    car: 1500,
    insurance: 1000,
    total: 14200,
    inflationRate: 0.025,
    label: "长沙（新一线）",
  },
  "重庆": {
    rent: 0,
    food: 2800,
    baby: 2000,
    utility: 1300,
    misc: 1400,
    nanny: 4000,
    car: 1500,
    insurance: 1000,
    total: 14000,
    inflationRate: 0.025,
    label: "重庆（新一线）",
  },
};
```

---

## 3. 个税计算的城市差异

个税七级累进税率全国统一，但以下参数各城市不同：

| 参数 | 说明 | 影响 |
|------|------|------|
| 社保个人比例 | 各城市 10.2%~10.5% | 影响应税收入 |
| 公积金比例 | 5%~12% | 影响应税收入 |
| 公积金基数上限 | 2.2万~3.9万 | 高薪人群影响大 |
| 专项附加扣除 | 全国统一 | 子女教育1000/房贷1000/赡养2000等 |

### reverseGrossSalary 城市适配

```javascript
function reverseGrossSalary(netTarget, city) {
  const profile = CITY_SSI_PROFILES[city] || CITY_SSI_PROFILES["北京"];
  let gross = netTarget * 1.4;
  for (let iter = 0; iter < 50; iter++) {
    // 社保 = 基数(取上下限) × 个人比例
    const ssiBase = Math.max(profile.ssiBaseLower, Math.min(gross, profile.ssiBaseUpper));
    const ssi = ssiBase * profile.ssiPersonalRate;
    // 公积金 = 基数(取上限) × 比例
    const hpfBase = Math.max(profile.hpfBaseLower, Math.min(gross, profile.hpfBaseUpper));
    const hpf = hpfBase * profile.hpfRate;
    // 个税（年度累进制）
    const taxable = gross - ssi - hpf - 5000 - (profile.medicalExtra || 0);
    let tax = 0;
    if (taxable > 0) {
      const annual = taxable * 12;
      if (annual <= 36000)        tax = annual * 0.03;
      else if (annual <= 144000)  tax = annual * 0.10 - 2520;
      else if (annual <= 300000)  tax = annual * 0.20 - 16920;
      else if (annual <= 420000)  tax = annual * 0.25 - 31920;
      else if (annual <= 660000)  tax = annual * 0.30 - 52920;
      else if (annual <= 960000)  tax = annual * 0.35 - 85920;
      else                        tax = annual * 0.45 - 181920;
      tax = tax / 12;
    }
    const net = gross - ssi - hpf - Math.max(0, tax) - (profile.medicalExtra || 0);
    const diff = net - netTarget;
    if (Math.abs(diff) < 10) break;
    gross -= diff * 0.7;
  }
  return Math.round(gross);
}
```

---

## 4. 使用指南

### 4.1 在 Phase 1 信息采集时

问用户：**"你在哪个城市生活/工作？"** 

根据回答匹配城市参数。如果用户所在城市不在预设列表中，使用最接近的城市参数或让 AI 搜索该城市最新社保数据。

### 4.2 在 Phase 2 建模时

```javascript
// 示例：根据城市初始化参数
const CITY = "杭州";
const citySSI = CITY_SSI_PROFILES[CITY];
const cityFlex = CITY_FLEX_SOCIAL[CITY];
const cityLiving = CITY_LIVING_COST[CITY];

// 灵活就业社保作为辞职后的支出项
const SPOUSE_SOCIAL = cityFlex.total;  // 或只算 pension 不交医保

// 通胀率使用城市特定值
const INFLATION_RATE = cityLiving.inflationRate;

// 反推税前工资时使用城市参数
const targetGross = reverseGrossSalary(targetNet, CITY);
```

### 4.3 城市对比功能（可选）

如果用户考虑换城市（如"从北京搬到成都"），可以生成城市对比分析 Tab：

- 生活成本对比（柱状图）
- 社保缴费对比
- 同样的资产在两个城市分别能撑多久
- 回去上班需要的薪资差异

---

## 5. 数据更新提示

每年 7 月各城市调整社保基数。当你使用此 skill 时：
1. 检查当前日期
2. 如果在每年 7 月后，建议搜索 "XX城市 {年份}年 社保缴费基数" 获取最新数据
3. 公积金基数通常每年 7 月 1 日更新
4. 灵活就业社保费用随基数同步调整


path: references/financial-model-spec.md
content: # 家庭财务全景分析 — 计算模型规范

本文档定义了仪表盘中所有财务计算的公式、逻辑和边界条件。
实现时以 JavaScript 内联在 HTML `<script>` 中，所有数值由常量派生，修改一处全局联动。

---

## 1. 数据结构 — 用户输入常量

### 1.1 资产类

```javascript
// 汇率（如有港币/美元资产）
const FX_RATE = __FX_RATE__;  // 例: 0.881 (1 HKD = 0.881 CNY)

// 股票/证券
const stockAccounts = [
  { name: '__NAME__', valueInOrigCurrency: __VALUE__, currency: 'HKD' },
  // ... 可多个账户
];
const totalStockCNY = stockAccounts.reduce((s, a) => s + a.valueInOrigCurrency * (a.currency === 'CNY' ? 1 : FX_RATE), 0);

// 未归属股权激励（RSU/Option）— 如用户有
const futureEquity = [
  { date: '__DATE__', shares: __N__, pricePerShare: __PRICE__, currency: 'HKD', label: '__LABEL__' },
];

// 房产
const properties = [
  { name: '自住房', value: __WAN__, mortgageRemain: __WAN__, monthlyPayment: __YUAN__, rate: __PCT__, isSelfUse: true, monthlyRent: 0 },
  { name: '投资房', value: __WAN__, mortgageRemain: 0, monthlyPayment: 0, rate: 0, isSelfUse: false, monthlyRent: __YUAN__ },
];

// 车辆
const carValue = __WAN__;

// 现金
const cashSaving = __WAN__;

// 负债
const loans = [
  { name: '商业贷款', remain: __WAN__, monthlyPayment: __YUAN__, rate: __PCT__, totalMonths: 360, paidMonths: __N__ },
  { name: '公积金贷款', remain: __WAN__, monthlyPayment: __YUAN__, rate: __PCT__, totalMonths: 360, paidMonths: __N__, minPayment: __YUAN__ },
  { name: '无息贷款（触发条件还款）', remain: __WAN__, monthlyPayment: 0, rate: 0, mustRepayOnQuit: true, note: '亲友借款/购房垫资/消费分期等，满足条件时需一次性归还' },
];
```

### 1.2 收支类

```javascript
// 工资（在职时）
const salary = {
  gross: __YUAN__,      // 税前
  ssi: __YUAN__,        // 社保个人
  hpf: __YUAN__,        // 公积金个人
  tax: __YUAN__,        // 个税
  net: __YUAN__,        // 到手
  companyHPF: __YUAN__, // 公司缴公积金
};

// 被动收入
const monthlyRentIncome = properties.filter(p => !p.isSelfUse).reduce((s, p) => s + p.monthlyRent, 0);

// 家庭生活支出项
const livingExpenses = {
  nanny: __YUAN__,       // 保姆/育儿嫂（可为 0）
  food: __YUAN__,        // 伙食
  baby: __YUAN__,        // 宝宝用品（可为 0）
  education: __YUAN__,   // 教育
  utility: __YUAN__,     // 水电物业网络话费
  misc: __YUAN__,        // 其他杂项
  insurance: __YUAN__,   // 家庭保险月均
  carCost: __YUAN__,     // 车辆保险+油费
  spouseSocial: __YUAN__,// 配偶灵活社保（可为 0）
};

// 创业/项目运营成本（可选模块）
const bizCost = {
  socialInsurance: __YUAN__, // 公司缴社保
  bookkeeping: __YUAN__,    // 记账报税
  cloudAndTools: __YUAN__,  // 云服务/SaaS/API
  misc: __YUAN__,           // 域名等杂费
};

// 系统参数
const INFLATION_RATE = __RATE__;     // 默认 0.035
const EMERGENCY_MONTHS = __N__;      // 默认 8（有小孩）或 6
const INVEST_RETURN = __RATE__;      // 默认 0.025
```

---

## 2. 核心计算模块

### 2.1 资产负债净值

```
totalAsset = Σ properties[i].value + totalStockWan + cashSaving + carValue
totalDebt  = Σ loans[i].remain
netWorth   = totalAsset - totalDebt
debtRatio  = totalDebt / totalAsset × 100%
liquidAsset = totalStockWan + cashSaving  // 可快速变现的
```

**判定标准**：
- 资产负债率 ≤ 40%：健康
- 40% < 资产负债率 ≤ 60%：需关注
- > 60%：偏高

### 2.2 月度收支模型

**在职时**：
```
onJobIncome    = salary.net + monthlyRentIncome
onJobExpense   = 商贷月供 + 家庭生活总支出 + 保险 + 车费
                 （公积金贷款由公积金账户扣，不计入自付）
onJobSaving    = onJobIncome - onJobExpense
```

**离职后**：
```
quitExpense = Σ loans[i].monthlyPayment  // 所有贷款（含公积金自付）
            + Σ livingExpenses
            + Σ bizCost                   // 如有创业
quitIncome  = monthlyRentIncome           // 仅被动收入
quitDeficit = quitExpense - quitIncome
```

### 2.3 通胀调整的 N 年支出时间线

```javascript
// 基准年数据
const yearPlanBase = [
  { year: 1, nanny: X, food: X, baby: X, education: X, ... },
  { year: 2, ... },
  // ... 最多 5 年
];

// 通胀修正
const yearPlan = yearPlanBase.map((base, idx) => {
  const inflator = Math.pow(1 + INFLATION_RATE, idx); // 第1年=1, 第2年=1.035...
  // 所有支出项乘以 inflator
  const adjusted = {};
  for (const key of expenseKeys) {
    adjusted[key] = Math.round(base[key] * inflator);
  }
  adjusted.monthlyFamily = Σ adjusted[expenseKeys];
  adjusted.monthlyDeficit = adjusted.monthlyFamily + adjusted.carCost + totalMonthlyLoans + bizMonthlyCost - monthlyRentIncome;
  return adjusted;
});
```

**关键点**：
- 通胀按复利逐年累计，不是每年加固定额
- 保姆→幼儿园的结构性变化先在基准数据中体现，通胀再叠加
- 教育支出随年龄非线性增长（幼儿园、兴趣班等）

### 2.4 两种场景对比

设用户有"立即行动"（Scenario A）和"延期行动"（Scenario B）两个时间点：

**Scenario A — 立即行动**：
```
liquidAfterLoan_A = liquidAsset - mustRepayLoans  // mustRepayLoans = 所有 mustRepayOnQuit=true 的贷款余额之和（含亲友借款、垫资等）
emergency_A       = quitExpense × EMERGENCY_MONTHS / 10000
deployable_A      = liquidAfterLoan_A - emergency_A
survivalMonths_A  = floor(deployable_A × 10000 / quitDeficit)
```

**Scenario B — 延期行动**（多等 M 个月）：
```
extraSavings      = onJobSaving × M / 10000
extraEquity       = futureEquityAfterTax / 10000  // 如有 RSU
liquidAsset_B     = liquidAsset + extraSavings + extraEquity
liquidAfterLoan_B = liquidAsset_B - mustRepayLoans
deployable_B      = liquidAfterLoan_B - emergency_A  // 备用金标准相同
survivalMonths_B  = floor(deployable_B × 10000 / quitDeficit)
```

### 2.5 等额本息贷款摊销

```javascript
function calcAmortization(principal, annualRate, totalMonths, paidMonths) {
  const r = annualRate / 100 / 12;
  const monthlyPayment = principal * r * Math.pow(1+r, totalMonths) / (Math.pow(1+r, totalMonths) - 1);
  
  let balance = principal;
  let totalInterestPaid = 0, totalPrincipalPaid = 0;
  
  for (let m = 1; m <= totalMonths; m++) {
    const interest = balance * r;
    const principalPart = monthlyPayment - interest;
    balance -= principalPart;
    if (m <= paidMonths) {
      totalInterestPaid += interest;
      totalPrincipalPaid += principalPart;
    }
  }
  
  return {
    monthlyPayment,
    totalPayment: monthlyPayment * totalMonths,
    totalInterest: monthlyPayment * totalMonths - principal,
    remainingPrincipal: principal - totalPrincipalPaid,
  };
}
```

### 2.6 动态生存模拟（含投资回报 + 通胀）

比静态计算更精确——每个月计算投资收益，支出按通胀逐年递增：

```javascript
function calcRealSurvival(monthlyExtraIncome = 0) {
  let asset = deployable * 10000;
  for (let m = 1; m <= 120; m++) {
    // 每月投资回报
    asset += asset * (INVEST_RETURN / 12);
    // 当年支出（按通胀后数据）
    const yearIdx = Math.min(Math.floor((m-1) / 12), yearPlan.length - 1);
    const deficit = yearPlan[yearIdx].monthlyDeficit - monthlyExtraIncome;
    asset -= deficit;
    if (asset <= 0) return m;
  }
  return Infinity; // 超过10年，财务可持续
}
```

### 2.7 反推税前工资（中国个税七级累进）

给定目标到手月薪，反推需要的税前工资。**需要根据城市参数调整社保/公积金比例**。

详见 `city-profiles.md` 中的 `reverseGrossSalary(netTarget, city)` 函数。

核心逻辑：
```javascript
function reverseGrossSalary(netTarget, city) {
  const profile = CITY_SSI_PROFILES[city]; // 从 city-profiles.md 加载
  let gross = netTarget * 1.4; // 初始猜测
  for (let iter = 0; iter < 50; iter++) {
    // 社保 = 基数(取上下限) × 个人比例（各城市不同）
    const ssiBase = Math.max(profile.ssiBaseLower, Math.min(gross, profile.ssiBaseUpper));
    const ssi = ssiBase * profile.ssiPersonalRate;
    // 公积金 = 基数(取上限) × 比例（各城市不同，5%~12%）
    const hpfBase = Math.max(profile.hpfBaseLower, Math.min(gross, profile.hpfBaseUpper));
    const hpf = hpfBase * profile.hpfRate;
    const taxable = gross - ssi - hpf - 5000;
    let tax = 0;
    if (taxable > 0) {
      const annual = taxable * 12;
      if (annual <= 36000) tax = annual * 0.03;
      else if (annual <= 144000) tax = annual * 0.10 - 2520;
      else if (annual <= 300000) tax = annual * 0.20 - 16920;
      else if (annual <= 420000) tax = annual * 0.25 - 31920;
      else if (annual <= 660000) tax = annual * 0.30 - 52920;
      else if (annual <= 960000) tax = annual * 0.35 - 85920;
      else tax = annual * 0.45 - 181920;
      tax = tax / 12;
    }
    const net = gross - ssi - hpf - Math.max(0, tax);
    const diff = net - netTarget;
    if (Math.abs(diff) < 10) break;
    gross -= diff * 0.7; // 阻尼收敛
  }
  return Math.round(gross);
}
```

**注意**：
- 社保基数有上限和下限，各城市不同 → 详见 `city-profiles.md`
- 公积金缴存基数上限各城市不同 → 详见 `city-profiles.md`
- 简化处理不含专项附加扣除（子女教育、住房贷款等，用户可自行调整）

### 2.8 止损线计算

```
jobSearchMonths = 4  // 平均找工作时间
stopLossAsset = quitDeficit × (EMERGENCY_MONTHS + jobSearchMonths) / 10000
```

**含义**：当资产降到此值时，必须立刻开始找工作，预留求职期间的生活费 + 紧急备用金。

### 2.9 职场空窗折损模型

| 空窗时间 | 薪资折损 | 求职难度 | 说明 |
|----------|----------|----------|------|
| ≤ 1 年 | 0-5% | 低 | 可解释为"创业尝试/深造"，技术栈未过时 |
| 1-2 年 | 5-15% | 中 | 需证明期间保持学习，可能需刷题 |
| 2-3 年 | 10-25% | 中高 | 技术迭代快的行业部分技能已过时 |
| 3-5 年 | 20-40% | 高 | 年龄+空窗双重压力，可能需降级或转方向 |

折损比例因行业而异——互联网/AI 更高，传统行业/公务员更低。

---

## 3. Tab 模块清单

根据用户具体情况，从以下 Tab 中选取适用的：

| Tab | 名称 | 内容 | 何时包含 |
|-----|------|------|----------|
| 0 | 资产总览 | 净值、资产负债率、资产/负债构成表 | **必须** |
| 1 | 股票 & RSU | 持仓明细、未归属 RSU、税务分析 | 有股票/RSU 时 |
| 2 | 房产 & 贷款 | 房产净值、贷款明细、离职后公积金影响 | 有房产贷款时 |
| 3 | 房贷成本 | 30 年总利息、逐年本息比、提前还贷分析 | 有房贷时 |
| 4 | 月度收支 | 在职 vs 离职收支对比、公积金优化 | **必须** |
| 5 | 辞职对比 | 两种场景全景对比、资产分层、OPC 收入情景 | 有两个场景时 |
| 6 | 行动清单 | 时间排序的待办事项 | **必须** |
| 7 | 家庭支出 | 5 年支出时间线、堆叠柱状图、饼图 | 有小孩/支出变化时 |
| 8 | 创业分析 | 运营成本拆解、盈亏平衡、资产消耗曲线 | 有创业计划时 |
| 9 | 提前还贷 | 提前还贷 vs 投资对比、净现值分析 | 有房贷时 |
| 10 | 盲区修正 | 通胀侵蚀、修正前后对比、动态生存模拟 | **建议** |
| 11 | 回归职场 | 最低/舒适/宽裕薪资、反推税前、止损线 | 有创业/离职场景时 |

---

## 4. 格式化辅助函数

```javascript
function fmt(n)   { return n.toLocaleString('zh-CN', {maximumFractionDigits:1}); }
function fmtY(n)  { return '¥' + n.toLocaleString('zh-CN', {maximumFractionDigits:0}); }
function fmtHK(n) { return 'HK$' + n.toLocaleString('zh-CN', {maximumFractionDigits:0}); }
function fmtPct(n){ return n.toFixed(1) + '%'; }
function fmtWan(n){ return fmt(n) + '万'; }
```

---

## 5. 给家人看的通俗总结模板

在所有计算完成后，生成一段 500 字以内的通俗总结。语气温暖、体贴、有底气，避免冷冰冰的数字罗列。
核心信息点：
1. 为什么做这个决定（家庭优先，不是逃避工作）
2. 钱够花多久（月数→年数）
3. 每月花多少、主要花在哪
4. 如果不成功，回去上班需要多少工资
5. 安全底线在哪（止损资产额）
6. 最关键的一个建议（如"一定要等到 X 月再辞"）


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)