// battu.jsx — Bát Tự (Tứ Trụ) engine · thuần toán, không React
// Cần dichly.jsx trước (DL_CAN, DL_CHI, dlSunLong, DL_NGUYET_LENH, TIET_SECTOR, NGU_HANH_COLOR)
// Exports: btLapLaSo, btThapThan, btMuiGioVN, BT_NOI_SINH, btLuuNguyet, btCanHanh, btChiHanh, BT_THAP_THAN_VIET, btGiaoTietNam, btHoaGiap

const BT_CAN_HANH = ['Mộc','Mộc','Hỏa','Hỏa','Thổ','Thổ','Kim','Kim','Thủy','Thủy'];
const BT_CHI_HANH = ['Thủy','Thổ','Mộc','Mộc','Thổ','Hỏa','Hỏa','Thổ','Kim','Kim','Thổ','Thủy'];
const BT_HANH_SINH = { 'Mộc':'Hỏa','Hỏa':'Thổ','Thổ':'Kim','Kim':'Thủy','Thủy':'Mộc' };
const BT_HANH_KHAC = { 'Mộc':'Thổ','Thổ':'Thủy','Thủy':'Hỏa','Hỏa':'Kim','Kim':'Mộc' };
const btCanHanh = (c) => BT_CAN_HANH[c];
const btChiHanh = (z) => BT_CHI_HANH[z];
// tàng can mỗi chi: [chính khí, trung khí, dư khí] (chỉ số can)
const BT_TANG_CAN = [[9],[5,9,7],[0,2,4],[1],[4,1,9],[2,6,4],[3,5],[5,3,1],[6,8,4],[7],[4,7,3],[8,0]];
const BT_TANG_W = [1, 0.6, 0.3];
const BT_THAP_THAN_VIET = { TK:'Tỷ Kiên', KT:'Kiếp Tài', TT:'Thực Thần', TQ:'Thương Quan', TA:'Thiên Tài', CT:'Chính Tài', TS:'Thất Sát', CQ:'Chính Quan', TAN:'Thiên Ấn', CA:'Chính Ấn' };
const BT_THAP_THAN_HAN = { TK:'比', KT:'劫', TT:'食', TQ:'傷', TA:'偏財', CT:'正財', TS:'殺', CQ:'官', TAN:'梟', CA:'印' };
// nhóm thập thần: trợ thân (Tỷ Kiếp Ấn) / tiết khắc thân (Thực Thương Tài Quan Sát)
const BT_TRO_THAN = { TK:1, KT:1, TAN:1, CA:1 };
// thập thần của can X đối với nhật chủ
function btThapThan(nhat, can) {
  const h0 = BT_CAN_HANH[nhat], h = BT_CAN_HANH[can];
  const same = (nhat % 2) === (can % 2);
  let k;
  if (h === h0) k = same ? 'TK' : 'KT';
  else if (BT_HANH_SINH[h0] === h) k = same ? 'TT' : 'TQ';
  else if (BT_HANH_KHAC[h0] === h) k = same ? 'TA' : 'CT';
  else if (BT_HANH_KHAC[h] === h0) k = same ? 'TS' : 'CQ';
  else k = same ? 'TAN' : 'CA';
  return { k, viet: BT_THAP_THAN_VIET[k], han: BT_THAP_THAN_HAN[k], tro: !!BT_TRO_THAN[k] };
}
// 12 trường sinh: dương thuận, âm nghịch; chi khởi Trường Sinh theo can
const BT_TS_TEN = ['Trường Sinh','Mộc Dục','Quan Đới','Lâm Quan','Đế Vượng','Suy','Bệnh','Tử','Mộ','Tuyệt','Thai','Dưỡng'];
const BT_TS_KHOI = [11, 6, 2, 9, 2, 9, 5, 0, 8, 3]; // Giáp Hợi, Ất Ngọ, Bính Dần, Đinh Dậu, Mậu Dần, Kỷ Dậu, Canh Tỵ, Tân Tý, Nhâm Thân, Quý Mão
const BT_TS_VUONG = { 'Trường Sinh':1, 'Quan Đới':1, 'Lâm Quan':1, 'Đế Vượng':1 };
function btTruongSinh(can, chi) {
  const k = BT_TS_KHOI[can];
  const i = can % 2 === 0 ? (chi - k + 12) % 12 : (k - chi + 12) % 12;
  return BT_TS_TEN[i];
}
const BT_NAP_AM = ['Hải Trung Kim','Lư Trung Hỏa','Đại Lâm Mộc','Lộ Bàng Thổ','Kiếm Phong Kim','Sơn Đầu Hỏa','Giản Hạ Thủy','Thành Đầu Thổ','Bạch Lạp Kim','Dương Liễu Mộc','Tuyền Trung Thủy','Ốc Thượng Thổ','Tích Lịch Hỏa','Tùng Bách Mộc','Trường Lưu Thủy','Sa Trung Kim','Sơn Hạ Hỏa','Bình Địa Mộc','Bích Thượng Thổ','Kim Bạch Kim','Phú Đăng Hỏa','Thiên Hà Thủy','Đại Dịch Thổ','Thoa Xuyến Kim','Tang Đố Mộc','Đại Khê Thủy','Sa Trung Thổ','Thiên Thượng Hỏa','Thạch Lựu Mộc','Đại Hải Thủy'];
// chỉ số hoa giáp 0..59 từ (can, chi)
function btHoaGiap(can, chi) { for (let i = 0; i < 60; i++) if (i % 10 === can && i % 12 === chi) return i; return 0; }
const btNapAm = (can, chi) => BT_NAP_AM[Math.floor(btHoaGiap(can, chi) / 2)];
const btTru = (can, chi) => ({ can, chi, canTen: DL_CAN[can], chiTen: DL_CHI[chi], ten: DL_CAN[can] + ' ' + DL_CHI[chi], hg: btHoaGiap(can, chi) });
const btTruTuHG = (hg) => btTru(((hg % 10) + 10) % 10, ((hg % 12) + 12) % 12);

