/**
 * 键共振 · 心性 vs 功用（铁律投喂包 v4.0）
 * 隔位不作用 · 传递链 · 根气共振 · 库中不透
 * 冲合即变 · 备份激活 · 合化临界 · 制杀论权
 * 四步闭环：定体 → 定境 → 做功变用 → 岁运增减
 * © 2026 Jason Li（泽森）
 */
(function (global) {
  "use strict";

  const TG = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
  const STEM_WX = [0, 0, 1, 1, 2, 2, 3, 3, 4, 4];
  const YANG = { 甲: 1, 丙: 1, 戊: 1, 庚: 1, 壬: 1 };
  const PKEY = ["year", "month", "day", "time"];
  const PZH = { year: "年", month: "月", day: "日", time: "时" };
  const LIUHE = { 子: "丑", 丑: "子", 寅: "亥", 亥: "寅", 卯: "戌", 戌: "卯", 辰: "酉", 酉: "辰", 巳: "申", 申: "巳", 午: "未", 未: "午" };
  const LIUHE_HUA = { 子丑: "土", 丑子: "土", 寅亥: "木", 亥寅: "木", 卯戌: "火", 戌卯: "火", 辰酉: "金", 酉辰: "金", 巳申: "水", 申巳: "水", 午未: "土", 未午: "土" };
  const LIUCHONG = { 子: "午", 午: "子", 丑: "未", 未: "丑", 寅: "申", 申: "寅", 卯: "酉", 酉: "卯", 辰: "戌", 戌: "辰", 巳: "亥", 亥: "巳" };
  const TIANGAN_HE = { 甲: "己", 己: "甲", 乙: "庚", 庚: "乙", 丙: "辛", 辛: "丙", 丁: "壬", 壬: "丁", 戊: "癸", 癸: "戊" };
  const TIANGAN_HE_HUA = { 甲己: "土", 己甲: "土", 乙庚: "金", 庚乙: "金", 丙辛: "水", 辛丙: "水", 丁壬: "木", 壬丁: "木", 戊癸: "火", 癸戊: "火" };
  const HIDE_LAYERS = {
    子: [["癸", "本"]], 丑: [["己", "本"], ["癸", "中"], ["辛", "余"]],
    寅: [["甲", "本"], ["丙", "中"], ["戊", "余"]], 卯: [["乙", "本"]],
    辰: [["戊", "本"], ["乙", "中"], ["癸", "余"]], 巳: [["丙", "本"], ["戊", "中"], ["庚", "余"]],
    午: [["丁", "本"], ["己", "中"]], 未: [["己", "本"], ["丁", "中"], ["乙", "余"]],
    申: [["庚", "本"], ["壬", "中"], ["戊", "余"]], 酉: [["辛", "本"]],
    戌: [["戊", "本"], ["辛", "中"], ["丁", "余"]], 亥: [["壬", "本"], ["甲", "中"]],
  };
  const HIDE_W = {
    子: [["癸", 1]], 丑: [["己", 0.6], ["癸", 0.3], ["辛", 0.1]],
    寅: [["甲", 0.6], ["丙", 0.3], ["戊", 0.1]], 卯: [["乙", 1]],
    辰: [["戊", 0.6], ["乙", 0.3], ["癸", 0.1]], 巳: [["丙", 0.6], ["戊", 0.3], ["庚", 0.1]],
    午: [["丁", 0.7], ["己", 0.3]], 未: [["己", 0.6], ["丁", 0.3], ["乙", 0.1]],
    申: [["庚", 0.6], ["壬", 0.3], ["戊", 0.1]], 酉: [["辛", 1]],
    戌: [["戊", 0.6], ["辛", 0.3], ["丁", 0.1]], 亥: [["壬", 0.7], ["甲", 0.3]],
  };
  const VAULT_BR = { 辰: 1, 戌: 1, 丑: 1, 未: 1 };
  const STEM_BASE = { year: 0.75, month: 0.875, day: 1.0, time: 0.75 };
  const BRANCH_BASE = { year: 0.75, month: 1.0, day: 0.875, time: 0.75 };
  const KIN_MALE = [
    { id: "fu", name: "父", ss: "偏财", palace: "month", palaceZh: "月柱父母宫" },
    { id: "mu", name: "母", ss: "正印", palace: "month", palaceZh: "月柱父母宫" },
    { id: "xiong", name: "兄", ss: "比肩", palace: "month", palaceZh: "月柱兄弟宫", sibling: true },
    { id: "jie", name: "姐", ss: "劫财", palace: "month", palaceZh: "月柱兄弟宫", sibling: true },
    { id: "di", name: "弟", ss: "食神", palace: "month", palaceZh: "月柱兄弟宫", sibling: true, junior: true },
    { id: "mei", name: "妹", ss: "伤官", palace: "month", palaceZh: "月柱兄弟宫", sibling: true, junior: true },
    { id: "qi", name: "妻", ss: "正财", palace: "day", palacePart: "zhi", palaceZh: "日支配偶宫" },
    { id: "zi", name: "子", ss: "七杀", palace: "time", palaceZh: "时柱子女宫" },
    { id: "nv", name: "女", ss: "正官", palace: "time", palaceZh: "时柱子女宫" },
  ];
  const KIN_FEMALE = [
    { id: "fu", name: "父", ss: "正财", palace: "month", palaceZh: "月柱父母宫" },
    { id: "mu", name: "母", ss: "偏印", palace: "month", palaceZh: "月柱父母宫" },
    { id: "jie", name: "姐", ss: "比肩", palace: "month", palaceZh: "月柱兄弟宫", sibling: true },
    { id: "xiong", name: "兄", ss: "劫财", palace: "month", palaceZh: "月柱兄弟宫", sibling: true },
    { id: "fu2", name: "夫", ss: "正官", palace: "day", palacePart: "zhi", palaceZh: "日支配偶宫" },
    { id: "zi", name: "子", ss: "伤官", palace: "time", palaceZh: "时柱子女宫" },
    { id: "nv", name: "女", ss: "食神", palace: "time", palaceZh: "时柱子女宫" },
  ];
  const XIN_XING = {
    七杀: "刚猛、果决、权威欲、危机感。代表外界压力、大领导或法律风险。",
    正官: "名分、体制、约束、稳升。代表考核、职务与规矩。",
    偏印: "冷眼旁观、偏门学识、宗教哲学、长辈暗助。",
    正印: "庇护、正统学业、文书、母亲与师长。",
    伤官: "才华外露、口舌犀利、桀骜不驯、技术控。",
    食神: "才华温和、口福、技艺认证、子女气。",
    偏财: "投机欲、交际应酬、大钱野心。",
    正财: "稳定财、经营、妻财、账面收入。",
    比肩: "自立、同辈、分财、并行。",
    劫财: "争夺、破财、兄弟竞合。",
    日主: "自身气质与承压面。",
  };

  /** 四步断命闭环（已锁定）。底盘看正从，钥匙看做功，路况看大运。 */
  const FOUR_STEP = [
    { id: "ti", name: "定体", zh: "看令地后、字作用前的五行总量，唯一且永久判定正从。令地旺衰不算合冲生克。地理叠能与合冲刑化不进此账。此身份作为底盘，终身不可逆、不可改。" },
    { id: "jing", name: "定境", zh: "原局字与字未发生生克制化前，仅反映先天现实环境（原生家庭、初始资源）。此阶段确定第一次名义用忌。" },
    { id: "gong", name: "做功变用", zh: "原局合、冲、刑、化后，只改第二次实际用忌。不改正从，不改我客，不改家内外。正格平衡的用忌看家内外做功是否对自己和家内有利。" },
    { id: "yun", name: "岁运增减", zh: "大运流年不改变做功后的实际用神，只改变该用神的力量阈值。从格喜旺，正格喜平衡。用神被合绊变质，补亦无用。" },
  ];
  const FOUR_STEP_LINE = "定体：令地后五行＋出生地理总量判定正从，身份终身不改。令地旺衰不算合冲生克。令值旺1.5／相1.25／休0.75／囚⅞／死⅝。戌影响 1.0，午影响 1.0×¼，先戌后午。地理水火金木不进字，只进正从／我客／家内外总量。正格两边|我−客|／max≤⅛为平衡格局，用忌看家内外做功是否对自己和家内有利。定境：未作用前为第一次名义用忌；平衡格名义用忌＝用忌看家内外做功。做功变用：只改第二次实际用忌；不改正从、我客、家内外。岁运增减：只改实际用神力量阈值；我党（定体印比）严重减少则地理补救。流局是外部环境，每字1.0只乘与命主距离；令地／年权只给人类盘。天干基数以日主为中心（年3/4／月7/8／日1.0／时3/4），地支基数以月令为中心（年3/4／月1.0／日7/8／时3/4）。断事分层：底盘看正从，钥匙看用忌，路况看大运，地理看补救。我客以定体为准断正从与夭寿身体能力；家内外以定境为准断自家与可利用资源。字能含藏干带着五行与十神，落在我客／用忌／内外，再遇运年与地理总量看消长，细事可读到时辰（非命中率，不卖铁口）。六亲以该星或原宫干支含藏干代表原星，当命主太极转换：印比我方、财官伤客方，断其环境与消长；不改写本盘正从。";
  const TAIJI_LINE = "六亲以该星或原宫干支含藏干代表原星，当命主太极转换。印比＝我方，财官伤＝客方。本局为环境，十神相对新原点全部重定；原日主只作环境中一字。先护其体再转换。不改写本盘正从与柱域。见星非即人，不卖铁口。";
  const TIANYI_LINE = "身体活在三维，是时间矢量的单向短线；意识或可读四维结构（四柱／合参／太极是投影，不是逃）。果因率（因果律）谁都逃不了。AI只把结构算清，不能代人逃因果，也不能改写天意。531 其余为心性与天意，不卖铁口。";

  const WXZH = ["木", "火", "土", "金", "水"];
  const CHAR_HOUR_LINE = "每个字含藏干的能量带着自身五行与十神，落在它所处的我客／用忌／内外。再遇运年作用与地理总量，看消长，细事可读到时辰（非命中率）。地理不进字条，只改该字所属我客／内外总量。531 其余为心性与天意，不卖铁口。";

  function domainOfKey(key) {
    const k = String(key || "");
    if (k === "day" || k === "time" || k === "日" || k === "时") return "内";
    return "外";
  }

  function yongArg(arg) {
    if (arg && typeof arg === "object") return { useGuest: !!arg.useGuest, balanced: !!arg.balanced, orient: arg.orient || "" };
    return { useGuest: !!arg, balanced: false, orient: "" };
  }

  function tagSeat(day, ch, pillarKey, useGuest) {
    const yong = yongArg(useGuest);
    const ss = shiShen(day, ch);
    const wi = stemWX(ch);
    const wx = wi < 0 ? "" : WXZH[wi];
    const bucket = godBucket(ss);
    const camp = bucket === "wo" ? "我" : "客";
    const yongJi = yong.balanced ? "看" : ((yong.useGuest ? bucket === "ta" : bucket === "wo") ? "用" : "忌");
    return { ch: ch, wx: wx, ss: ss, camp: camp, yongJi: yongJi, domain: domainOfKey(pillarKey), bucket: bucket };
  }

  function tagCharRows(rows, day, useGuest) {
    return (rows || []).map(function (p) {
      const key = p.key || p.lab;
      const ganTag = tagSeat(day, p.gan && p.gan.ch, key, useGuest);
      const gan = Object.assign({}, p.gan, ganTag, { e: p.gan && p.gan.e });
      const hides = (p.hides || []).map(function (h) {
        return Object.assign({}, h, tagSeat(day, h.ch, key, useGuest));
      });
      return Object.assign({}, p, { gan: gan, hides: hides, domain: domainOfKey(key) });
    });
  }

  function stemWX(ch) { const i = TG.indexOf(ch); return i < 0 ? -1 : STEM_WX[i]; }
  function yang(ch) { return !!YANG[ch]; }
  function term(s) {
    const L = global.KRLang;
    return L && L.term ? L.term(s) : s;
  }

  /** 日主对某干的十神（地支按本气） */
  function shiShen(day, other) {
    if (!day || !other || day === other) return "日主";
    const d = stemWX(day), o = stemWX(other);
    if (d < 0 || o < 0) return "";
    const same = yang(day) === yang(other);
    if (o === d) return same ? "比肩" : "劫财";
    if ((d + 1) % 5 === o) return same ? "食神" : "伤官";
    if ((d + 2) % 5 === o) return same ? "偏财" : "正财";
    if ((d + 3) % 5 === o) return same ? "七杀" : "正官";
    return same ? "偏印" : "正印";
  }

  function branchMainStem(br) {
    const HIDE = {
      子: "癸", 丑: "己", 寅: "甲", 卯: "乙", 辰: "戊", 巳: "丙",
      午: "丁", 未: "己", 申: "庚", 酉: "辛", 戌: "戊", 亥: "壬",
    };
    return HIDE[br] || "";
  }

  function hasChar(pillars, ch) {
    return PKEY.some(k => pillars[k] && (pillars[k][0] === ch || pillars[k][1] === ch));
  }
  function natalBranches(pillars) {
    return PKEY.map(k => pillars[k] && pillars[k][1]).filter(Boolean);
  }
  function natalStems(pillars) {
    return PKEY.map(k => pillars[k] && pillars[k][0]).filter(Boolean);
  }

  function ironLaws() {
    return [
      { id: "a0", zh: "令值旺1.5／相1.25／休0.75／囚⅞／死⅝。戌影响 1.0，午影响 1.0×¼，先戌后午。普通生克必须紧贴 a=0。a≥1 只特关合冲，无生克。字内藏干不互作用，两字间同行不作用。先算藏干特关；若无特关，相生先生后克，相克先克后生。流层对原局分隔不生克，入局只经纠缠与共振；流年对运 a=0 可生克。单字共振局中＋¼×E、不流动。天干同行只传递。凡合冲生克以小量为基数。四冲／五合可隔位遥感×E。做功后只改用忌，不改正从／我客／家内外。" },
      { id: "chain", zh: "干∥支作用后含量固定，通根（同字共振＋同柱同行传递）后五行固定。目字十神生链／截断不改能量，只判流通与截断。制杀除外，不以截断论凶。" },
      { id: "genqi", zh: "根气共振：两透干虽隔位，但同根于同一地支藏干，视为能量互通（庚气可转化为壬水）。" },
      { id: "vault", zh: "墓库物化：刑冲可开库，库中藏干不透出则天不作向外用神（戌中辛永不合身）。" },
      { id: "chonghe", zh: "冲合即变：地支六冲、六合可开库或变性（辰戌冲开水库）。两字间 a=0 先合冲再生克。会合先合后冲，再到生最后到克；刑冲害破先冲后合，再到克最后到生。两字间同行不作用。不走整字合化。" },
      { id: "backup", zh: "备份激活：天干被合／克，若地支有其中气／余气藏干，即刻激活为备用电源，继续传导。" },
      { id: "hehua", zh: "天干特关仅四冲／五合，可隔位遥感×E。五合属会合、以小量化；否则绊，不聚 C，能量按普通克。四冲属刑冲，小量强−½弱−⅝，与五合同级，共干则合解冲。地支特关分会合／刑冲害破。" },
      { id: "zhisha", zh: "制杀论权：火克金（食伤制杀）不以截断论凶，而以制伏得用论吉——杀被制则化为权威与专业壁垒。" },
    ];
  }

  function hideOf(stem, pillars) {
    const hits = [];
    PKEY.forEach(k => {
      const gz = pillars[k]; if (!gz) return;
      (HIDE_LAYERS[gz[1]] || []).forEach(function (pair) {
        if (pair[0] === stem) hits.push({ branch: gz[1], pillar: PZH[k], layer: pair[1] });
      });
    });
    return hits;
  }

  function detectSystems(pillars, rel) {
    const day = pillars.day[0];
    const stems = natalStems(pillars);
    const branches = natalBranches(pillars);
    const pairs = (rel && rel.sharedRoots) || (global.KRRelations && global.KRRelations.sharedRootPairs(pillars)) || [];
    const vault = (rel && rel.vaultTrapped) || (global.KRRelations && global.KRRelations.vaultTrappedOf(pillars)) || [];
    const keys = (rel && rel.keys) || [];

    const shaStems = stems.filter(s => s !== day && shiShen(day, s) === "七杀");
    const yinStems = stems.filter(s => s !== day && (shiShen(day, s) === "偏印" || shiShen(day, s) === "正印"));
    const systems = { A: null, B: null, C: null };

    pairs.forEach(p => {
      const a = p.stems[0], b = p.stems[1];
      const sha = shaStems.includes(a) ? a : (shaStems.includes(b) ? b : null);
      const yin = yinStems.includes(a) ? a : (yinStems.includes(b) ? b : null);
      if (sha && yin) {
        systems.A = {
          id: "A", kind: "有情",
          name: "杀印相生",
          chain: `${sha}→${p.branch}→${yin}→${day}`,
          rootBranch: p.branch,
          sha: sha, yin: yin, day: day,
          xin: "以柔克刚",
          note: `${sha}（杀）不走克身，与${yin}同根于${p.branch}，化为印再生身。`,
        };
      }
    });

    keys.forEach(k => {
      if (k.type !== "ke") return;
      const chars = k.chars || [];
      const pair = chars.join("");
      yinStems.forEach(yin => {
        const cai = chars.find(c => c !== yin);
        if (!cai) return;
        if (chars.indexOf(yin) < 0) return;
        const ss = shiShen(day, branchMainStem(cai) || cai);
        if (ss === "偏财" || ss === "正财" || cai === "戌" || cai === "丑" || cai === "辰" || cai === "未" || cai === "戊" || cai === "己") {
          if (/同柱/.test(k.note || "") || (k.a === 0 && /干/.test(k.layerZh || "") && /支/.test(k.layerZh || ""))) {
            systems.B = {
              id: "B", kind: "无情",
              name: "财坏印",
              chain: `${cai}→克${yin}`,
              cai: cai, yin: yin,
              xin: "为钱所困",
              note: `${cai}紧贴克${yin}，生身气口被财截流。`,
            };
          }
        }
      });
    });

    if (systems.A && systems.A.rootBranch) {
      const root = systems.A.rootBranch;
      keys.forEach(k => {
        if (k.type !== "ke") return;
        const chars = k.chars || [];
        if (chars.indexOf(root) < 0) return;
        const other = chars.find(c => c !== root);
        if (!other) return;
        const ss = shiShen(day, branchMainStem(other) || other);
        if (ss === "伤官" || ss === "食神" || other === "午" || other === "巳" || other === "丁" || other === "丙") {
          systems.C = {
            id: "C", kind: "独立",
            name: "伤官制杀",
            chain: `${other}→克${root}`,
            shang: other, root: root,
            xin: "制伏得权",
            note: `${other}紧贴克${root}，只消耗杀性、不损水性，不截断链1。制杀论权，化为权威与专业壁垒。`,
          };
        }
      });
    }

    return { systems, shaStems, yinStems, pairs, vault, day };
  }

  function mapStar(pillars, det, star) {
    const day = det.day;
    const A = det.systems.A, B = det.systems.B, C = det.systems.C;
    const ss = star.ss;
    const base = {
      ch: star.ch, seat: star.seat, ss: ss,
      xinXing: XIN_XING[ss] || "",
      gongYong: "", xian: "", verdict: "", tone: "mid",
    };

    if (ss === "七杀" && A && A.sha === star.ch) {
      base.gongYong = `不克身。虽与日主邻柱，但不走克路、走共振路：${A.chain}。`;
      base.xian = "心性中的「怕」全变成「谋」。面对大佬施压，第一反应不是对抗，而是调动深层直觉，把危机转化成解题思路。";
      base.verdict = "七杀化印，有情做功（100%有效）。";
      base.tone = "good";
      return base;
    }
    if ((ss === "偏印" || ss === "正印") && A && A.yin === star.ch) {
      const sick = !!B;
      const bak = hideOf(star.ch, pillars).filter(h => h.layer === "中" || h.layer === "余");
      const bakZh = bak.length ? bak.map(h => `${h.pillar}支${h.branch}中${star.ch}（${h.layer}气）`).join("、") + "即刻激活为备用电源，持续供水。" : "";
      base.gongYong = sick
        ? `邻柱满格生身，是唯一气口；但被${B.cai}紧贴克制（财坏印，100%），生身之力大幅削弱。${bakZh}`
        : "邻柱满格生身，气口畅通。";
      base.xian = sick
        ? "表面依赖直觉与偏门学识，但总被现实利益拖累。气口被截时，靠地支藏干备份续命。"
        : "直觉与学识能直接养身。";
      base.verdict = sick ? "生身链条虽通，水流极细；备份未断，带病做功。" : "印星满格生身，有情做功。";
      base.tone = sick ? "warn" : "good";
      return base;
    }
    if ((ss === "伤官" || ss === "食神") && C && (C.shang === star.ch || star.ch === C.shang)) {
      const sha = (A && A.sha) || "杀";
      const yin = (A && A.yin) || "印";
      const root = C.root || (A && A.rootBranch) || "根";
      base.gongYong = `${C.chain}（a=0，100%）。${root}同时承载${sha}的杀气与${yin}的印气；${C.shang}克${root}只消耗杀性，不损水性，不截断链1。`;
      base.xian = "骨子里有傲骨，靠专业硬刚危机。制杀得权，化为权威与专业壁垒；每次刚完，必耗一层气血。";
      base.verdict = "制伏得用论吉。独立制杀得权，不截断有情化杀生身之链。";
      base.tone = "good";
      return base;
    }
    if (TIANGAN_HE[star.ch] === day && /[年月时]干/.test(star.seat)) {
      const hua = TIANGAN_HE_HUA[star.ch + day] || TIANGAN_HE_HUA[day + star.ch] || "";
      const seatKey = /时/.test(star.seat) ? "time" : (/月/.test(star.seat) ? "month" : "year");
      const crit = hehuaCritOf(pillars, hua, "day", seatKey);
      if (crit.mode === "ban") {
        base.gongYong = `${day}${star.ch}合${hua}而不化（${crit.reason}）。合绊仍是特关，不聚化神，能量按普通克，不按${hua}做功。`;
        base.xian = "日主被合绊，性情胶着；不改道为新五行，原十神按克路耗能。";
        base.verdict = `合而不化，合绊。正从不改。`;
        base.tone = "warn";
        return base;
      }
      base.gongYong = `${day}${star.ch}合${hua}（紧贴·${crit.reason}）。合化优先：按${hua}做功，大于单一生克／官杀克身。`;
      base.xian = "日主被合，性情改道；先论合化新五行，再论原十神心性。";
      base.verdict = `合化优先，按${hua}做功。`;
      base.tone = "good";
      return base;
    }
    if ((ss === "偏财" || ss === "正财") && B && (B.cai === star.ch || star.seat.indexOf(B.cai) >= 0)) {
      const trap = (det.vault || []).filter(v => {
        if (!(v.branch === star.ch || star.seat.indexOf(v.branch) >= 0)) return false;
        const hid = shiShen(day, v.stem);
        return hid === "正官" || hid === "七杀";
      });
      const xinNote = trap.length
        ? trap.map(v => `${v.branch}中${v.stem}（${shiShen(day, v.stem)}）受困永不出库，绝不会走「正官合身」的体制稳升线。`).join(" ")
        : "财星紧贴坏印。";
      base.gongYong = `开库只加强「财坏印」。${xinNote}`;
      base.xian = "赚钱欲望一生动，立刻克死庇护星。开库释放燥土，不做正向流通。";
      base.verdict = "财星只负责捣乱，不做正向流通。库开物不出。";
      base.tone = "bad";
      return base;
    }

    base.gongYong = "按铁律：非紧贴则天干生克归零；有通根则只认根气，不认隔位生克。";
    base.xian = "心性仍按其十神气质显，功用须看它是否进入传递链。";
    base.verdict = "未入三条主链，不作向外用神夸大。";
    return base;
  }

  function starsOf(pillars, det) {
    const day = det.day;
    const seen = {};
    const stars = [];
    PKEY.forEach(k => {
      const gz = pillars[k]; if (!gz) return;
      const gan = { ch: gz[0], seat: PZH[k] + "干", ss: shiShen(day, gz[0]) };
      if (gz[0] !== day && !seen["g" + gz[0]]) { seen["g" + gz[0]] = 1; stars.push(gan); }
      const brSs = shiShen(day, branchMainStem(gz[1]));
      const br = { ch: gz[1], seat: PZH[k] + "支", ss: brSs };
      if (!seen["z" + gz[1]]) { seen["z" + gz[1]] = 1; stars.push(br); }
    });
    const order = { 七杀: 1, 偏印: 2, 正印: 3, 伤官: 4, 食神: 5, 偏财: 6, 正财: 7 };
    stars.sort((a, b) => (order[a.ss] || 9) - (order[b.ss] || 9));
    return stars.map(s => mapStar(pillars, det, s)).filter(s => s.ss !== "日主");
  }

  /** 作者盘黄金样办：癸丑 壬戌 甲申 庚午。脚本句只绑此盘。 */
  function isAuthorNatal(pillars) {
    return !!(pillars && pillars.year === "癸丑" && pillars.month === "壬戌"
      && pillars.day === "甲申" && pillars.time === "庚午");
  }

  function hehuaCritOf(pillars, hua, keyA, keyB) {
    const R = global.KRRelations;
    const monthBr = pillars && pillars.month && pillars.month[1];
    if (R && R.hehuaThreshold) return R.hehuaThreshold(hua, monthBr, pillars, keyA, keyB);
    return { canHua: true, mode: "hua", ling: "旺", reason: "合化优先，按" + hua + "做功", chongPo: false };
  }

  function natalHehua(pillars) {
    const day = pillars.day && pillars.day[0];
    if (!day) return [];
    const pi = { year: 0, month: 1, day: 2, time: 3 };
    const hits = [];
    PKEY.forEach(k => {
      if (k === "day") return;
      const st = pillars[k] && pillars[k][0];
      if (!st || TIANGAN_HE[st] !== day) return;
      const a = Math.abs(pi[k] - 2);
      const hua = TIANGAN_HE_HUA[st + day] || TIANGAN_HE_HUA[day + st] || "";
      const crit = hehuaCritOf(pillars, hua, "day", k);
      const monthBr = pillars.month && pillars.month[1];
      const dist = a === 0 ? "同柱" : (a === 1 ? "邻柱" : "隔位遥感×E=" + Math.pow(0.5, a));
      if (crit.mode === "ban") {
        hits.push({
          stem: st, pillar: PZH[k], hua: hua, a: a,
          mode: "ban", ling: crit.ling, reason: crit.reason,
          text: `${day}${st}合${hua}而不化（${PZH[k]}干${dist}，月令${monthBr || "?"}，${crit.reason}）。合绊仍是特关，不聚化神，能量按普通克，不按${hua}做功。`,
        });
      } else {
        hits.push({
          stem: st, pillar: PZH[k], hua: hua, a: a,
          mode: "hua", ling: crit.ling, reason: crit.reason,
          text: `${day}${st}合${hua}（${PZH[k]}干${dist}，月令${monthBr || "?"}，${crit.reason}）。合化优先，按${hua}做功。`,
        });
      }
    });
    return hits;
  }

  function natalXing(pillars) {
    const br = natalBranches(pillars);
    const hits = [];
    if (br.indexOf("丑") >= 0 && br.indexOf("戌") >= 0) {
      hits.push({ kind: "刑", chars: ["丑", "戌"], text: "丑戌刑。刑开库或伤库中藏干；透则作向外用神，不透则永不合身。" });
    }
    if (br.indexOf("寅") >= 0 && br.indexOf("巳") >= 0 && br.indexOf("申") >= 0) {
      hits.push({ kind: "三刑", chars: ["寅", "巳", "申"], text: "寅巳申三刑。冲合即变，根气与合绊同看。" });
    }
    return hits;
  }

  function luckDelta(gz, pillars, systems) {
    if (!gz || gz.length < 2) return [];
    const br = gz[1], st = gz[0];
    const tags = [];
    const A = systems.A, C = systems.C;
    const day = pillars.day ? pillars.day[0] : "";
    const script = isAuthorNatal(pillars) ? YUN_SCRIPT[gz] : null;
    if (script) {
      script.tags.forEach(t => tags.push(t));
      return tags;
    }
    if (TIANGAN_HE[st] === day) {
      const hua = TIANGAN_HE_HUA[st + day] || TIANGAN_HE_HUA[day + st] || "";
      const crit = hehuaCritOf(pillars, hua, "day", null);
      if (crit.mode === "ban") {
        tags.push({
          kind: "heban",
          reason: `${day}${st}合${hua || ""}而不化（月令${crit.ling}）`,
          text: "岁运合绊：不改实际用神身份。若绊的是用神，补亦无用。",
        });
      } else {
        tags.push({
          kind: "hehua",
          reason: `${day}${st}合${hua || ""}`,
          text: "岁运合而化：只加减化神力量阈值，不改实际用神身份。月干印若未被合，生身未断。",
        });
      }
    }
    if (A && A.yin && stemWX(st) >= 0 && ((stemWX(st) + 2) % 5 === stemWX(A.yin))) {
      const bak = hideOf(A.yin, pillars).filter(h => h.layer === "中" || h.layer === "余");
      tags.push({
        kind: "hardKe",
        reason: `${st}紧贴克${A.yin}（天干硬断）`,
        text: bak.length
          ? `${bak.map(h => h.branch + "中" + A.yin + "（" + h.layer + "气）").join("、")}即刻激活为备用电源，持续供水。惨胜，非崩溃。`
          : "印干被克，生身气口硬断。",
      });
    }
    if (A && A.sha && stemWX(st) >= 0 && ((stemWX(st) + 2) % 5 === stemWX(A.sha))) {
      tags.push({ kind: "zhisha", reason: `${st}紧贴制${A.sha}`, text: "食伤制杀得权：不以截断论凶，杀被制化为权威与专业壁垒。" });
    }
    if (br === "辰" && hasChar(pillars, "戌")) {
      const yin = (A && A.yin) || "印";
      const root = (A && A.rootBranch) || "";
      const half = root ? `对${root}中${yin}（大运对日 a=0）补水` : "大运对日补水";
      tags.push({ kind: "openVault", reason: "辰戌冲开水库", text: `冲散戌土对${yin}的克制，财星截断解除；辰为水库，${half}。` });
    }
    if (A && A.rootBranch && LIUCHONG[br] === A.rootBranch) {
      tags.push({ kind: "shakeRoot", reason: `${br}冲${A.rootBranch}摇根`, text: "根气动摇，链1带病；若藏干备份仍在，不按崩盘、不改论只剩制杀。" });
    }
    if (br === "子" && hasChar(pillars, "丑")) {
      tags.push({ kind: "closeVault", reason: "子合丑闭库", text: "年支财库被合绊；链1根在申，不因此直接崩盘。" });
    }
    if (br === "卯" && hasChar(pillars, "戌")) {
      tags.push({ kind: "hehuaZhi", reason: "卯合戌化火", text: "冲合即变：戌土财坏印之性被合化改写。" });
    }
    if (A && br === A.rootBranch) {
      tags.push({ kind: "boostA", reason: `${br}再现根气基站`, text: "链1中转站加码；杀源仍化印，不克身。" });
    }
    if (C && br === C.shang) {
      tags.push({ kind: "zhisha", reason: `${br}再现伤官制杀`, text: "制杀得权加强，只消耗杀性，不损水性，不截断链1。" });
    }
    if (A && st === A.sha) {
      tags.push({ kind: "boostA", reason: `${st}杀透再现`, text: "杀气加码；有根气共振则仍走化印。" });
    }
    return tags;
  }

  const YUN_SCRIPT = {
    己未: {
      tags: [{
        kind: "hehua",
        reason: "甲己合土",
        text: "日主被财星牵制，但月干壬水依然紧贴生身（未被合），偏印之力未断，仅分心于财务。",
      }],
    },
    戊午: {
      tags: [
        { kind: "hardKe", reason: "戊土紧贴克壬水（天干硬断）", text: "日支申中壬水（中气）即刻激活为备用电源，持续供水。" },
        { kind: "zhisha", reason: "午火制申金", text: "制杀得权，只削杀性。此运为惨胜，非崩溃。" },
      ],
    },
    丙辰: {
      tags: [
        { kind: "openVault", reason: "辰戌紧贴相冲（a=0）", text: "冲散戌土对壬水的克制，财星截断解除。" },
        { kind: "backup", reason: "辰水库对申中壬（大运对日 a=0）", text: "日柱紧贴补水，备份加粗。" },
        { kind: "zhisha", reason: "丙火制时干庚金（大运对时 a=1，50%）", text: "食神制杀得权。" },
      ],
    },
  };

  function firstFourYun(dayun, pillars, systems) {
    const list = (dayun || []).filter(d => d && d.gz && d.gz.length >= 2 && (d.startAge == null || d.startAge > 0));
    function row(d, i) {
      const tags = luckDelta(d.gz, pillars, systems);
      const line = tags.length
        ? tags.map(t => t.reason + " → " + t.text).join(" ")
        : "原局两链维持；看该步是否加力杀源／制杀／财坏印。";
      return {
        i: i + 1,
        gz: d.gz,
        years: (d.startYear && d.endYear) ? `${d.startYear}–${d.endYear}` : "",
        ages: (d.startAge != null && d.endAge != null) ? `${d.startAge}–${d.endAge}岁` : "",
        tags: tags,
        line: line,
      };
    }
    const out = list.slice(0, 4).map(row);
    const seen = {};
    out.forEach(y => { seen[y.gz] = 1; });
    list.forEach((d, i) => {
      if (isAuthorNatal(pillars) && YUN_SCRIPT[d.gz] && !seen[d.gz]) { seen[d.gz] = 1; out.push(row(d, i)); }
    });
    return out;
  }

  function godBucket(ss) {
    if (ss === "正印" || ss === "偏印" || ss === "比肩" || ss === "劫财" || ss === "印" || ss === "比劫") return "wo";
    if (ss === "日主") return "wo";
    return "ta";
  }

  function huaShiShen(day, hua) {
    const d = stemWX(day);
    const WXZH = { 木: 0, 火: 1, 土: 2, 金: 3, 水: 4 };
    const o = WXZH[hua];
    if (d < 0 || o == null) return "";
    if (o === d) return "比劫";
    if ((d + 1) % 5 === o) return "食伤";
    if ((d + 2) % 5 === o) return "财";
    if ((d + 3) % 5 === o) return "官杀";
    return "印";
  }

  function liBai(useGuest, bucket) {
    const yong = yongArg(useGuest);
    if (yong.balanced) {
      if (yong.orient === "做功利自己与家内") return "利";
      if (yong.orient === "做功败自己与家内") return "败";
      if (yong.orient === "做功一利一败") return "半";
      return "";
    }
    if (yong.useGuest) return bucket === "ta" ? "利" : "败";
    return bucket === "wo" ? "利" : "败";
  }

  function liBaiNote(verdict, useGuest, bucket) {
    const yong = yongArg(useGuest);
    if (yong.balanced) {
      const why = yong.orient || "用忌看家内外做功";
      if (verdict === "利") return "利于自身与家内（" + why + "）";
      if (verdict === "败") return "败自身与家内（" + why + "）";
      if (verdict === "半") return "一利一败（" + why + "）";
      return why;
    }
    const why = yong.useGuest
      ? (bucket === "ta" ? "用客·客党做功" : "用客·我党做功＝利忌")
      : (bucket === "wo" ? "用我·我党做功" : "用我·客党做功＝利忌");
    return verdict === "利" ? "利于用（" + why + "）" : "败／利忌（" + why + "）";
  }

  function seatDomain(pillars, ch) {
    const hits = [];
    PKEY.forEach(k => {
      const gz = pillars[k]; if (!gz || !ch) return;
      const domain = (k === "year" || k === "month") ? "外" : "内";
      if (gz[0] === ch) hits.push({ pillar: PZH[k], layer: "干", domain: domain });
      if (gz[1] === ch) hits.push({ pillar: PZH[k], layer: "支", domain: domain });
    });
    return hits;
  }

  function domainZh(hits) {
    if (!hits || !hits.length) return "";
    const d = unique(hits.map(h => h.domain));
    if (d.length === 2) return "内外贯通";
    return d[0] === "外" ? "家外" : "家内";
  }

  function kinTable(sex) {
    return (sex === "F" || sex === "女" || sex === 0) ? KIN_FEMALE : KIN_MALE;
  }

  function xunKongOf(dayGz) {
    if (!dayGz || dayGz.length < 2) return "";
    const stems = "甲乙丙丁戊己庚辛壬癸";
    const brs = "子丑寅卯辰巳午未申酉戌亥";
    const si = stems.indexOf(dayGz[0]), bi = brs.indexOf(dayGz[1]);
    if (si < 0 || bi < 0) return "";
    const jiaBr = ((bi - si) % 12 + 12) % 12;
    const map = { 子: "戌亥", 戌: "申酉", 申: "午未", 午: "辰巳", 辰: "寅卯", 寅: "子丑" };
    return map[brs[jiaBr]] || "";
  }

  function isExposed(pillars, ch) {
    return PKEY.some(k => pillars[k] && pillars[k][0] === ch);
  }

  function natalCharEnergy(pillars, ch) {
    const E = global.KREngine;
    const P = global.KRParams || {};
    if (!E || !E.layerLingFactor || !ch) return 0;
    const monthBr = "子丑寅卯辰巳午未申酉戌亥".indexOf(pillars.month && pillars.month[1]);
    const hourBr = "子丑寅卯辰巳午未申酉戌亥".indexOf(pillars.time && pillars.time[1]);
    if (monthBr < 0) return 0;
    const wi = stemWX(ch);
    if (wi < 0) return 0;
    const diRel = (P.diRelativeToLing != null) ? P.diRelativeToLing : 0.25;
    const apply = E.lingApply || function (L, ev) { return 1 - (1 - L) * ev; };
    const dApply = E.diApply || function (D, ev, r) { return 1 - (1 - D) * (r == null ? 0.25 : r) * ev; };
    const diF = E.layerDiFactor || E.layerLingFactor;
    let e = 0;
    PKEY.forEach((k, i) => {
      const gz = pillars[k]; if (!gz) return;
      const eLingG = E.lingFallE ? E.lingFallE(i, "gan") : 1;
      const eLingZ = E.lingFallE ? E.lingFallE(i, "zhi") : 1;
      const eDiG = E.diFallE ? E.diFallE(i, "gan") : 1;
      const eDiZ = E.diFallE ? E.diFallE(i, "zhi") : 1;
      if (gz[0] === ch) e += (STEM_BASE[k] || 0.7) * apply(E.layerLingFactor(wi, monthBr, P), eLingG)
        * dApply(diF(wi, hourBr < 0 ? monthBr : hourBr, P), eDiG, diRel);
      (HIDE_W[gz[1]] || []).forEach(function (pair) {
        if (pair[0] === ch) e += pair[1] * (BRANCH_BASE[k] || 0.75) * apply(E.layerLingFactor(wi, monthBr, P), eLingZ)
          * dApply(diF(wi, hourBr < 0 ? monthBr : hourBr, P), eDiZ, diRel);
      });
    });
    return e;
  }

  function glyphBand(e) {
    if (e < 1) return "危";
    if (e < 2) return "安";
    if (e < 7) return "旺";
    return "极旺";
  }

  function signed(n) {
    const v = Number(n) || 0;
    return (v >= 0 ? "+" : "") + v.toFixed(2);
  }

  function layerRank(layer) {
    return layer === "透" ? 0 : (layer === "本" ? 1 : (layer === "中" ? 2 : 3));
  }

  function listStarHits(pillars, day, targetSS, sibling) {
    const hits = [];
    PKEY.forEach(k => {
      const gz = pillars[k]; if (!gz) return;
      if (!(sibling && k === "day" && gz[0] === day)) {
        const ssG = shiShen(day, gz[0]);
        if (ssG === targetSS) {
          hits.push({ ch: gz[0], kind: "gan", pillar: k, layer: "透", seat: PZH[k] + "干", br: "" });
        }
      }
      (HIDE_LAYERS[gz[1]] || []).forEach(function (pair) {
        if (sibling && k === "day" && pair[0] === day) return;
        const ssH = shiShen(day, pair[0]);
        if (ssH === targetSS) {
          hits.push({
            ch: pair[0], kind: "hide", pillar: k, layer: pair[1],
            seat: PZH[k] + "支" + gz[1] + "中" + pair[0], br: gz[1],
          });
        }
      });
    });
    hits.sort((a, b) => layerRank(a.layer) - layerRank(b.layer));
    return hits;
  }

  function palaceHits(pillars, day, kin) {
    const k = kin.palace || "month";
    const gz = pillars[k];
    if (!gz) return [];
    const hits = [];
    if (kin.palacePart !== "zhi") {
      hits.push({ ch: gz[0], kind: "gan", pillar: k, layer: "透", seat: PZH[k] + "干", br: "", palace: true });
    }
    (HIDE_LAYERS[gz[1]] || []).forEach(function (pair) {
      hits.push({
        ch: pair[0], kind: "hide", pillar: k, layer: pair[1],
        seat: PZH[k] + "支" + gz[1] + "中" + pair[0], br: gz[1], palace: true,
      });
    });
    const match = hits.filter(h => shiShen(day, h.ch) === kin.ss);
    return match.length ? match : hits;
  }

  function proxyStars(kin) {
    if (!kin.sibling) return [];
    if (kin.junior) return kin.ss === "食神" ? ["食神"] : ["伤官"];
    if (kin.name === "兄" || kin.id === "xiong") return ["偏印", "正印"];
    return ["正印", "偏印"];
  }

  function pickOrigin(pillars, day, kin) {
    const starHits = listStarHits(pillars, day, kin.ss, !!kin.sibling);
    let source = "该星";
    let hits = starHits;
    let proxy = false;
    let proxySS = "";
    if (!hits.length && kin.sibling) {
      const pss = proxyStars(kin);
      for (let i = 0; i < pss.length && !hits.length; i++) {
        hits = listStarHits(pillars, day, pss[i], false);
        if (hits.length) { source = "代取"; proxy = true; proxySS = pss[i]; }
      }
    }
    if (!hits.length) {
      hits = palaceHits(pillars, day, kin);
      source = "原宫";
    }
    const hit = hits[0] || null;
    if (!hit) return null;
    const kong = xunKongOf(pillars.day);
    const empty = !!(hit.br && kong && kong.indexOf(hit.br) >= 0);
    const vault = hit.kind === "hide" && hit.layer !== "本" && !!VAULT_BR[hit.br] && !isExposed(pillars, hit.ch);
    return {
      ch: hit.ch,
      source: source,
      seat: hit.seat,
      layer: hit.layer,
      pillar: hit.pillar,
      kind: hit.kind,
      proxy: proxy,
      proxySS: proxySS,
      vault: vault,
      kong: empty,
      e0: natalCharEnergy(pillars, hit.ch),
    };
  }

  function scaleCharE(e0, wx, el0, el1) {
    const b = (el0 && el0[wx]) || 0;
    const a = (el1 && el1[wx]) || 0;
    if (b <= 1e-12) return e0;
    return e0 * (a / b);
  }

  function taijiRow(kin, origin, pillars, day, freeze) {
    const E = global.KREngine;
    const wi = stemWX(origin.ch);
    const wx = wi < 0 ? "" : WXZH[wi];
    const el0 = (freeze && freeze.elLingDi) || null;
    const geo = (freeze && freeze.geoBirth) || { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 };
    const elGongZi = (freeze && freeze.elShengKeZi) || el0;
    let jing = null, gong = null, campJing = null, campGong = null;
    if (E && E.campFromElements && E.mergeEl && E.judgeZhengCong && el0) {
      const elZ = E.mergeEl(el0, geo);
      const elG = E.mergeEl(elGongZi, geo);
      campJing = E.campFromElements(elZ, origin.ch);
      campGong = E.campFromElements(elG, origin.ch);
      jing = E.judgeZhengCong(campJing);
      gong = E.judgeZhengCong(campGong);
    }
    const e0 = origin.e0 || 0;
    const e1 = el0 && elGongZi ? scaleCharE(e0, wx, el0, elGongZi) : e0;
    const dayAs = shiShen(origin.ch, day);
    const remap = PKEY.map(k => {
      const gz = pillars[k];
      if (!gz) return "";
      return PZH[k] + "干" + gz[0] + shiShen(origin.ch, gz[0]);
    }).filter(Boolean);
    const bits = [];
    if (origin.proxy) bits.push("代取" + (origin.proxySS || "") + "（手足缺星）");
    if (origin.vault) bits.push("库中不透，不作向外用神");
    if (origin.kong) bits.push("坐空亡（只意象，不入能量）");
    if (jing && jing.wo <= 1) bits.push("其体未过1.0，先护其体再转换");
    const dWo = campGong && campJing ? campGong.wo - campJing.wo : 0;
    const dTa = campGong && campJing ? campGong.ta - campJing.ta : 0;
    const jingZh = jing
      ? `定境我${jing.wo.toFixed(2)}∶客${jing.ta.toFixed(2)} → ${jing.pattern}·${jing.strength} · ${jing.orient}`
      : "环境待核";
    const xiaoZh = campGong
      ? `做功后我${campGong.wo.toFixed(2)}（${signed(dWo)}）∶客${campGong.ta.toFixed(2)}（${signed(dTa)}）；原点字能${e0.toFixed(2)}${glyphBand(e0)}→${e1.toFixed(2)}${glyphBand(e1)}（${signed(e1 - e0)}）`
      : `原点字能${e0.toFixed(2)}${glyphBand(e0)}`;
    const originSS = shiShen(day, origin.ch);
    const starZh = origin.source === "该星"
      ? `${kin.ss}${origin.ch}（${origin.seat}·该星）`
      : (origin.source === "代取"
        ? `原星${kin.ss}未见，代取${origin.proxySS || originSS}${origin.ch}（${origin.seat}）`
        : `原星${kin.ss}未见，原宫${origin.seat}${origin.ch}（${originSS}）`);
    const line = `【太极·${kin.name}】${starZh}当命主。${jingZh}。${xiaoZh}。原日主${day}在其环境为${dayAs}。${bits.length ? bits.join("；") + "。" : ""}不改写本盘正从。`;
    return {
      id: kin.id, name: kin.name, ss: kin.ss, palaceZh: kin.palaceZh,
      origin: origin, wx: wx, dayAs: dayAs, remap: remap, starZh: starZh,
      jing: jing, gong: gong, campJing: campJing, campGong: campGong,
      e0: e0, e1: e1, dE: e1 - e0,
      jingZh: jingZh, xiaoZh: xiaoZh, note: bits.join("；"),
      line: line,
    };
  }

  function taijiOf(pillars, sex, freeze) {
    const day = pillars && pillars.day && pillars.day[0];
    if (!day) return { ok: false, rows: [], line: TAIJI_LINE };
    const rows = kinTable(sex).map(kin => {
      const origin = pickOrigin(pillars, day, kin);
      if (!origin) {
        return {
          id: kin.id, name: kin.name, ss: kin.ss, palaceZh: kin.palaceZh,
          origin: null, line: `【太极·${kin.name}】未见${kin.ss}，原宫${kin.palaceZh}亦无字可代。见星非即人。`,
        };
      }
      return taijiRow(kin, origin, pillars, day, freeze);
    });
    return { ok: true, sex: (sex === "F" || sex === "女" || sex === 0) ? "F" : "M", line: TAIJI_LINE, rows: rows };
  }

  /**
   * 正从用忌 · 我客 · 家内外 → 格局 → 做功利败
   * 正从以地后为准，不做二次改写；用忌以生克后二次为尺。
   */
  function frameLedger(xin) {
    const F = xin.freeze;
    const items = [];
    const lines = [];
    if (!F || !F.zhuan) {
      return {
        ok: false,
        items: [],
        lines: ["【三、正从用忌】未传入冻结。先读链，利败待生克后二次用忌。"],
        summary: "",
        overall: "",
        patternNames: [],
      };
    }
    const di = F.diHou || F.zhuan;
    const zh = F.zhuan;
    const soft = !!zh.yongJiSoft;
    const balanced = !!(di.balanced || zh.balanced);
    const useGuest = balanced ? { useGuest: false, balanced: true, orient: zh.orient } : !!zh.useGuest;
    const flipped = di.orient && zh.orient && di.orient !== zh.orient;
    const el0 = F.elLingDi || di.elLingDi;
    const elZ = di.el;
    if (el0) {
      lines.push(`【定体·令地后五行】字作用前 木${(el0.木 || 0).toFixed(2)} 火${(el0.火 || 0).toFixed(2)} 土${(el0.土 || 0).toFixed(2)} 金${(el0.金 || 0).toFixed(2)} 水${(el0.水 || 0).toFixed(2)}（不含量地理）。令地旺衰不算合冲生克。`);
    }
    if (elZ) {
      lines.push(`【定体·正从总量】令地＋出生地理 木${(elZ.木 || 0).toFixed(2)} 火${(elZ.火 || 0).toFixed(2)} 土${(elZ.土 || 0).toFixed(2)} 金${(elZ.金 || 0).toFixed(2)} 水${(elZ.水 || 0).toFixed(2)} · 我${(di.wo || 0).toFixed(2)}∶客${(di.ta || 0).toFixed(2)} → ${di.pattern}·${di.strength}。地理不进字。`);
    }
    lines.push(`【三、正从用忌】定体·${di.pattern}·${di.strength}（正从冻结，终身不改身份）· 我${(di.wo || 0).toFixed(2)}∶客${(di.ta || 0).toFixed(2)}${balanced ? " · |我−客|／max≤⅛" : ""}。定境·第一次名义用忌＝${di.orient}。做功后只改用忌：实际用忌＝${zh.orient}${flipped ? "（相对定境已变）" : "（用忌未变）"}。正从、我客、家内外不改${soft ? " · 用忌轻" : ""}。岁运不得改此实际用神身份。`);
    if (F.geoRemedy) {
      lines.push(`【三、地理补救】大运流年使我党（水木／印比）从${(F.woGong || 0).toFixed(2)}降至${(F.woLive || 0).toFixed(2)}，含量严重减少。地理水火／金木不进字，只补正从／我客／家内外总量。`);
    }

    const V = F.vitality || {};
    const wo0 = V.woBefore != null ? V.woBefore : (di.wo || 0);
    const ta0 = V.taBefore != null ? V.taBefore : (di.ta || 0);
    const wo1 = V.woAfter != null ? V.woAfter : (zh.wo || 0);
    const ta1 = V.taAfter != null ? V.taAfter : (zh.ta || 0);
    const dWo = wo1 - wo0, dTa = ta1 - ta0;
    const shou = (V.shou && V.shou.zh) || "夭寿待核";
    const shen = (V.shen && V.shen.zh) || "身体待核";
    const neng = (V.neng && V.neng.zh) || "能力待核";
    lines.push(`【三、我客】我客以定体为准，做功不改写。定体我${wo0.toFixed(2)}∶客${ta0.toFixed(2)} → ${di.pattern}·${di.strength}。夭寿 ${shou}；身体 ${shen}；能力 ${neng}。`);

    const nei = F.nei != null ? F.nei : 0;
    const wai = F.wai != null ? F.wai : 0;
    const nei1 = F.neiAfter != null ? F.neiAfter : nei;
    const wai1 = F.waiAfter != null ? F.waiAfter : wai;
    const dNei = F.neiDelta != null ? F.neiDelta : (nei1 - nei);
    const dWai = F.waiDelta != null ? F.waiDelta : (wai1 - wai);
    const tilt = wai1 > nei1 * 1.15 ? "可利用资源重于自家" : (nei1 > wai1 * 1.15 ? "自家重于可利用资源" : "自家与可利用资源相若");
    const godBits = (V.gods || []).map(g => {
      const sign = g.delta >= 0 ? "+" : "";
      return `${g.name}${g.before.toFixed(2)}→${g.after.toFixed(2)}（${sign}${g.delta.toFixed(2)}）对日主${g.toDay}、对自家${g.toJia}`;
    }).join("；");
    lines.push(`【三、我客内外】家内＝自家实际量，家外＝可利用资源实际量。家内外以定境为准，做功不改写。家内${nei.toFixed(2)}∶家外${wai.toFixed(2)} · ${tilt}。柱域日时内／年月外，以内取外。${godBits ? "十神对自家与日主：" + godBits + "。" : ""}`);

    const dayCh = xin.day || (xin.pillars && xin.pillars.day && xin.pillars.day[0]);
    if (dayCh && xin.pillars) {
      const seats = PKEY.map(k => {
        const gz = xin.pillars[k];
        if (!gz) return "";
        const tg = tagSeat(dayCh, gz[0], k, useGuest);
        return `${PZH[k]}干${gz[0]}${tg.wx}${tg.ss}·${tg.camp}${tg.yongJi}${tg.domain}`;
      }).filter(Boolean);
      lines.push(`【三、字能时辰】${CHAR_HOUR_LINE}原局：${seats.join("；")}。`);
    }

    const TJ = xin.taiji || taijiOf(xin.pillars, xin.sex, F);
    if (TJ && TJ.ok) {
      lines.push(`【六、太极六亲】${TAIJI_LINE}`);
      (TJ.rows || []).forEach(function (r) { if (r.line) lines.push(r.line); });
    }

    const A = xin.systems && xin.systems.A;
    const B = xin.systems && xin.systems.B;
    const C = xin.systems && xin.systems.C;
    const names = [];

    if (A) {
      const v = liBai(useGuest, "wo");
      const seats = [].concat(seatDomain(xin.pillars, A.sha), seatDomain(xin.pillars, A.yin), seatDomain(xin.pillars, A.rootBranch));
      items.push({ name: "杀印相生", verdict: v, bucket: "wo", domain: domainZh(seats) });
      names.push("杀印相生");
      lines.push(`【三、格局】杀印相生（${A.chain}）${domainZh(seats) ? " · " + domainZh(seats) : ""} → ${liBaiNote(v, useGuest, "wo")}。`);
    }
    if (C) {
      const v = liBai(useGuest, "ta");
      const seats = [].concat(seatDomain(xin.pillars, C.shang), seatDomain(xin.pillars, C.root));
      items.push({ name: "伤官制杀", verdict: v, bucket: "ta", domain: domainZh(seats) });
      names.push("伤官制杀");
      lines.push(`【三、格局】伤官制杀（${C.chain}）${domainZh(seats) ? " · " + domainZh(seats) : ""} → ${liBaiNote(v, useGuest, "ta")}。`);
    }
    if (B) {
      const v = liBai(useGuest, "ta");
      const seats = [].concat(seatDomain(xin.pillars, B.cai), seatDomain(xin.pillars, B.yin));
      items.push({ name: "财坏印", verdict: v, bucket: "ta", domain: domainZh(seats) });
      names.push("财坏印");
      lines.push(`【三、格局】财坏印（${B.chain}）${domainZh(seats) ? " · " + domainZh(seats) : ""} → ${liBaiNote(v, useGuest, "ta")}。`);
    }
    (xin.hehua || []).forEach(h => {
      if (h.mode === "ban") {
        const ss0 = shiShen(xin.day, h.stem);
        const bucket0 = godBucket(ss0);
        const v0 = liBai(useGuest, bucket0);
        const yongList = (zh.yongShen || []);
        const metamorph = yongList.indexOf(ss0) >= 0;
        items.push({ name: "合绊" + (h.hua || ""), verdict: v0, bucket: bucket0, mode: "ban", yongMetamorph: metamorph });
        names.push((xin.day || "") + (h.stem || "") + "合" + (h.hua || "") + "而不化");
        lines.push(`【三、格局】合而不化：${h.text}原十神${ss0}相绊（按普通克，不按${h.hua}做功） → ${liBaiNote(v0, useGuest, bucket0)}。${metamorph ? "实际用神被合绊变质，岁运补亦无用。" : "合绊不改用神身份。"}`);
        return;
      }
      const ss = huaShiShen(xin.day, h.hua);
      const bucket = godBucket(ss);
      const v = liBai(useGuest, bucket);
      items.push({ name: "合化" + (h.hua || ""), verdict: v, bucket: bucket, mode: "hua" });
      names.push((xin.day || "") + (h.stem || "") + "合" + (h.hua || ""));
      lines.push(`【三、格局】合化优先：${h.text}化出${ss || h.hua}（${bucket === "wo" ? "我党" : "客党"}） → ${liBaiNote(v, useGuest, bucket)}。`);
    });
    (F.works || []).forEach(w => {
      if (names.indexOf(w.name) >= 0) return;
      names.push(w.name);
      const v = /利于用|有利于用/.test(w.jiXiong || "") && !/利忌/.test(w.jiXiong || "") ? "利"
        : /利忌|泄身|攻身|争合/.test(w.jiXiong || "") ? "败" : "";
      if (v) items.push({ name: w.name, verdict: v, bucket: "", fromWorks: true });
      lines.push(`【三、格局】${w.name}（十神份额） → ${w.jiXiong}。`);
    });
    if (!names.length) {
      names.push("扶抑常规");
      lines.push("【三、格局】未见杀印／制杀／合化主链，按扶抑常规读冻结用忌。");
    }

    const li = items.filter(i => i.verdict === "利").length;
    const bai = items.filter(i => i.verdict === "败").length;
    let overall = "待核";
    if (li && bai) overall = "半利半败";
    else if (li && !bai) overall = "利";
    else if (bai && !li) overall = "败";
    const softZh = soft ? "用忌轻，吉凶仍读做功方向。" : "";
    lines.push(`【三、做功】总断${overall}（利${li}／败${bai}）。利用则吉、利忌则凶。${softZh}不做二次正从。`);

    const summary = `${di.pattern}·${di.strength}（地后）／二次${zh.orient} · 格局${names.slice(0, 3).join("、")} → ${overall}`;
    return {
      ok: true, items, lines, summary, overall, patternNames: names,
      useGuest: balanced ? null : !!zh.useGuest, balanced: balanced, flipped, soft,
      nei: F.nei, wai: F.wai, neiAfter: F.neiAfter, waiAfter: F.waiAfter,
      neiFlow: F.neiFlow, waiFlow: F.waiFlow, stages: F.stages,
      vitality: V, geoRemedy: !!F.geoRemedy,
    };
  }

  function analyze(opts) {
    opts = opts || {};
    const pillars = opts.pillars;
    if (!pillars || !pillars.day) {
      return { ok: false, laws: ironLaws(), stars: [], systems: {}, summary: "", yun: [], live: [] };
    }
    const R = global.KRRelations;
    const rel = opts.rel || (R && R.scan ? R.scan(pillars, opts.el || { 木: 1, 火: 1, 土: 1, 金: 1, 水: 1 }, global.KRParams) : null);
    const det = detectSystems(pillars, rel);
    const stars = starsOf(pillars, det);
    const S = det.systems;
    const hehua = natalHehua(pillars);
    const xing = natalXing(pillars);
    const bak = S.A ? hideOf(S.A.yin, pillars).filter(h => h.layer === "中" || h.layer === "余") : [];
    const parts = [];
    if (S.A) {
      const cut = S.B ? `${S.B.cai}紧贴克${S.A.yin}（财坏印），生身削弱；` : "";
      const bakZh = bak.length
        ? `${bak.map(h => h.branch + "中" + S.A.yin + "（" + h.layer + "气）").join("、")}作备用电源，持续供水。`
        : "";
      parts.push(`链1（有情化杀生身）：${S.A.chain}，100%满格。${cut}${bakZh}`);
    }
    if (S.C) {
      parts.push(`链2（独立制杀得权）：${S.C.chain}，a=0、100%。${S.C.shang}克${S.C.root}只消耗杀性、不损水性，不截断链1。`);
    }
    if (S.B && !S.A) parts.push(`财坏印：${S.B.chain}。`);
    if (hehua.length && !S.A) parts.push(hehua.map(h => h.text).join(" "));
    const summary = parts.length
      ? (S.A && S.C ? "核心做功只认这两条链。" : "核心做功只认已检出的链／合化。") + parts.join(" ")
      : "本盘未同时检出杀印共振／伤官制杀主链，仍按铁律读紧贴、通根与合化。";
    const liveGz = opts.liveGz && opts.liveGz.length
      ? opts.liveGz
      : (opts.extras || []).map(function (e) { return { label: e.label, gz: e.gz }; });
    const yun = firstFourYun(opts.dayun, pillars, S);
    const live = liveGz.map(item => ({
      label: item.label, gz: item.gz, tags: luckDelta(item.gz, pillars, S),
    }));
    const yearBrief = yearWorkBrief(pillars, S, liveGz);
    const heshenBan = unique((det.vault || []).filter(v => {
      const hid = shiShen(det.day, v.stem);
      return hid === "正官" || hid === "七杀";
    }).map(v => `${v.stem}藏${v.branch}不透（${shiShen(det.day, v.stem)}），永不合身`));
    const pack = {
      ok: true,
      laws: ironLaws(),
      stars: stars,
      systems: S,
      hehua: hehua,
      xing: xing,
      backups: bak,
      vault: det.vault || [],
      sharedRoots: det.pairs || [],
      heshenBan: heshenBan,
      summary: summary,
      soul: "",
      yun: yun,
      live: live,
      yearBrief: yearBrief,
      day: det.day,
      pillars: pillars,
      freeze: opts.freeze || null,
      sex: opts.sex || opts.gender || (opts.sexNum === 0 ? "F" : "M"),
    };
    pack.taiji = taijiOf(pillars, pack.sex, pack.freeze);
    pack.soul = composeSoul(pack);
    pack.ledger = frameLedger(pack);
    pack.judge = judge(pack);
    return pack;
  }

  function yearWorkBrief(pillars, systems, liveGz) {
    const yun = (liveGz || []).find(x => x.label === "大运");
    const nian = (liveGz || []).find(x => x.label === "流年");
    if (!yun || !nian) return "";
    const yTags = luckDelta(yun.gz, pillars, systems);
    const nTags = luckDelta(nian.gz, pillars, systems);
    if (isAuthorNatal(pillars) && yun.gz === "丙辰" && nian.gz === "丙午") {
      return "丙辰运 × 2026丙午年：辰戌冲开，财坏印解除；辰水库对申中壬（大运对日 a=0）补水，链1生身加粗。天干丙火对时庚 a=1（食神制杀得权，50%）；流年午与时支午同字，链2制杀再加码，只削杀性、不截断链1。当年做功＝表面温和食神（技术／人脉）＋地下水库深谋（备份印气），双轨求生，惨胜转正。";
    }
    const bits = [];
    bits.push(yun.label + yun.gz + "：" + (yTags.length ? yTags.map(t => t.reason + "，" + t.text).join(" ") : "原局链维持。"));
    bits.push(nian.label + nian.gz + "：" + (nTags.length ? nTags.map(t => t.reason + "，" + t.text).join(" ") : "未引动截断／开库／制杀／合化。"));
    return bits.join(" ");
  }

  function composeSoul(xin) {
    if (isAuthorNatal(xin.pillars) && xin.systems.A && xin.systems.B && xin.systems.C) {
      return "心性为「孤臣孽子，绝处逢生」：表面依赖直觉与偏门学识（偏印），总被现实利益拖累（财坏印）；骨子里靠专业硬刚危机（伤官制杀），刚完必耗一层气血。大运冲开水库后，转为「表面温和食神，地下暗藏深谋」，靠技术人脉与幕后资源双轨求生。";
    }
    const A = xin.systems.A && xin.systems.A.ok !== false && xin.systems.A;
    const B = xin.systems.B;
    const C = xin.systems.C;
    const H = xin.hehua && xin.hehua.length;
    if (A && B && C) {
      return "心性要化杀成权，功用要制杀得权。两套系统同时成立、互相打架——孤臣孽子，权力与才华皆有，归宿难安。";
    }
    if (A && C) return "杀印相生与制杀得权并行。以柔克刚，亦凭才破局。";
    if (A && B) return "有情化杀，但财星截印。主电源不稳，先寻备份再言权。";
    if (A) return "有情化杀生身。以柔克刚，不硬刚外界压力。";
    if (C) return "制杀得权。凭才破局，杀气被压成权柄。";
    if (B) return "财坏印。名利来时学问与靠山先伤。";
    if (H) {
      const banN = xin.hehua.filter(function (h) { return h.mode === "ban"; }).length;
      const huaN = xin.hehua.length - banN;
      if (banN && !huaN) return "合而不化：日主被合绊，不聚化神，能量按普通克。正从不改。";
      if (banN && huaN) return "有合而化，亦有合而不化。化者守恒改道、按新五行做功；绊者按普通克、不聚化神。正从不改。";
      return "合化优先：日主被合，先按新五行做功，再论原十神心性。";
    }
    return "先立功用链，再谈心性喜忌。无链则不论隔位生克。";
  }

  /**
   * 断盘五步（样办 SOP）
   * 1 铁律  2 找链  3 心性≠功用  4 大运截/合/冲  5 流年做功
   * 禁：隔位生克、库中合身、制杀截断链1、不透之辛合身
   */
  function judge(xin) {
    const A = xin.systems && xin.systems.A;
    const B = xin.systems && xin.systems.B;
    const C = xin.systems && xin.systems.C;
    const day = xin.day;
    const lines = [];
    const tags = [];

    lines.push("【零、四步闭环】" + FOUR_STEP_LINE);
    lines.push("【零、天意】" + TIANYI_LINE);
    lines.push("【一、铁律】普通生克隔位归零；四冲五合可隔位遥感×E；库中不透永不合身；制杀只论权不截链；冲合即变；五合旺相才化，绊按普通克；四冲与五合同级互解。");

    if (A) {
      lines.push(`【二、链1】${A.chain}：有情化杀生身，${A.sha}不克身。`);
      tags.push("不克身", "化印");
    } else {
      lines.push("【二、链1】未见完整杀→根→印→身。勿用隔位天干硬凑生克。");
    }
    if (B) {
      lines.push(`【二、截】${B.chain}（财坏印），主电源被截。`);
      tags.push("财坏印");
    }
    if (xin.backups && xin.backups.length && A) {
      const bakZh = xin.backups.map(h => h.branch + "中" + A.yin + "（" + h.layer + "气）").join("、");
      lines.push(`【二、备】${bakZh}即刻激活为备用电源，持续供水。`);
      tags.push("备用电源");
    }
    if (C) {
      const sha = (A && A.sha) || "杀";
      const yin = (A && A.yin) || "印";
      lines.push(`【二、链2】${C.chain}：${C.root}承载${sha}杀气与${yin}印气。制杀得权，不截断链1。`);
      tags.push("不截断链1", "制杀得权");
    }
    (xin.hehua || []).forEach(h => {
      if (h.mode === "ban") {
        lines.push(`【二、合绊】${h.text}`);
        tags.push("合而不化", "合绊");
      } else {
        lines.push(`【二、合化】${h.text}`);
        tags.push("合化优先");
      }
    });
    (xin.xing || []).forEach(x => {
      lines.push(`【二、${x.kind}】${x.text}`);
    });

    const L = xin.ledger || frameLedger(xin);
    (L.lines || []).forEach(s => lines.push(s));
    if (L.overall) tags.push("做功" + L.overall);
    if (L.patternNames) L.patternNames.forEach(n => tags.push(n));

    lines.push(`【三、心性≠功用】${xin.soul || "心性按其十神气质显，功用只认进入传递链／合化者。"}`);
    (xin.heshenBan || []).forEach(h => {
      lines.push(`【三、禁】${h}。`);
      tags.push("永不合身");
    });

    if (xin.yun && xin.yun.length) {
      xin.yun.forEach(y => lines.push(`【四、岁运】第${y.i}步 ${y.gz} — ${y.line}`));
    } else if (xin.live && xin.live.some(x => x.label === "大运" && x.tags && x.tags.length)) {
      xin.live.filter(x => x.label === "大运").forEach(x => {
        lines.push(`【四、岁运】${x.gz} — ${x.tags.map(t => t.reason + " → " + t.text).join(" ")}`);
      });
    } else {
      lines.push("【四、岁运】只改实际用神力量阈值，不改其身份。大运对年日月 a=0、对时 a=1。小运对时日 a=0、对月 a=1、对年 a=2。流年 a=0→流时 a=3。用神合绊变质则补亦无用。");
    }

    if (xin.yearBrief) {
      lines.push(`【五、流年】${xin.yearBrief}`);
      if (/双轨/.test(xin.yearBrief)) tags.push("双轨求生", "表面食神", "地下深谋");
    } else {
      lines.push("【五、流年】流年干叠大运干则加倍；流年支与原局同字则该链加码。勿只看食伤/财官字面。");
    }

    return {
      ok: true,
      steps: lines,
      text: lines.join("\n"),
      soul: xin.soul,
      tags: unique(tags),
      bans: ["隔位生克", "库中合身", "制杀截断链1", "不透之辛合身"],
    };
  }

  function unique(arr) {
    return arr.filter((x, i) => arr.indexOf(x) === i);
  }

  function html(pack, t) {
    t = t || function (k) { return k; };
    if (!pack || !pack.ok) return "";
    function card(s) {
      const col = s.tone === "good" ? "var(--ok)" : (s.tone === "bad" ? "var(--hot)" : "var(--bronze)");
      return '<div class="xg-card" style="border-left:3px solid ' + col + ';padding:8px 10px;margin:8px 0">' +
        '<div style="font-weight:700;color:' + col + '">' + s.ch + " · " + term(s.ss) + "（" + s.seat + "）</div>" +
        '<div class="verdict" style="font-size:12.5px"><b>' + t("xinXing") + "</b>" + s.xinXing + "</div>" +
        '<div class="verdict" style="font-size:12.5px"><b>' + t("gongYong") + "</b>" + s.gongYong + "</div>" +
        (s.xian ? '<div class="verdict" style="font-size:12.5px"><b>' + t("xianHua") + "</b>" + s.xian + "</div>" : "") +
        '<div class="verdict" style="font-size:12.5px;color:' + col + '"><b>' + t("gongJie") + "</b>" + s.verdict + "</div>" +
        "</div>";
    }
    const law = '<div class="verdict" style="font-size:12px;color:var(--mist)">' +
      pack.laws.map(function (l, i) { return (i + 1) + ". " + l.zh; }).join("<br>") + "</div>";
    const sop = (pack.judge && pack.judge.steps && pack.judge.steps.length)
      ? '<div class="verdict" style="font-weight:700;margin-top:8px;color:var(--jade)">' + t("sop") + "</div>" +
        '<div class="verdict" style="font-size:12.5px;white-space:pre-wrap">' + pack.judge.steps.join("<br>") + "</div>"
      : "";
    const sys = pack.summary ? '<div class="verdict">' + pack.summary + "</div>" : "";
    const soul = pack.soul ? '<div class="verdict" style="color:var(--bronze)">' + pack.soul + "</div>" : "";
    const vault = (pack.heshenBan && pack.heshenBan.length)
      ? '<div class="verdict" style="color:var(--hot)">' + t("vaultBan") + pack.heshenBan.join("；") + "</div>" : "";
    const yun = (pack.yun && pack.yun.length)
      ? pack.yun.map(function (y) {
        return '<div class="verdict" style="font-size:12.5px"><b>' + t("yunStep") + y.i + " · " + y.gz +
          (y.years ? "（" + y.years + "）" : "") + (y.ages ? " " + y.ages : "") + "</b> — " + y.line + "</div>";
      }).join("")
      : "";
    const liveHits = (pack.live || []).filter(function (x) { return x.tags && x.tags.length; });
    const live = liveHits.length
      ? liveHits.map(function (x) {
        return '<div class="verdict" style="font-size:12.5px;color:var(--gold)"><b>' + x.label + x.gz + "</b> · " +
          x.tags.map(function (tg) { return tg.reason + " → " + tg.text; }).join(" ");
      }).join("")
      : "";
    const brief = pack.yearBrief
      ? '<div class="verdict" style="font-weight:700;margin-top:8px;color:var(--jade)">' + t("yearBrief") + "</div>" +
        '<div class="verdict">' + pack.yearBrief + "</div>"
      : "";
    const tj = pack.taiji;
    function lab(k, fb) { const v = t(k); return (v && v !== k) ? v : fb; }
    const taijiBlk = (tj && tj.ok)
      ? '<div class="verdict" style="font-weight:700;margin-top:8px;color:var(--jade)">' + lab("taiji", "太极六亲") + "</div>" +
        '<div class="verdict" style="font-size:12px;color:var(--mist)">' + tj.line + "</div>" +
        (tj.rows || []).map(function (r) {
          const o = r.origin;
          const head = r.name + " · " + (r.starZh || (o ? r.ss + o.ch + "（" + o.seat + "·" + o.source + "）" : r.ss));
          return '<div class="xg-card" style="border-left:3px solid var(--bronze);padding:8px 10px;margin:8px 0">' +
            '<div style="font-weight:700;color:var(--bronze)">' + head + "</div>" +
            (r.jingZh ? '<div class="verdict" style="font-size:12.5px"><b>' + lab("taijiJing", "环境：") + "</b>" + r.jingZh + "</div>" : "") +
            (r.xiaoZh ? '<div class="verdict" style="font-size:12.5px"><b>' + lab("taijiXiao", "消长：") + "</b>" + r.xiaoZh + "</div>" : "") +
            (r.dayAs ? '<div class="verdict" style="font-size:12px;color:var(--mist)">原日主在其环境为' + r.dayAs + (r.note ? " · " + r.note : "") + "</div>" : "") +
            "</div>";
        }).join("")
      : "";
    return law + vault + sop + pack.stars.map(card).join("") + sys + soul +
      (yun ? '<div class="verdict" style="font-weight:700;margin-top:8px">' + t("yunCuts") + "</div>" + yun : "") +
      (live ? '<div class="verdict" style="font-weight:700;margin-top:8px">' + t("liveCuts") + "</div>" + live : "") +
      brief + taijiBlk;
  }

  global.KRXinGong = { analyze, html, judge, composeSoul, frameLedger, shiShen, luckDelta, ironLaws, yearWorkBrief, natalHehua, isAuthorNatal, hehuaCritOf, tagSeat, tagCharRows, domainOfKey, taijiOf, pickOrigin, kinTable, TAIJI_LINE, TIANYI_LINE, XIN_XING, YUN_SCRIPT, FOUR_STEP, FOUR_STEP_LINE, CHAR_HOUR_LINE };
})(typeof window !== "undefined" ? window : globalThis);
