/**
 * 键共振 Destiny · 问真框架新站
 * 排盘：lunar.js（北半球对齐问真 vip=0 序）
 * 模型：KRParams 2026-08-11
 */
(function () {
  "use strict";

  const YANG = "甲丙戊庚壬";
  const WX_GAN = {甲:"木",乙:"木",丙:"火",丁:"火",戊:"土",己:"土",庚:"金",辛:"金",壬:"水",癸:"水"};
  const WX_ZHI = {子:"水",丑:"土",寅:"木",卯:"木",辰:"土",巳:"火",午:"火",未:"土",申:"金",酉:"金",戌:"土",亥:"水"};
  const LIU_CHONG = {子:"午",午:"子",丑:"未",未:"丑",寅:"申",申:"寅",卯:"酉",酉:"卯",辰:"戌",戌:"辰",巳:"亥",亥:"巳"};
  const LIU_HE = {子:"丑",丑:"子",寅:"亥",亥:"寅",卯:"戌",戌:"卯",辰:"酉",酉:"辰",巳:"申",申:"巳",午:"未",未:"午"};
  const PLACE_LABEL = {
    chicago:"芝加哥", beijing:"北京", shanghai:"上海", taipei:"台北",
    hongkong:"香港", newyork:"纽约", london:"伦敦", sydney:"悉尼",
    la:"洛杉矶", rio:"里约热内卢", sao_paulo:"圣保罗"
  };

  const state = {
    mode: "solar",
    chart: null,
    analysis: null,
    daYunList: [],
    daYunIndex: 0,
  };

  function $(id) { return document.getElementById(id); }
  function P() { return window.KRParams || {}; }

  function yinYangClass(ch) {
    return YANG.includes(ch) ? "yang" : "yin";
  }

  function parseGzLine(text) {
    const cleaned = (text || "").replace(/[年月日時时柱：:\s,，、|/]+/g, " ").trim();
    const parts = cleaned.match(/[甲乙丙丁戊己庚辛壬癸][子丑寅卯辰巳午未申酉戌亥]/g);
    if (!parts || parts.length < 4) throw new Error("请输入完整四柱，如：戊子 癸亥 丁丑 辛丑");
    return { year: parts[0], month: parts[1], day: parts[2], time: parts[3] };
  }

  function hourFromZhiIndex(zi) {
    // 子=23或0 → 用 0; 丑=1 → 1; 寅=3 ...
    const map = [0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21];
    return map[zi] ?? 12;
  }

  function syncPlaceFields(fromBirth) {
    const E = window.KREngine;
    if (!E) return;
    const id = fromBirth ? $("birthPlace").value : $("standPlace").value;
    const p = E.PLACES[id];
    if (!p) return;
    if (fromBirth) {
      $("birthLat").value = p.lat;
      $("birthLon").value = p.lng;
      $("zoneHours").value = p.zone;
      if ($("syncPlace").checked) {
        $("standPlace").value = id;
        $("standLat").value = p.lat;
        $("standLon").value = p.lng;
        if ($("standZone")) $("standZone").value = p.zone;
      }
    } else {
      $("standLat").value = p.lat;
      $("standLon").value = p.lng;
      if ($("standZone")) $("standZone").value = p.zone;
    }
    updateGeoHint();
    setFlowNow();
  }

  function updateGeoHint() {
    const E = window.KREngine;
    if (!E || !$("geoHint")) return;
    const blat = +$("birthLat").value, blon = +$("birthLon").value;
    const slat = +$("standLat").value, slon = +$("standLon").value;
    const fwB = E.geoFireWater(blat);
    const fwS = E.geoFireWater(slat);
    const mwB = E.geoMetalWoodFromBeijing(blon);
    const mwS = E.geoMetalWoodFromBeijing(slon);
    $("geoHint").textContent =
      `出生 水${fwB.water.toFixed(2)}/火${fwB.fire.toFixed(2)} · 金${mwB.metal.toFixed(2)}/木${mwB.wood.toFixed(2)}` +
      ` ｜ 现地 水${fwS.water.toFixed(2)}/火${fwS.fire.toFixed(2)} · 金${mwS.metal.toFixed(2)}/木${mwS.wood.toFixed(2)}` +
      ` ｜ 中轴120°E·大西洋日界线−60°`;
  }

  function setFlowNow() {
    const alias = { la: "losangeles", sao_paulo: "saopaulo" };
    const rawId = $("standPlace") ? $("standPlace").value : "";
    const cid = alias[rawId] || rawId;
    if (window.KRCities && KRCities.nowAtCity && KRCities.byId && KRCities.byId[cid]) {
      const n = KRCities.nowAtCity(cid);
      if ($("fy")) $("fy").value = n.y;
      if ($("fm")) $("fm").value = n.mo;
      if ($("fd")) $("fd").value = n.d;
      if ($("fh")) $("fh").value = n.h;
      return;
    }
    const zone = +($("standZone") ? $("standZone").value : $("zoneHours").value);
    const now = new Date();
    const utc = now.getTime() + now.getTimezoneOffset() * 60000;
    const local = new Date(utc + zone * 3600000);
    if ($("fy")) $("fy").value = local.getFullYear();
    if ($("fm")) $("fm").value = local.getMonth() + 1;
    if ($("fd")) $("fd").value = local.getDate();
    if ($("fh")) $("fh").value = local.getHours();
  }

  /** 现地流层四柱：公历／农历用现地当地日；干支再经美洲日+1／南月支冲 */
  function buildFlowPillars() {
    if (typeof Solar === "undefined") return { pillars: null, timeInfo: null, hour: 12, minute: 0 };
    const E = window.KREngine;
    const y = +$("fy").value, m = +$("fm").value, d = +$("fd").value, h = +$("fh").value;
    const lon = +$("standLon").value;
    const zone = +($("standZone") ? $("standZone").value : $("zoneHours").value);
    const useTS = $("trueSolar").checked;
    const timeInfo = E
      ? E.correctTime({ y, m, d, h, mi: 0 }, lon, zone, useTS, false)
      : null;
    // 公历／农历：现地当地日不变（不用日+1改历）
    let sh = h;
    if (timeInfo && useTS && !timeInfo.dayAhead) sh = timeInfo.trueSolar.getHours();
    try {
      const solar = Solar.fromYmdHms(y, m, d, sh, 0, 0);
      const lunar = solar.getLunar();
      const ec = lunar.getEightChar();
      return {
        pillars: {
          year: ec.getYear(),
          month: ec.getMonth(),
          day: ec.getDay(),
          time: ec.getTime(),
        },
        timeInfo,
        hour: h,
        minute: 0,
        solarStr: `${y}-${String(m).padStart(2,"0")}-${String(d).padStart(2,"0")} ${String(h).padStart(2,"0")}:00:00`,
        lunarStr: lunar.toString(),
      };
    } catch (e) {
      console.warn("流层排盘失败", e);
      return { pillars: null, timeInfo, hour: h, minute: 0, solarStr: "", lunarStr: "" };
    }
  }

  function runAnalysis(pillars, timeInfo, localClock) {
    const E = window.KREngine;
    if (!E) return null;
    const flow = buildFlowPillars();
    return E.analyze({
      pillars,
      birthLat: +$("birthLat").value,
      birthLon: +$("birthLon").value,
      standLat: +$("standLat").value,
      standLon: +$("standLon").value,
      timeInfo,
      localHour: localClock ? localClock.hour : 12,
      localMinute: localClock ? localClock.minute : 0,
      flowPillars: flow.pillars,
      flowTimeInfo: flow.timeInfo,
      flowHour: flow.hour,
      flowMinute: flow.minute,
      params: window.KRParams,
    });
  }

  function buildFromSolar(y, m, d, h, mi) {
    const E = window.KREngine;
    const useTS = $("trueSolar").checked;
    const bjClock = $("beijingClock").checked;
    const zone = +$("zoneHours").value;
    const lon = +$("birthLon").value;
    const timeInfo = E
      ? E.correctTime({ y:+y, m:+m, d:+d, h:+h, mi:+mi }, lon, zone, useTS, bjClock)
      : null;

    // 公历／农历：始终用当地输入日（美洲进一不改历日）
    // 真太阳时只调时分用于年/月令；美洲区时支仍取当地钟点
    let sh = +h, smi = +mi;
    if (timeInfo && useTS && !(timeInfo.dayAhead)) {
      sh = timeInfo.trueSolar.getHours();
      smi = timeInfo.trueSolar.getMinutes();
    }

    const solar = Solar.fromYmdHms(+y, +m, +d, sh, smi, 0);
    const lunar = solar.getLunar();
    const ec = lunar.getEightChar();
    // 显示用当地墙钟串，避免时分被真太阳改写造成「历日变了」的错觉
    const displaySolar = Solar.fromYmdHms(+y, +m, +d, +h, +mi, 0);
    return packChart({
      solar: displaySolar,
      lunar: displaySolar.getLunar(),
      ec,
      source: useTS ? "公历·当地历日·真太阳参令" : "公历·当地民事",
      timeInfo,
      localClock: { hour: +h, minute: +mi },
    });
  }

  function buildFromLunar(y, m, d, hourZhi, leap) {
    const month = leap ? -Math.abs(+m) : +m;
    const h = hourFromZhiIndex(+hourZhi);
    const lunar = Lunar.fromYmdHms(+y, month, +d, h, 0, 0);
    const solar = lunar.getSolar();
    const ec = lunar.getEightChar();
    return packChart({ solar, lunar, ec, source: "农历·lunar.js" });
  }

  function buildFromGz(gz) {
    // 干支直排：无精确公历时，仅展示四柱；大运用五虎遁近似跳过起运
    const fake = {
      getYear: () => gz.year,
      getMonth: () => gz.month,
      getDay: () => gz.day,
      getTime: () => gz.time,
      getYearGan: () => gz.year[0], getYearZhi: () => gz.year[1],
      getMonthGan: () => gz.month[0], getMonthZhi: () => gz.month[1],
      getDayGan: () => gz.day[0], getDayZhi: () => gz.day[1],
      getTimeGan: () => gz.time[0], getTimeZhi: () => gz.time[1],
      getYearHideGan: () => (window.LunarUtil && LunarUtil.ZHI_HIDE_GAN[gz.year[1]]) || [],
      getMonthHideGan: () => (window.LunarUtil && LunarUtil.ZHI_HIDE_GAN[gz.month[1]]) || [],
      getDayHideGan: () => (window.LunarUtil && LunarUtil.ZHI_HIDE_GAN[gz.day[1]]) || [],
      getTimeHideGan: () => (window.LunarUtil && LunarUtil.ZHI_HIDE_GAN[gz.time[1]]) || [],
      getYearShiShenGan: () => shiShen(gz.day[0], gz.year[0]),
      getMonthShiShenGan: () => shiShen(gz.day[0], gz.month[0]),
      getDayShiShenGan: () => "日主",
      getTimeShiShenGan: () => shiShen(gz.day[0], gz.time[0]),
      getYearNaYin: () => (window.LunarUtil && LunarUtil.NAYIN[gz.year]) || "—",
      getMonthNaYin: () => (window.LunarUtil && LunarUtil.NAYIN[gz.month]) || "—",
      getDayNaYin: () => (window.LunarUtil && LunarUtil.NAYIN[gz.day]) || "—",
      getTimeNaYin: () => (window.LunarUtil && LunarUtil.NAYIN[gz.time]) || "—",
      getYearDiShi: () => "—", getMonthDiShi: () => "—", getDayDiShi: () => "—", getTimeDiShi: () => "—",
      getYearXunKong: () => "—", getMonthXunKong: () => "—", getDayXunKong: () => "—", getTimeXunKong: () => "—",
      getYun: null,
    };
    return packChart({ solar: null, lunar: null, ec: fake, source: "干支直排", gzOnly: true, gz });
  }

  function shiShen(dayGan, otherGan) {
    if (window.LunarUtil && LunarUtil.SHI_SHEN) {
      return LunarUtil.SHI_SHEN[dayGan + otherGan] || "—";
    }
    return "—";
  }

  function packChart({ solar, lunar, ec, source, gzOnly, gz, timeInfo, localClock }) {
    const sex = +$("sex").value;
    const name = ($("name").value || "未名").trim();
    const birth = $("birthPlace").value;
    const stand = $("standPlace").value;
    let pillars = {
      year: ec.getYear(),
      month: ec.getMonth(),
      day: ec.getDay(),
      time: ec.getTime(),
    };

    const clock = localClock || {
      hour: solar ? solar.getHour() : 12,
      minute: solar ? solar.getMinute() : 0,
    };
    // 引擎：美洲日干支+1（历日不变）／南半球月支对冲 + 正从／BI／断语
    const analysis = runAnalysis(pillars, timeInfo || null, clock);
    if (analysis) {
      pillars = analysis.pillars;
      state.analysis = analysis;
    } else {
      state.analysis = null;
    }

    let yunMeta = null;
    let daYun = [];
    if (!gzOnly && ec.getYun) {
      try {
        const yun = ec.getYun(sex, 1);
        yunMeta = {
          forward: yun.isForward ? yun.isForward() : true,
          startYear: yun.getStartYear ? yun.getStartYear() : 0,
          startMonth: yun.getStartMonth ? yun.getStartMonth() : 0,
          startDay: yun.getStartDay ? yun.getStartDay() : 0,
        };
        const list = yun.getDaYun ? yun.getDaYun(10) : [];
        daYun = list.map((dy, i) => ({
          index: i,
          startAge: dy.getStartAge ? dy.getStartAge() : i * 10,
          endAge: dy.getEndAge ? dy.getEndAge() : i * 10 + 9,
          startYear: dy.getStartYear ? dy.getStartYear() : 0,
          ganZhi: dy.getGanZhi ? dy.getGanZhi() : "—",
        })).filter(d => d.index === 0 || (d.ganZhi && d.ganZhi !== "—"));
      } catch (e) {
        console.warn("大运计算失败", e);
      }
    } else {
      daYun = approxDaYun(pillars.month, sex);
    }

    return {
      name, sex, birth, stand, source, pillars, ec, solar, lunar, yunMeta, daYun, gzOnly: !!gzOnly,
      solarStr: solar ? solar.toYmdHms() : "（干支直排）",
      lunarStr: lunar ? lunar.toString() : "—",
      timeInfo: timeInfo || null,
      analysis,
    };
  }

  function approxDaYun(monthGz, sex) {
    const jiazi = [];
    const TG = "甲乙丙丁戊己庚辛壬癸";
    const DZ = "子丑寅卯辰巳午未申酉戌亥";
    for (let i = 0; i < 60; i++) jiazi.push(TG[i % 10] + DZ[i % 12]);
    let idx = jiazi.indexOf(monthGz);
    if (idx < 0) idx = 0;
    const forward = sex === 1;
    const out = [];
    for (let i = 0; i < 8; i++) {
      const j = forward ? (idx + 1 + i) % 60 : (idx - 1 - i + 600) % 60;
      out.push({ index: i + 1, startAge: 1 + i * 10, endAge: 10 + i * 10, startYear: 0, ganZhi: jiazi[j] });
    }
    return out;
  }

  function setMode(mode) {
    state.mode = mode;
    document.querySelectorAll("#modeBar button").forEach(b => {
      b.classList.toggle("on", b.dataset.mode === mode);
    });
    $("formSolar").style.display = mode === "solar" ? "" : "none";
    $("formLunar").style.display = mode === "lunar" ? "" : "none";
    $("formGz").style.display = mode === "gz" ? "" : "none";
  }

  function setTab(tab) {
    // 问真主页签映射
    const mainMap = { info: "info", ji: "basic", zhuan: "pro", jian: "pro", params: "pro", teach: "teach" };
    const main = mainMap[tab] || "info";
    document.querySelectorAll("#wzMainTabs button").forEach(b => {
      b.classList.toggle("active", b.dataset.main === main);
    });

    document.querySelectorAll("#tabs button").forEach(b => {
      b.classList.toggle("active", b.dataset.tab === tab);
    });
    document.querySelectorAll(".wz-panels > .panel").forEach(p => p.classList.remove("active"));
    // 键/参镶在专业细盘内
    if (tab === "jian" || tab === "params") {
      const pro = $("p-zhuan");
      if (pro) pro.classList.add("active");
      setProSub(tab === "params" ? "params" : "jian");
    } else if (tab === "ji") {
      $("p-ji").classList.add("active");
    } else if (tab === "zhuan") {
      $("p-zhuan").classList.add("active");
      setProSub("fine");
    } else if (tab === "teach") {
      $("p-teach").classList.add("active");
    } else {
      $("p-info").classList.add("active");
    }

    document.querySelectorAll(".bottom a").forEach(a => a.classList.remove("on"));
    const map = { info: "navInfo", ji: "navJi", zhuan: "navZh", jian: "navJn" };
    if (map[tab] && $(map[tab])) $(map[tab]).classList.add("on");
    else if (tab === "info") $("navInfo").classList.add("on");
  }

  function setProSub(sub) {
    document.querySelectorAll("#proSubTabs button").forEach(b => {
      b.classList.toggle("on", b.dataset.sub === sub);
    });
    const fine = $("pro-fine"), jian = $("pro-jian"), params = $("pro-params");
    if (fine) fine.hidden = sub !== "fine";
    if (jian) jian.hidden = sub !== "jian";
    if (params) params.hidden = sub !== "params";
  }

  function setMainTab(main) {
    if (main === "basic") setTab("ji");
    else if (main === "pro") setTab("zhuan");
    else if (main === "teach") setTab("teach");
    else setTab("info");
  }

  function msg(text, ok) {
    $("msg").innerHTML = text
      ? `<span class="${ok ? "ok" : "err"}">${text}</span>`
      : "";
  }

  function renderPillars(chart) {
    const ec = chart.ec;
    const E = window.KREngine;
    const hideOf = (br) => {
      if (E && E.PLACES) {
        // use engine HIDE via analyze path — fallback LunarUtil
      }
      if (window.LunarUtil && LunarUtil.ZHI_HIDE_GAN) return LunarUtil.ZHI_HIDE_GAN[br] || [];
      return [];
    };
    const cols = [
      { lab: "年", gz: chart.pillars.year, ss: ec.getYearShiShenGan(), hide: hideOf(chart.pillars.year[1]),
        nayin: ec.getYearNaYin(), dishi: ec.getYearDiShi(), xunkong: ec.getYearXunKong && ec.getYearXunKong() },
      { lab: "月", gz: chart.pillars.month, ss: chart.pillars.month[0] === ec.getMonthGan() ? ec.getMonthShiShenGan() : shiShen(chart.pillars.day[0], chart.pillars.month[0]),
        hide: hideOf(chart.pillars.month[1]),
        nayin: (window.LunarUtil && LunarUtil.NAYIN[chart.pillars.month]) || ec.getMonthNaYin(),
        dishi: ec.getMonthDiShi(), xunkong: ec.getMonthXunKong && ec.getMonthXunKong() },
      { lab: "日", gz: chart.pillars.day, ss: ec.getDayShiShenGan(), hide: hideOf(chart.pillars.day[1]),
        nayin: ec.getDayNaYin(), dishi: ec.getDayDiShi(), xunkong: ec.getDayXunKong && ec.getDayXunKong() },
      { lab: "时", gz: chart.pillars.time, ss: ec.getTimeShiShenGan(), hide: hideOf(chart.pillars.time[1]),
        nayin: ec.getTimeNaYin(), dishi: ec.getTimeDiShi(), xunkong: ec.getTimeXunKong && ec.getTimeXunKong() },
    ];

    // 兼容隐藏 pillars 节点
    const pillarsEl = $("pillars");
    if (pillarsEl) {
      pillarsEl.innerHTML = cols.map(c => {
        const hide = Array.isArray(c.hide) ? c.hide.join(" ") : (c.hide || "");
        return `<div class="pillar">
          <div class="lab">${c.lab}</div>
          <div class="stem ${yinYangClass(c.gz[0])}">${c.gz[0]}</div>
          <div class="branch ${yinYangClass(c.gz[1])}">${c.gz[1]}</div>
          <div class="god">${c.ss || "—"}</div>
          <div class="hide">${hide || "—"}</div>
        </div>`;
      }).join("");
    }

    // 问真式主表
    const body = $("wzChartBody");
    if (body) {
      const hideCell = (h) => Array.isArray(h) ? h.join(" ") : (h || "—");
      const xk = cols.map(c => c.xunkong || "").filter(Boolean);
      const xkText = [...new Set(xk)].join(" · ") || "—";
      body.innerHTML = `
        <tr><th>十神</th>${cols.map(c => `<td>${c.ss || "—"}</td>`).join("")}</tr>
        <tr class="gz-row"><th>天干</th>${cols.map(c => `<td class="${yinYangClass(c.gz[0])}">${c.gz[0]}</td>`).join("")}</tr>
        <tr class="gz-row"><th>地支</th>${cols.map(c => `<td class="${yinYangClass(c.gz[1])}">${c.gz[1]}</td>`).join("")}</tr>
        <tr><th>藏干</th>${cols.map(c => `<td>${hideCell(c.hide)}</td>`).join("")}</tr>
        <tr><th>纳音</th>${cols.map(c => `<td>${c.nayin || "—"}</td>`).join("")}</tr>
        <tr><th>星运</th>${cols.map(c => `<td>${c.dishi || "—"}</td>`).join("")}</tr>
        <tr><th>空亡</th><td colspan="4">${xkText}</td></tr>
      `;
    }

    const dayGan = chart.pillars.day[0];
    const A = chart.analysis;
    const flags = [];
    if (A && A.south) flags.push("南半球月支对冲");
    if (A && A.dayAhead) flags.push("美洲日柱+1");
    if (A && A.zhengDiHou) flags.push(A.zhengDiHou.summary.split(" · ")[0]);
    $("metaLine").innerHTML = `
      <span>名号 <b>${chart.name}</b></span>
      <span>性别 <b>${chart.sex === 1 ? "乾造" : "坤造"}</b></span>
      <span>日主 <b>${dayGan}${WX_GAN[dayGan] || ""}</b></span>
      <span>月令 <b>${chart.pillars.month[1]}</b></span>
      <span>源 <b>${chart.source}</b></span>
      ${flags.length ? `<span>旗标 <b>${flags.join(" · ")}</b></span>` : ""}`;

    const dys = chart.daYun || [];
    state.daYunList = dys;
    $("luckBar").innerHTML =
      `<span class="footnote" style="flex:0 0 100%;margin:0 0 4px">大运／小运 · 与出生地同步（+出生水火金木）</span>` +
      (dys.map((d, i) =>
        `<button type="button" data-i="${i}" class="${i === state.daYunIndex ? "on" : ""}">
          <span class="y">${d.startAge}–${d.endAge}岁</span>${d.ganZhi}
        </button>`
      ).join("") || `<span class="footnote">大运未起</span>`);

    $("luckBar").querySelectorAll("button").forEach(btn => {
      btn.addEventListener("click", () => {
        state.daYunIndex = +btn.dataset.i;
        renderPillars(chart);
        renderPanels(chart);
      });
    });

    // 现地流层条
    const flowEl = $("flowBar");
    if (flowEl && A && A.layers && A.layers.stand) {
      const S = A.layers.stand;
      const fp = S.pillars;
      const fw = S.fireWater, mw = S.metalWood;
      const adj = (S.adjustNotes || []).join("；");
      flowEl.innerHTML = `
        <div><b>现地流层</b>（现地公历／农历不变
          ${S.dayAhead ? " · 流日干支+1" : ""}
          ${S.southNote ? " · 流月支对冲" : ""}）
          · 水 ${fw.water.toFixed(3)} / 火 ${fw.fire.toFixed(3)}
          · 金 ${mw.metal.toFixed(3)} / 木 ${mw.wood.toFixed(3)}
        </div>
        ${adj ? `<div class="footnote">${adj}</div>` : ""}
        ${fp ? `<div class="flow-pillars">
          <div class="fp"><div class="lab">流年</div><div class="gz">${fp.year}</div></div>
          <div class="fp"><div class="lab">流月</div><div class="gz">${fp.month}</div></div>
          <div class="fp"><div class="lab">流日</div><div class="gz">${fp.day}</div></div>
          <div class="fp"><div class="lab">流时</div><div class="gz">${fp.time}</div></div>
        </div>` : `<p class="footnote">流层未排（请填现地流层公历）</p>`}`;
    } else if (flowEl) {
      flowEl.innerHTML = "";
    }
  }

  function barHtml(label, value, max) {
    const pct = Math.max(6, Math.min(100, Math.round((value / max) * 100)));
    return `<div class="h-line"><span>${label}</span><span class="n">${value.toFixed(3)}</span></div>
      <div class="bar"><i style="width:${pct}%"></i></div>`;
  }

  function scanRelations(pillars) {
    const labels = ["年", "月", "日", "时"];
    const z = [pillars.year[1], pillars.month[1], pillars.day[1], pillars.time[1]];
    const hits = [];
    const sk = P().shengKe || {};
    for (let i = 0; i < 4; i++) {
      for (let j = i + 1; j < 4; j++) {
        if (LIU_CHONG[z[i]] === z[j]) {
          hits.push(`${labels[i]}${z[i]} 冲 ${labels[j]}${z[j]}（A${sk.chong?.a} / B${sk.chong?.b}）`);
        }
        if (LIU_HE[z[i]] === z[j]) {
          hits.push(`${labels[i]}${z[i]} 合 ${labels[j]}${z[j]}（A${sk.he?.a} / B${sk.he?.b} / C${sk.he?.c}）`);
        }
      }
    }
    return hits;
  }

  function schematicEnergies(chart) {
    // 示意／非完整引擎。天干以日主为中心 年3/4／月7/8／日1.0／时3/4；地支以月令为中心 年3/4／月1.0／日7/8／时3/4。大运小运无令地，只叠能。
    const p = P();
    const yun = p.yun || { daYun: 1, xiaoYun: 0.125 };
    const base = {
      年干: 0.75, 月干: 0.875, 日干: 1.0, 时干: 0.75,
      年支: 0.75, 月支: 1.0, 日支: 0.875, 时支: 0.75,
    };
    const dy = state.daYunList[state.daYunIndex];
    if (dy) {
      base["大运干"] = 1.0 * yun.daYun;
      base["大运支"] = 1.0 * yun.daYun;
      base["小运示意"] = 1.0 * yun.xiaoYun;
    }
    return base;
  }

  function renderPanels(chart) {
    const p = P();
    const A = chart.analysis;
    const birth = PLACE_LABEL[chart.birth] || chart.birth;
    const stand = PLACE_LABEL[chart.stand] || chart.stand;
    const dayGan = chart.pillars.day[0];
    const rel = scanRelations(chart.pillars);
    const dy = state.daYunList[state.daYunIndex];

    const ti = chart.timeInfo;
    $("infoBirth").innerHTML = `<div class="kv">
      <div class="k">公历</div><div>${chart.solarStr}</div>
      <div class="k">农历</div><div>${chart.lunarStr}</div>
      <div class="k">出生地</div><div>${birth} · 纬 ${(+($("birthLat").value)).toFixed(4)} · 经 ${(+($("birthLon").value)).toFixed(4)}</div>
      <div class="k">现地</div><div>${stand} · 纬 ${(+($("standLat").value)).toFixed(4)} · 经 ${(+($("standLon").value)).toFixed(4)}</div>
      <div class="k">真太阳时</div><div>${$("trueSolar").checked ? "开" : "关"}${$("beijingClock").checked ? " · 输入＝北京钟表" : ""}</div>
      <div class="k">北京民事</div><div>${ti ? ti.beijingCivil.toLocaleString() : "—"}</div>
      <div class="k">真太阳</div><div>${ti ? ti.trueSolar.toLocaleString() : "—"}</div>
      <div class="k">日干支+1</div><div>${A && A.dayAhead ? "是（美洲区：公历／农历不变，仅日柱进一；时干五鼠遁）" : "否"}</div>
      <div class="k">月支对冲</div><div>${A && A.south ? (A.southNote || "南半球·月干不变") : "否"}</div>
      <div class="k">干支校正</div><div>${A && A.geoAdjustNotes && A.geoAdjustNotes.length ? A.geoAdjustNotes.join("；") : "无"}</div>
      <div class="k">起运</div><div>${chart.yunMeta
        ? `${chart.yunMeta.startYear}年${chart.yunMeta.startMonth}月${chart.yunMeta.startDay}日 · ${chart.yunMeta.forward ? "顺排" : "逆排"}`
        : "干支直排示意／未精确起运"}</div>
    </div>`;

    const ec = chart.ec;
    $("infoPillars").innerHTML = `<div class="kv">
      <div class="k">年柱</div><div>${chart.pillars.year} · ${ec.getYearShiShenGan()} · 纳音 ${ec.getYearNaYin()} · 长生 ${ec.getYearDiShi()}</div>
      <div class="k">月柱</div><div>${chart.pillars.month} · ${ec.getMonthShiShenGan()} · 纳音 ${ec.getMonthNaYin()} · 长生 ${ec.getMonthDiShi()}${A && A.south ? "（已对冲）" : ""}</div>
      <div class="k">日柱</div><div>${chart.pillars.day} · 日主 · 纳音 ${ec.getDayNaYin()} · 长生 ${ec.getDayDiShi()}</div>
      <div class="k">时柱</div><div>${chart.pillars.time} · ${ec.getTimeShiShenGan()} · 纳音 ${ec.getTimeNaYin()} · 长生 ${ec.getTimeDiShi()}</div>
    </div>
    <p class="footnote">${A ? A.flowNote : ""}</p>`;

    // —— 基：正从用忌 ——
    if (A && A.zhengDiHou) {
      const z = A.zhengDiHou;
      $("jiStruct").innerHTML = `<div class="kv">
        <div class="k">日主</div><div>${dayGan}（${WX_GAN[dayGan]}）</div>
        <div class="k">月令</div><div>${chart.pillars.month[1]}（${WX_ZHI[chart.pillars.month[1]]}）</div>
        <div class="k">正从</div><div>${z.pattern} · ${z.strength}</div>
        <div class="k">取向</div><div>${z.orient}${z.yongJiSoft ? "（用忌轻 O8）" : ""}</div>
        <div class="k">体客</div><div>我 ${z.wo.toFixed(3)} ∶ 客 ${z.ta.toFixed(3)}（地后冻结）</div>
        <div class="k">用神</div><div>${z.yongShen.join("、")}</div>
        <div class="k">忌神</div><div>${z.jiShen.join("、")}</div>
        <div class="k">地有效</div><div>令后 × ${p.diRelativeToLing}</div>
      </div>
      <div class="chiprow">
        <span class="chip ok">${z.pattern}</span>
        <span class="chip">${z.orient}</span>
        ${z.yongJiSoft ? '<span class="chip">用忌轻</span>' : '<span class="chip hot">用忌重</span>'}
      </div>`;
      $("jiVerdict").innerHTML = `<div class="verdict"><b>初断：</b>${z.summary}<br/>${(z.notes || []).slice(0,3).map(n => "· "+n).join("<br/>")}</div>
        <div class="verdict" style="margin-top:8px"><b>断语库（配对）：</b><br/>${(A.verdicts.lines || []).map(l => "· "+l).join("<br/>")}</div>`;
    }

    // —— 专：量度 / 做功 ——
    $("zhParam").innerHTML = `<p>${p.oneLinerZh || ""}</p>
      <p class="footnote">冻结 ${p.version} · 流程：${A ? A.flowNote : ""}</p>`;

    if (A) {
      const el = A.energy.diHou;
      const B = A.layers.birth;
      const S = A.layers.stand;
      const fw = B.fireWater;
      const mwB = B.metalWood;
      const sf = S.fireWater;
      const mwS = S.metalWood;
      $("zhBars").innerHTML =
        Object.entries(el).map(([k, v]) => barHtml("原局"+k, v, 3)).join("") +
        barHtml("出生·火", fw.fire, 1) + barHtml("出生·水", fw.water, 1) +
        barHtml("出生·金", mwB.metal, 1) + barHtml("出生·木", mwB.wood, 1) +
        barHtml("现地·火", sf.fire, 1) + barHtml("现地·水", sf.water, 1) +
        barHtml("现地·金", mwS.metal, 1) + barHtml("现地·木", mwS.wood, 1);

      $("zhGeo").innerHTML = `<div class="kv">
        <div class="k">双锚</div><div>出生→原局/大小运+水火金木；现地→流年日月时+水火金木</div>
        <div class="k">水火式</div><div>|纬|/90→水，火=1−水（±90°水=1，赤道水=0；南纬为负）</div>
        <div class="k">金木式</div><div>北京${A.geo.beijingMeridian}°E为中心；大西洋日界线≈−60°；以东 0&lt;木≤1，以西 0&lt;金≤1</div>
        <div class="k">出生水火</div><div>纬 ${B.lat.toFixed(4)}° → 水 ${fw.water.toFixed(4)} / 火 ${fw.fire.toFixed(4)}</div>
        <div class="k">出生金木</div><div>经 ${B.lon.toFixed(4)}° → ${mwB.note}</div>
        <div class="k">现地水火</div><div>纬 ${S.lat.toFixed(4)}° → 水 ${sf.water.toFixed(4)} / 火 ${sf.fire.toFixed(4)}</div>
        <div class="k">现地金木</div><div>经 ${S.lon.toFixed(4)}° → ${mwS.note}</div>
        <div class="k">流层四柱</div><div>${S.pillars ? [S.pillars.year,S.pillars.month,S.pillars.day,S.pillars.time].join(" ") : "—"}</div>
      </div>`;

      const zz = A.zhengZhuan;
      const works = A.works || [];
      $("zhYongJi").innerHTML = `<div class="kv">
        <div class="k">锁定正从</div><div>${zz.pattern}·${zz.strength} · ${zz.orient}</div>
        <div class="k">专档体客</div><div>我 ${zz.wo.toFixed(3)} ∶ 客 ${zz.ta.toFixed(3)}（定体·令地旺衰，不算合冲生克）</div>
        <div class="k">用神</div><div>${zz.yongShen.join("、")}</div>
        <div class="k">忌神</div><div>${zz.jiShen.join("、")}</div>
      </div>
      <div class="chiprow">${works.map(w => `<span class="chip ${w.jiXiong.includes("利忌")?"hot":"ok"}">${w.name}</span>`).join("")}</div>
      <div class="verdict"><b>做功：</b>${works.map(w => `${w.name} → ${w.jiXiong}（${w.note}）`).join("； ")}。大运 ${dy ? dy.ganZhi : "—"} ×${p.yun?.daYun}；小运 ×${p.yun?.xiaoYun}。</div>`;

      $("zhFine").innerHTML = (rel.length
        ? `<ul style="margin:8px 0;padding-left:1.2rem;color:var(--mist)">${rel.map(r => `<li>${r}</li>`).join("")}</ul>`
        : `<p class="footnote">未检出六冲／六合。</p>`) +
        `<div class="verdict"><b>BI：</b>${A.bi.bi.toFixed(3)}（${A.bi.label}）· 配对带 ${A.verdicts.band}</div>`;

      $("zhDetail").innerHTML = `<div class="verdict">${(A.verdicts.lines || []).map(l => l).join("<br/>")}</div>`;

      // 键页：真·键/关系列表（合冲刑害破生克 + 同字共振 + 通根 + 烈度）
      const keys = (A.keys || []).slice().sort((a, b) => b.intensity - a.intensity);
      $("jnKeys").innerHTML = `
        <div class="verdict"><b>双冻结：</b>正从锁令地旺衰（不算合冲生克）${A.zhengDiHou.pattern}·${A.zhengDiHou.strength} → 细断只改用忌 ${zz.orient}；键共振峰烈度 <b>${A.intensity.toFixed(2)}</b>（事显阈值≥2.0）。</div>
        <table class="pt"><thead><tr><th>键类</th><th>双方</th><th>层</th><th>烈度</th><th>事显</th></tr></thead><tbody>
        ${keys.length ? keys.map(k => `<tr><td>${k.cls}${k.doubleKey ? "·双键" : ""}</td><td>${(k.chars || []).join("")}${k.hua ? "→" + k.hua : ""}</td><td>${k.layerZh || k.note || "—"}</td><td>${k.intensity}</td><td>${k.manifest ? "✔显" : "—"}</td></tr>`).join("") : `<tr><td colspan="5">未检出键</td></tr>`}
        </tbody></table>
        ${(A.tonggen && A.tonggen.length) ? `<div class="verdict"><b>通根：</b>${A.tonggen.map(t => t.note).join("；")}</div>` : ""}`;
      $("jnEnergy").innerHTML = Object.entries(A.energy.parts).map(([k, v]) =>
        `<div class="h-line"><span>${k}</span><span class="n">${v.toFixed(3)}</span></div>`
      ).join("") + `<div class="verdict"><b>BI 份额：</b>印 ${Math.round(A.bi.shares.yin*100)}% · 比 ${Math.round(A.bi.shares.biJie*100)}% · 食伤 ${Math.round(A.bi.shares.shiShang*100)}% · 财 ${Math.round(A.bi.shares.cai*100)}% · 官杀 ${Math.round(A.bi.shares.guanSha*100)}%</div>`;
      $("jnDetail").innerHTML = `<div class="verdict">${(A.verdicts.lines || []).join("<br/>")}</div>
        <p class="footnote"><a href="../AI_FEED_CANON.html?unlock=master">AI_FEED_CANON →</a></p>`;
    }

    $("paramTables").innerHTML = renderParamTables(p);

    // 教程三阶段
    if ($("teachRoot") && window.KRTutorial) {
      if (window.KRTutorial.mount) window.KRTutorial.mount($("teachRoot"), "basics");
      else $("teachRoot").innerHTML = window.KRTutorial.renderHtml();
    }
  }

  function renderParamTables(p) {
    const ling = p.ling || {};
    const sk = p.shengKe || {};
    return `
      <div class="kv">
        <div class="k">版本</div><div>${p.version}</div>
        <div class="k">地／令</div><div>× ${p.diRelativeToLing}</div>
        <div class="k">大运／小运</div><div>× ${p.yun?.daYun} ／ × ${p.yun?.xiaoYun}</div>
      </div>
      <table class="pt"><thead><tr><th>令位</th><th>系数</th></tr></thead><tbody>
        ${Object.entries(ling).map(([k, v]) => `<tr><td>${k}</td><td>${v}</td></tr>`).join("")}
      </tbody></table>
      <table class="pt"><thead><tr><th>关系</th><th>A</th><th>B</th><th>C</th></tr></thead><tbody>
        <tr><td>合</td><td>${sk.he?.a}</td><td>${sk.he?.b}</td><td>${sk.he?.c}</td></tr>
        <tr><td>冲</td><td>${sk.chong?.a}</td><td>${sk.chong?.b}</td><td>—</td></tr>
        <tr><td>生</td><td>${sk.sheng?.a}</td><td>${sk.sheng?.b}</td><td>—</td></tr>
        <tr><td>克</td><td>${sk.ke?.a}</td><td>${sk.ke?.b}</td><td>—</td></tr>
        <tr><td>刑</td><td>${sk.xing?.a}</td><td>${sk.xing?.b}</td><td>—</td></tr>
        <tr><td>害</td><td>${sk.hai?.a}</td><td>${sk.hai?.b}</td><td>—</td></tr>
        <tr><td>破(A生B)</td><td>${sk.poSheng?.a}</td><td>${sk.poSheng?.b}</td><td>—</td></tr>
      </tbody></table>
      <p class="footnote">${p.oneLinerZh || ""}</p>`;
  }

  function runPaipan() {
    try {
      if ($("syncPlace").checked) {
        $("standPlace").value = $("birthPlace").value;
      }
      let chart;
      if (state.mode === "solar") {
        chart = buildFromSolar($("sy").value, $("sm").value, $("sd").value, $("sh").value, $("smin").value);
      } else if (state.mode === "lunar") {
        chart = buildFromLunar($("ly").value, $("lm").value, $("ld").value, $("lhi").value, $("leap").value === "1");
      } else {
        chart = buildFromGz(parseGzLine($("gzIn").value));
      }
      state.chart = chart;
      state.daYunIndex = Math.min(1, Math.max(0, (chart.daYun || []).length > 1 ? 1 : 0));
      renderPillars(chart);
      renderPanels(chart);
      setTab("info");
      msg(`已排盘 · ${chart.pillars.year} ${chart.pillars.month} ${chart.pillars.day} ${chart.pillars.time}`, true);
    } catch (e) {
      console.error(e);
      msg(e.message || String(e), false);
    }
  }

  function loadDemo() {
    setMode("solar");
    $("sy").value = 2008; $("sm").value = 12; $("sd").value = 3; $("sh").value = 2; $("smin").value = 10;
    $("gzIn").value = "戊子 癸亥 丁丑 辛丑";
    $("name").value = "芝加哥示意";
    $("sex").value = "1";
    $("birthPlace").value = "chicago";
    $("standPlace").value = "chicago";
    $("syncPlace").checked = true;
    $("trueSolar").checked = true;
    $("beijingClock").checked = false;
    syncPlaceFields(true);
    runPaipan();
  }

  function loadRio() {
    setMode("solar");
    // 里约：当地公历/农历不变 · 日干支+1 · 月支对冲
    $("sy").value = 1973; $("sm").value = 10; $("sd").value = 15; $("sh").value = 12; $("smin").value = 0;
    $("name").value = "里约·南美";
    $("sex").value = "1";
    $("birthPlace").value = "rio";
    $("standPlace").value = "rio";
    $("syncPlace").checked = true;
    $("trueSolar").checked = true;
    $("beijingClock").checked = false;
    syncPlaceFields(true);
    runPaipan();
  }

  async function fetchWenzhen() {
    if (!state.chart || state.chart.gzOnly) {
      msg("请先用公历／农历排盘再问真联网", false);
      return;
    }
    const s = state.chart.solar;
    if (!s) { msg("无公历对象", false); return; }
    const q = new URLSearchParams({
      year: s.getYear(), month: s.getMonth(), day: s.getDay(),
      hour: s.getHour(), minute: s.getMinute(),
      sex: state.chart.sex,
    });
    msg("问真代理请求中…", true);
    try {
      const res = await fetch("/api/wenzhen?" + q.toString());
      if (!res.ok) throw new Error("HTTP " + res.status);
      const ct = res.headers.get("content-type") || "";
      if (ct.includes("json")) {
        await res.json();
        msg("问真代理已返回 JSON；本地四柱仍以 lunar.js 为准", true);
      } else {
        const t = await res.text();
        msg(`问真代理 HTML ${t.length} 字节；本地四柱 ${state.chart.pillars.year} ${state.chart.pillars.month} ${state.chart.pillars.day} ${state.chart.pillars.time}`, true);
      }
    } catch (e) {
      msg("问真代理暂不可用：" + e.message + "（本地盘可用）", false);
    }
  }

  function applyGate() {
    const unlocked = window.KRAuthor && KRAuthor.ensure();
    const gate = $("gate");
    const shell = $("shell");
    if (unlocked) {
      gate.style.display = "none";
      shell.classList.add("show");
    } else {
      gate.style.display = "";
      shell.classList.remove("show");
    }
  }

  /* 日夜色：自动 = 本地 06:00–18:00 昼，其余夜；可手动锁昼/夜 */
  const THEME_KEY = "kr_theme_pref";
  function autoThemeByClock() {
    const h = new Date().getHours();
    return (h >= 6 && h < 18) ? "day" : "night";
  }
  function readThemePref() {
    try {
      const v = localStorage.getItem(THEME_KEY);
      if (v === "day" || v === "night" || v === "auto") return v;
    } catch (e) {}
    return "auto";
  }
  function applyTheme(pref) {
    const mode = pref === "day" || pref === "night" ? pref : autoThemeByClock();
    document.documentElement.setAttribute("data-theme", mode);
    document.documentElement.setAttribute("data-theme-pref", pref || "auto");
    const sw = $("themeSwitch");
    if (sw) {
      sw.querySelectorAll("button").forEach(b => {
        b.classList.toggle("on", b.dataset.pref === (pref || "auto"));
      });
    }
  }
  function setThemePref(pref) {
    try {
      if (pref === "auto") localStorage.removeItem(THEME_KEY);
      else localStorage.setItem(THEME_KEY, pref);
    } catch (e) {}
    applyTheme(pref);
  }
  function scheduleThemeTick() {
    // 每分钟核对一次自动切换（跨 6/18 点）
    setInterval(() => {
      if (readThemePref() === "auto") applyTheme("auto");
    }, 60 * 1000);
  }

  function bind() {
    document.querySelectorAll("#modeBar button").forEach(b => {
      b.addEventListener("click", () => setMode(b.dataset.mode));
    });
    document.querySelectorAll("#tabs button").forEach(b => {
      b.addEventListener("click", () => setTab(b.dataset.tab));
    });
    document.querySelectorAll("#wzMainTabs button").forEach(b => {
      b.addEventListener("click", () => setMainTab(b.dataset.main));
    });
    document.querySelectorAll("#proSubTabs button").forEach(b => {
      b.addEventListener("click", () => {
        setTab("zhuan");
        setProSub(b.dataset.sub);
        if (b.dataset.sub === "jian") {
          document.querySelectorAll(".bottom a").forEach(a => a.classList.remove("on"));
          $("navJn").classList.add("on");
        }
      });
    });
    $("btnRun").addEventListener("click", runPaipan);
    $("btnDemo").addEventListener("click", loadDemo);
    if ($("btnRio")) $("btnRio").addEventListener("click", loadRio);
    if ($("btnFlowNow")) $("btnFlowNow").addEventListener("click", () => { setFlowNow(); runPaipan(); });
    $("btnWz").addEventListener("click", fetchWenzhen);
    $("birthPlace").addEventListener("change", () => syncPlaceFields(true));
    $("standPlace").addEventListener("change", () => {
      if ($("syncPlace").checked) return;
      syncPlaceFields(false);
    });
    $("syncPlace").addEventListener("change", () => {
      if ($("syncPlace").checked) syncPlaceFields(true);
    });
    ["birthLat","birthLon","standLat","standLon"].forEach(id => {
      if ($(id)) $(id).addEventListener("change", updateGeoHint);
    });
    const sw = $("themeSwitch");
    if (sw) {
      sw.querySelectorAll("button").forEach(b => {
        b.addEventListener("click", () => setThemePref(b.dataset.pref));
      });
    }
    $("navIn").addEventListener("click", () => {
      document.querySelectorAll(".bottom a").forEach(a => a.classList.remove("on"));
      $("navIn").classList.add("on");
    });
    ["navInfo", "navJi", "navZh", "navJn"].forEach((id, i) => {
      const tabs = ["info", "ji", "zhuan", "jian"];
      $(id).addEventListener("click", (e) => {
        e.preventDefault();
        setTab(tabs[i]);
        $("navIn").classList.remove("on");
      });
    });
  }

  function boot() {
    applyTheme(readThemePref());
    scheduleThemeTick();
    applyGate();
    if (!(window.KRAuthor && KRAuthor.ensure())) return;
    bind();
    applyTheme(readThemePref());
    // 预填参烈表／教程
    $("paramTables").innerHTML = renderParamTables(P());
    if ($("teachRoot") && window.KRTutorial) {
      if (window.KRTutorial.mount) window.KRTutorial.mount($("teachRoot"), "basics");
      else $("teachRoot").innerHTML = window.KRTutorial.renderHtml();
    }
    syncPlaceFields(true);
    setFlowNow();
    updateGeoHint();
    const q = new URLSearchParams(location.search);
    if (q.get("demo") === "1") loadDemo();
    else if (q.get("rio") === "1") loadRio();
    else runPaipan();
  }

  if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot);
  else boot();
})();