// ── thời gian ──
// phương trình thời gian (phút) — Spencer, sai số < 1 phút
function btEoT(utcMs) {
  const d = new Date(utcMs);
  const start = Date.UTC(d.getUTCFullYear(), 0, 1);
  const N = (utcMs - start) / 86400000;
  const B = 2 * Math.PI * (N - 81) / 365.242;
  return 9.87 * Math.sin(2 * B) - 7.53 * Math.cos(B) - 1.5 * Math.sin(B);
}
// múi giờ VN theo lịch sử (gợi ý — người dùng sửa được). mien: 'bac' | 'nam'
function btMuiGioVN(y, m, d, mien = 'nam') {
  const t = Date.UTC(y, m - 1, d);
  const at = (Y, M, D) => Date.UTC(Y, M - 1, D);
  if (t < at(1943, 1, 1)) return { tz: 7, note: 'Trước 1943: Đông Dương dùng UTC+7' };
  if (t < at(1945, 3, 14)) return { tz: 8, note: '1943–3/1945: toàn Đông Dương UTC+8' };
  if (t < at(1945, 9, 2)) return { tz: 9, note: '14/3–2/9/1945: Nhật chiếm đóng, UTC+9' };
  if (t < at(1947, 4, 1)) return { tz: 7, note: '9/1945–3/1947: UTC+7' };
  if (t < at(1955, 7, 1)) return { tz: mien === 'bac' ? 7 : 8, note: '1947–1955: vùng Pháp kiểm soát UTC+8, vùng kháng chiến UTC+7 — tự chọn theo nơi sinh' };
  if (mien === 'nam' && t >= at(1960, 1, 1) && t < at(1975, 6, 13)) return { tz: 8, note: '1960–13/6/1975: miền Nam dùng UTC+8 (giờ Sài Gòn nhanh hơn Hà Nội 1 giờ)' };
  return { tz: 7, note: 'UTC+7 (giờ Việt Nam hiện hành)' };
}
const BT_NOI_SINH = [
  { ten: 'Hà Nội', lon: 105.85, mien: 'bac' }, { ten: 'Hải Phòng', lon: 106.68, mien: 'bac' }, { ten: 'Nam Định', lon: 106.17, mien: 'bac' },
  { ten: 'Thanh Hóa', lon: 105.78, mien: 'bac' }, { ten: 'Vinh', lon: 105.69, mien: 'bac' }, { ten: 'Huế', lon: 107.58, mien: 'nam' },
  { ten: 'Đà Nẵng', lon: 108.22, mien: 'nam' }, { ten: 'Quy Nhơn', lon: 109.22, mien: 'nam' }, { ten: 'Nha Trang', lon: 109.19, mien: 'nam' },
  { ten: 'Đà Lạt', lon: 108.44, mien: 'nam' }, { ten: 'Buôn Ma Thuột', lon: 108.05, mien: 'nam' }, { ten: 'Sài Gòn', lon: 106.70, mien: 'nam' },
  { ten: 'Biên Hòa', lon: 106.82, mien: 'nam' }, { ten: 'Vũng Tàu', lon: 107.08, mien: 'nam' }, { ten: 'Cần Thơ', lon: 105.78, mien: 'nam' },
  { ten: 'Rạch Giá', lon: 105.08, mien: 'nam' }, { ten: 'Cà Mau', lon: 105.15, mien: 'nam' },
];

const btSunSector = (utcMs) => Math.floor(dlSunLong(new Date(utcMs)) / 15) % 24;
// góc lệch hoàng kinh so với mốc (−180..180)
const btDiff = (utcMs, target) => ((dlSunLong(new Date(utcMs)) - target + 540) % 360) - 180;
// tìm thời điểm hoàng kinh = target trong [a,b] (nhị phân, tới giây)
function btTimMoc(a, b, target) {
  for (let i = 0; i < 48; i++) { const m = (a + b) / 2; if (btDiff(m, target) < 0) a = m; else b = m; }
  return (a + b) / 2;
}
// mốc giao TIẾT (sector lẻ = đầu tháng lệnh) kế tiếp (dir=1) hoặc vừa qua (dir=-1)
function btGiaoTiet(utcMs, dir) {
  const s = btSunSector(utcMs);
  const DAY = 86400000;
  if (dir > 0) {
    const tgt = ((s % 2 === 1 ? s + 2 : s + 1) % 24) * 15;
    let b = utcMs + DAY; while (btDiff(b, tgt) < 0) b += DAY;
    return { ms: btTimMoc(b - DAY, b, tgt), sector: Math.round(tgt / 15) % 24 };
  }
  const tgt = ((s % 2 === 1 ? s : s + 23) % 24) * 15;
  let a = utcMs - DAY; while (btDiff(a, tgt) >= 0) a -= DAY;
  return { ms: btTimMoc(a, a + DAY, tgt), sector: Math.round(tgt / 15) % 24 };
}
// 12 mốc giao tiết của một năm can chi (từ Lập Xuân năm Y tới Lập Xuân Y+1), giờ UTC ms
function btGiaoTietNam(y) {
  const out = [];
  let t = Date.UTC(y, 0, 20);
  for (let i = 0; i < 12; i++) { const g = btGiaoTiet(t, 1); out.push(g); t = g.ms + 86400000; }
  return out;
}

// ── từ wall-clock (y,m,d,hh,mm, tz, lon) → tứ trụ ──
// mode: 'chan' = chân thái dương thời (kinh độ + phương trình thời gian) | 'dongho' = giờ đồng hồ theo múi
function btTuTru(inp) {
  const { y, m, d, hh = 12, mm = 0, tz = 7, lon = 105.85, mode = 'chan' } = inp;
  const wallMs = Date.UTC(y, m - 1, d, hh, mm);
  const utcMs = wallMs - tz * 3600000;
  const eot = btEoT(utcMs);
  const lonMin = (lon - tz * 15) * 4; // lệch kinh độ so với kinh tuyến múi (phút)
  const hieuChinh = mode === 'chan' ? lonMin + eot : 0;
  const solarMs = wallMs + hieuChinh * 60000; // wall-clock mặt trời thật (giả UTC)
  const S = new Date(solarMs);
  const sy = S.getUTCFullYear(), sm = S.getUTCMonth() + 1, sd = S.getUTCDate(), sh = S.getUTCHours(), smin = S.getUTCMinutes();
  // trụ tháng theo hoàng kinh tại thời điểm vật lý
  const sector = btSunSector(utcMs);
  const thangIdx = Math.floor(((sector - 21 + 24) % 24) / 2); // 0 = Dần … 11 = Sửu
  // trụ năm: đổi năm tại Lập Xuân
  let namSo = sy;
  if (sm <= 2 && sector >= 18 && sector <= 20) namSo = sy - 1;      // Đông Chí–Đại Hàn đầu năm dương → năm cũ
  if (sm >= 12 && sector >= 21) namSo = sy + 1;                      // (hiếm) Lập Xuân rơi vào cuối tháng 12
  const canNam = ((namSo - 4) % 10 + 10) % 10, chiNam = ((namSo - 4) % 12 + 12) % 12;
  const canThang = ((canNam % 5) * 2 + 2 + thangIdx) % 10, chiThang = (thangIdx + 2) % 12;
  // trụ ngày: giờ Tý (23h) thuộc ngày hôm sau
  let dy = sy, dm = sm, dd = sd;
  if (sh === 23) { const n = new Date(Date.UTC(sy, sm - 1, sd + 1)); dy = n.getUTCFullYear(); dm = n.getUTCMonth() + 1; dd = n.getUTCDate(); }
  const a = Math.floor((14 - dm) / 12), yr = dy + 4800 - a, mn = dm + 12 * a - 3;
  const jdn = dd + Math.floor((153 * mn + 2) / 5) + 365 * yr + Math.floor(yr / 4) - Math.floor(yr / 100) + Math.floor(yr / 400) - 32045;
  const canNgay = (jdn + 9) % 10, chiNgay = (jdn + 1) % 12;
  const gioIdx = Math.floor(((sh + 1) % 24) / 2);
  const canGio = ((canNgay % 5) * 2 + gioIdx) % 10;
  return {
    nam: btTru(canNam, chiNam), thang: btTru(canThang, chiThang), ngay: btTru(canNgay, chiNgay), gio: btTru(canGio, gioIdx),
    utcMs, solarMs, eot, lonMin, hieuChinh, sector, thangIdx, namSo,
    solar: { y: sy, m: sm, d: sd, hh: sh, mm: smin },
  };
}

// ── thần sát ──
const BT_TAM_HOP = (chi) => [8, 0, 4].includes(chi) ? 0 : [2, 6, 10].includes(chi) ? 1 : [5, 9, 1].includes(chi) ? 2 : 3; // Thân Tý Thìn / Dần Ngọ Tuất / Tỵ Dậu Sửu / Hợi Mão Mùi
const BT_SAT_TAMHOP = {
  'Hàm Trì': [9, 3, 6, 0], 'Dịch Mã': [2, 8, 11, 5], 'Hoa Cái': [4, 10, 1, 7], 'Kiếp Sát': [5, 11, 2, 8], 'Tướng Tinh': [0, 6, 9, 3],
};
const BT_THIEN_AT = [[1, 7], [0, 8], [11, 9], [11, 9], [1, 7], [0, 8], [1, 7], [2, 6], [5, 3], [5, 3]];
const BT_VAN_XUONG = [5, 6, 8, 9, 8, 9, 11, 0, 2, 3];
const BT_LOC = [2, 3, 5, 6, 5, 6, 8, 9, 11, 0];
const BT_DUONG_NHAN = { 0: 3, 1: 2, 2: 6, 3: 5, 4: 6, 5: 5, 6: 9, 7: 8, 8: 0, 9: 11 }; // = Đế Vượng cả 10 can (26:691-695, 13_:154, CK)
const BT_KHOI_CUONG = [[6, 4], [6, 10], [8, 4], [4, 10]]; // Canh Thìn, Canh Tuất, Nhâm Thìn, Mậu Tuất
const BT_THAI_CUC = [[0, 6], [0, 6], [3, 9], [3, 9], [4, 10, 1, 7], [4, 10, 1, 7], [2, 11], [2, 11], [5, 8], [5, 8]];
const BT_HONG_DIEM = [6, 8, 2, 7, 4, 4, 10, 9, 0, 8]; // CK: Giáp Ngọ · Ất Thân · Bính Dần · Đinh Mùi · Mậu Thìn · Kỷ Thìn · Canh Tuất · Tân Dậu · Nhâm Tý · Quý Thân
const BT_KIM_DU = [4, 5, 7, 8, 7, 8, 10, 11, 1, 2]; // Lộc + 2
const BT_QUOC_AN = [10, 11, 1, 2, 1, 2, 4, 5, 7, 8]; // Lộc + 8 (Canh → Thìn theo quy tắc)
const BT_AM_DUONG_SAI_THO = [[2, 0], [3, 1], [4, 2], [7, 3], [8, 4], [9, 5], [2, 6], [3, 7], [4, 8], [7, 9], [8, 10], [9, 11]];
const BT_PHUC_TINH = [[2, 0], [1, 7], [0, 8], [11, 9], [8, 4], [10, 3]]; // Phúc Tinh Quý Nhân: Giáp/Mậu(? ) → Dần Tý · Ất/Kỷ → Sửu Mùi · Bính → Tý Thân · Đinh → Hợi Dậu · Canh/Nhâm(?) → Thân Thìn · Tân/Quý → Tuất Mão — phổ thông, KB chưa có (CK pending)
const BT_PHUC_TINH_CAN = { 0: [2, 0], 4: [2, 0], 1: [1, 7], 5: [1, 7], 2: [0, 8], 3: [11, 9], 6: [8, 4], 8: [8, 4], 7: [10, 3], 9: [10, 3] };
const BT_THIEN_DUC = { 2: ['can', 3], 3: ['chi', 8], 4: ['can', 8], 5: ['can', 7], 6: ['chi', 11], 7: ['can', 0], 8: ['can', 9], 9: ['chi', 2], 10: ['can', 2], 11: ['can', 1], 0: ['chi', 5], 1: ['can', 6] };
const BT_NGUYET_DUC = [8, 2, 6, 0]; // theo tam hợp tháng: Thân Tý Thìn Nhâm · Dần Ngọ Tuất Bính · Tỵ Dậu Sửu Canh · Hợi Mão Mùi Giáp
const BT_CO_QUA = (chi) => [11, 0, 1].includes(chi) ? [2, 10] : [2, 3, 4].includes(chi) ? [5, 1] : [5, 6, 7].includes(chi) ? [8, 4] : [11, 7];
const BT_SAT_NGHIA = {
  'Thiên Ất Quý Nhân': 'quý nhân phù trợ, gặp hung hóa cát', 'Văn Xương': 'thông minh, học hành, văn chương', 'Lộc Thần': 'bổng lộc, chỗ dựa tài chính',
  'Dương Nhận': 'khí mạnh quá độ — quyết đoán nhưng dễ tổn thương', 'Hàm Trì': 'sức hút, duyên tình, nghệ thuật (tên mượn: Đào Hoa)', 'Dịch Mã': 'di chuyển, thay đổi, đi xa', 'Khôi Cương': 'thông tuệ, cương quyết, hiếu thắng',
  'Hoa Cái': 'cô cao, nghệ thuật, tôn giáo, huyền học', 'Kiếp Sát': 'tranh đoạt, hao tổn bất ngờ', 'Tướng Tinh': 'uy quyền, chỉ huy',
  'Thiên Đức': 'đức trời che chở', 'Nguyệt Đức': 'đức tháng, nhân hậu', 'Cô Thần': 'lẻ loi (nam kỵ)', 'Quả Tú': 'lẻ loi (nữ kỵ)', 'Không Vong': 'rỗng — việc ở trụ ấy khó bền',
  'Thái Cực Quý Nhân': 'ham huyền học, có căn tu, gặp quý nhân đạo học', 'Hồng Diễm': 'duyên thầm, sức hút phái khác (khác Hàm Trì)', 'Kim Dư': 'xe vàng — hưởng lộc, phúc vợ con', 'Quốc Ấn': 'ấn tín, chức vụ, uy tín', 'Âm Dương Sai Thố': 'hôn nhân dễ lệch, ngoại tộc bất hoà', 'Phúc Tinh Quý Nhân': 'phúc ấm, an nhàn', 'Vong Thần': 'hao tán, mất mát ngầm', 'Thiên La': 'lưới trời (nam kỵ)', 'Địa Võng': 'lưới đất (nữ kỵ)',
};
function btThanSat(T) {
  const tru = [['nam', T.nam], ['thang', T.thang], ['ngay', T.ngay], ['gio', T.gio]];
  const out = { nam: [], thang: [], ngay: [], gio: [] };
  const add = (k, ten, nguon) => { if (!out[k].some(s => s.ten === ten)) out[k].push({ ten, nguon, nghia: BT_SAT_NGHIA[ten] || '' }); };
  const nc = T.ngay.can, yc = T.nam.chi, dc = T.ngay.chi, ycan = T.nam.can;
  if (BT_KHOI_CUONG.some(([c, z]) => c === T.ngay.can && z === T.ngay.chi)) add('ngay', 'Khôi Cương', 'trụ ngày');
  if (BT_AM_DUONG_SAI_THO.some(([c, z]) => c === T.ngay.can && z === T.ngay.chi)) add('ngay', 'Âm Dương Sai Thố', 'trụ ngày');
  for (const [k, t] of tru) {
    if (BT_THAI_CUC[nc].includes(t.chi)) add(k, 'Thái Cực Quý Nhân', 'nhật can');
    if (k !== 'nam' && BT_THAI_CUC[ycan].includes(t.chi)) add(k, 'Thái Cực Quý Nhân', 'niên can');
    if (BT_HONG_DIEM[nc] === t.chi) add(k, 'Hồng Diễm', 'nhật can');
    if (k !== 'nam' && BT_HONG_DIEM[ycan] === t.chi) add(k, 'Hồng Diễm', 'niên can');
    if (BT_KIM_DU[nc] === t.chi) add(k, 'Kim Dư', 'nhật can');
    if (BT_QUOC_AN[nc] === t.chi) add(k, 'Quốc Ấn', 'nhật can');
    if (k !== 'nam' && BT_QUOC_AN[ycan] === t.chi) add(k, 'Quốc Ấn', 'niên can');
    if (BT_VAN_XUONG[ycan] === t.chi && k !== 'nam') add(k, 'Văn Xương', 'niên can');
    if ((BT_PHUC_TINH_CAN[nc] || []).includes(t.chi)) add(k, 'Phúc Tinh Quý Nhân', 'nhật can');
    const vong = { 0: 11, 1: 8, 2: 5, 3: 2 };
    if (k !== 'nam' && vong[BT_TAM_HOP(yc)] === t.chi) add(k, 'Vong Thần', 'niên chi');
    if ((yc === 4 && t.chi === 5) || (yc === 5 && t.chi === 4) || (dc === 4 && t.chi === 5) || (dc === 5 && t.chi === 4)) add(k, 'Địa Võng', 'Thìn–Tỵ');
    if ((yc === 10 && t.chi === 11) || (yc === 11 && t.chi === 10) || (dc === 10 && t.chi === 11) || (dc === 11 && t.chi === 10)) add(k, 'Thiên La', 'Tuất–Hợi');
    if (BT_THIEN_AT[nc].includes(t.chi)) add(k, 'Thiên Ất Quý Nhân', 'nhật can');
    if (k !== 'nam' && BT_THIEN_AT[ycan].includes(t.chi)) add(k, 'Thiên Ất Quý Nhân', 'niên can');
    if (BT_VAN_XUONG[nc] === t.chi) add(k, 'Văn Xương', 'nhật can');
    if (BT_LOC[nc] === t.chi) add(k, 'Lộc Thần', 'nhật can');
    if (BT_DUONG_NHAN[nc] === t.chi) add(k, 'Dương Nhận', 'nhật can');
    for (const ten in BT_SAT_TAMHOP) {
      if (k !== 'nam' && BT_SAT_TAMHOP[ten][BT_TAM_HOP(yc)] === t.chi) add(k, ten, 'niên chi');
      if (k !== 'ngay' && BT_SAT_TAMHOP[ten][BT_TAM_HOP(dc)] === t.chi) add(k, ten, 'nhật chi');
    }
    const td = BT_THIEN_DUC[T.thang.chi];
    if (td && ((td[0] === 'can' && t.can === td[1]) || (td[0] === 'chi' && t.chi === td[1]))) add(k, 'Thiên Đức', 'nguyệt chi');
    if (t.can === BT_NGUYET_DUC[BT_TAM_HOP(T.thang.chi)]) add(k, 'Nguyệt Đức', 'nguyệt chi');
    const [co, qua] = BT_CO_QUA(yc);
    if (k !== 'nam' && t.chi === co) add(k, 'Cô Thần', 'niên chi');
    if (k !== 'nam' && t.chi === qua) add(k, 'Quả Tú', 'niên chi');
  }
  // không vong theo tuần của trụ ngày
  const tuan = T.ngay.hg - T.ngay.can;
  const kv = [(tuan + 10) % 12, (tuan + 11) % 12];
  for (const [k, t] of tru) if (k !== 'ngay' && kv.includes(t.chi)) add(k, 'Không Vong', 'tuần nhật trụ');
  return { sat: out, khongVong: kv.map(c => DL_CHI[c]) };
}

// ── cường nhược sơ bộ + phân bố ngũ hành ──
function btCuongNhuoc(T, tuLenhCan) {
  const nc = T.ngay.can;
  const hanh = { 'Mộc': 0, 'Hỏa': 0, 'Thổ': 0, 'Kim': 0, 'Thủy': 0 };
  let tro = 0, hao = 0;
  const chiTiet = [];
  const dem = (can, w, nhan) => {
    hanh[BT_CAN_HANH[can]] += w;
    if (can === nc && nhan === 'ngày·can') return; // nhật chủ không tự tính
    const tt = btThapThan(nc, can);
    if (tt.tro) tro += w; else hao += w;
    chiTiet.push({ can: DL_CAN[can], tt: tt.viet, w: +w.toFixed(2), nhan });
  };
  const W_CAN = { nam: 1, thang: 1.2, ngay: 0, gio: 1 }, W_CHI = { nam: 1, thang: 2, ngay: 1.4, gio: 1 };
  for (const k of ['nam', 'thang', 'ngay', 'gio']) {
    const t = T[k];
    if (k !== 'ngay') dem(t.can, W_CAN[k], k + '·can'); else hanh[BT_CAN_HANH[t.can]] += 1;
    BT_TANG_CAN[t.chi].forEach((c, i) => dem(c, BT_TANG_W[i] * W_CHI[k], k + '·tàng ' + DL_CAN[c]));
  }
  // đắc lệnh: can đang nắm lệnh (nhân nguyên tư lệnh) lúc sinh sinh/tỷ nhật chủ
  const tl = tuLenhCan != null ? btThapThan(nc, tuLenhCan) : null;
  const dacLenh = tl ? tl.tro : false;
  if (tl) { if (tl.tro) tro += 2; else hao += 2; }
  const ts = btTruongSinh(nc, T.thang.chi);
  const dacDia = ['nam', 'thang', 'ngay', 'gio'].filter(k => BT_TANG_CAN[T[k].chi].some(c => btThapThan(nc, c).tro)).length; // số chi có gốc
  const dacThe = ['nam', 'thang', 'gio'].filter(k => btThapThan(nc, T[k].can).tro).length;
  const ratio = tro / (tro + hao);
  const muc = ratio >= 0.62 ? 'Thân vượng' : ratio >= 0.5 ? 'Thân hơi vượng' : ratio >= 0.4 ? 'Thân hơi nhược' : 'Thân nhược';
  return { hanh, tro: +tro.toFixed(2), hao: +hao.toFixed(2), ratio, muc, dacLenh, tuLenh: tl, tsThang: ts, tsVuong: !!BT_TS_VUONG[ts], dacDia, dacThe, chiTiet };
}

// ── đại vận ──
function btDaiVan(T, gioiTinh) {
  const duongNam = T.nam.can % 2 === 0;
  const thuan = (duongNam && gioiTinh === 'nam') || (!duongNam && gioiTinh === 'nu');
  const g = btGiaoTiet(T.utcMs, thuan ? 1 : -1);
  const diffMs = Math.abs(g.ms - T.utcMs);
  const khoiNam = diffMs / (3 * 86400000); // 3 ngày = 1 năm
  const khoiThang = Math.round((khoiNam % 1) * 12);
  const khoiMs = T.utcMs + khoiNam * 365.2422 * 86400000;
  const van = [];
  for (let i = 1; i <= 8; i++) {
    const hg = T.thang.hg + (thuan ? i : -i);
    const tru = btTruTuHG(hg);
    const batDauMs = khoiMs + (i - 1) * 10 * 365.2422 * 86400000;
    const tuoi = Math.floor(khoiNam) + (i - 1) * 10;
    van.push({ i, ...tru, tuoi, tuoiAm: tuoi + 1, batDau: new Date(batDauMs).getUTCFullYear(), ketThuc: new Date(batDauMs).getUTCFullYear() + 9, batDauMs });
  }
  return { thuan, khoiNam, khoiThang, khoiMs, van, giaoTiet: g, tietTen: TIET_SECTOR[g.sector] };
}
// lưu niên 10 năm của một đại vận
function btLuuNien(T, v) {
  const out = [];
  for (let y = v.batDau; y <= v.ketThuc; y++) {
    const can = ((y - 4) % 10 + 10) % 10, chi = ((y - 4) % 12 + 12) % 12;
    out.push({ y, ...btTru(can, chi), tuoi: y - T.namSo, tt: btThapThan(T.ngay.can, can), ts: btTruongSinh(T.ngay.can, chi), napAm: btNapAm(can, chi) });
  }
  return out;
}
// lưu nguyệt một năm dương lịch Y (12 tháng lệnh từ Lập Xuân, mốc giao tiết chính xác)
function btLuuNguyet(T, y, tz = 7) {
  const moc = btGiaoTietNam(y);
  const canNam = ((y - 4) % 10 + 10) % 10;
  return moc.map((g, i) => {
    const can = ((canNam % 5) * 2 + 2 + i) % 10, chi = (i + 2) % 12;
    const next = moc[i + 1] ? moc[i + 1].ms : btGiaoTiet(g.ms + 86400000, 1).ms;
    return { i, ...btTru(can, chi), tiet: TIET_SECTOR[g.sector], tu: g.ms, den: next, tuVN: new Date(g.ms + tz * 3600000), denVN: new Date(next + tz * 3600000), tt: btThapThan(T.ngay.can, can), ts: btTruongSinh(T.ngay.can, chi) };
  });
}

// can đang nắm lệnh (nhân nguyên tư lệnh) tại thời điểm utcMs — ngày thứ mấy của tiết tính theo mốc giao chính xác
function btTuLenhTai(utcMs) {
  const s = btSunSector(utcMs);
  const tgt = s * 15;
  let a = utcMs - 86400000; while (btDiff(a, tgt) >= 0) a -= 86400000;
  const moc = btTimMoc(a, a + 86400000, tgt);
  const ngayThu = Math.floor((utcMs - moc) / 86400000) + 1;
  const tiet = TIET_SECTOR[s];
  const rules = DL_NGUYET_LENH[tiet] || [];
  let can = rules.length ? rules[rules.length - 1][2] : 'Mậu Thổ';
  for (const [s0, e0, c] of rules) if (ngayThu >= s0 && ngayThu <= e0) { can = c; break; }
  const canIdx = DL_CAN.indexOf(can.split(' ')[0]);
  return { tiet, ngayThu, can, canIdx, hanh: can.split(' ')[1], mocMs: moc };
}

// ── tổng hợp lá số ──
function btLapLaSo(inp) {
  const T = btTuTru(inp);
  const nc = T.ngay.can;
  const tl = btTuLenhTai(T.utcMs);
  const tru = {};
  for (const k of ['nam', 'thang', 'ngay', 'gio']) {
    const t = T[k];
    tru[k] = { ...t, tt: k === 'ngay' ? { k: 'NC', viet: 'Nhật chủ', han: '日主', tro: true } : btThapThan(nc, t.can),
      tang: BT_TANG_CAN[t.chi].map((c, i) => ({ can: c, canTen: DL_CAN[c], hanh: BT_CAN_HANH[c], tt: btThapThan(nc, c), w: BT_TANG_W[i] })),
      napAm: btNapAm(t.can, t.chi), ts: btTruongSinh(nc, t.chi), canHanh: BT_CAN_HANH[t.can], chiHanh: BT_CHI_HANH[t.chi] };
  }
  const { sat, khongVong } = btThanSat(T);
  for (const k in sat) tru[k].sat = sat[k];
  const cn = btCuongNhuoc(T, tl.canIdx);
  const dv = btDaiVan(T, inp.gioiTinh || 'nam');
  // thai nguyên: can tháng +1, chi tháng +3 · mệnh cung: 14 − (tháng + giờ), tháng Dần=1, giờ Tý=1 → chi Tý=1
  const thaiNguyen = btTru((T.thang.can + 1) % 10, (T.thang.chi + 3) % 12);
  let mc = 14 - ((T.thangIdx + 1) + (T.gio.chi + 1)); if (mc <= 0) mc += 12; if (mc > 12) mc -= 12;
  const mcChi = (mc - 1 + 12) % 12;
  const mcCan = ((T.nam.can % 5) * 2 + 2 + ((mcChi - 2 + 12) % 12)) % 10;
  const menhCung = btTru(mcCan, mcChi);
  return { inp, T, tru, nhatChu: { can: nc, ten: DL_CAN[nc], hanh: BT_CAN_HANH[nc], duong: nc % 2 === 0 }, tuLenh: tl, khongVong, cuongNhuoc: cn, daiVan: dv, thaiNguyen, menhCung, napAmNam: btNapAm(T.nam.can, T.nam.chi) };
}

Object.assign(window, { btLapLaSo, btThapThan, btMuiGioVN, BT_NOI_SINH, btLuuNguyet, btLuuNien, btCanHanh, btChiHanh, BT_THAP_THAN_VIET, BT_THAP_THAN_HAN, btGiaoTietNam, btHoaGiap, btTruongSinh, btNapAm, btTuLenhTai, btGiaoTiet, BT_TANG_CAN, BT_SAT_NGHIA, btEoT });
