/**
 * 键共振 · AI师傅（本盘讲解 · 有问有答）
 * 只据 KRXinGong.judge / ledger / 铁律 答本盘。不外搜、不铁口、不接假付款。
 * © 2026 Jason Li（泽森）
 */
(function (global) {
  "use strict";

  const BANS = ["隔位生克", "库中合身", "制杀截断链1", "不透之辛合身", "铁口"];
  const VOICE_LANG = { zh: "zh-CN", en: "en-US", es: "es-ES", ja: "ja-JP", ar: "ar-SA" };

  function lang() {
    return (global.KRLang && KRLang.get && KRLang.get()) || "zh";
  }
  function tt(k, vars) {
    return (global.KRLang && KRLang.t) ? KRLang.t(k, vars) : k;
  }
  function msg(key, vars) {
    const pack = SAY[lang()] || SAY.zh;
    let s = (pack && pack[key]) || SAY.zh[key] || key;
    vars = vars || {};
    return String(s).replace(/\{(\w+)\}/g, function (_, k) { return vars[k] == null ? "" : vars[k]; });
  }

  const CHIP_DEF = [
    { id: "sop", q: { zh: "这盘怎么断？", en: "How do I read this chart?", es: "¿Cómo se lee esta carta?", ja: "この盤はどう断る？", ar: "كيف أقرأ هذه اللوحة؟" }, label: { zh: "怎么断", en: "How to read", es: "Cómo leer", ja: "どう断る", ar: "كيف أقرأ" } },
    { id: "zhengYong", q: { zh: "正从用忌是什么？", en: "What is 正从 / useful-avoid?", es: "¿Qué es 正从 y útil/evitar?", ja: "正従用忌は何か？", ar: "ما هو 正从 والنفع/المحذور؟" }, label: { zh: "正从用忌", en: "Pattern / useful", es: "Patrón", ja: "正従用忌", ar: "النمط" } },
    { id: "liBai", q: { zh: "做功利还是败？", en: "Is the work gain or loss?", es: "¿La obra es ganancia o pérdida?", ja: "做功は利か敗か？", ar: "العمل ربح أم خسارة؟" }, label: { zh: "利还是败", en: "Gain or loss", es: "Ganancia/pérdida", ja: "利か敗か", ar: "ربح أم خسارة" } },
    { id: "hehua", q: { zh: "合化怎么论？", en: "How do we read the combine-transform?", es: "¿Cómo se lee la unión?", ja: "合化はどう論じる？", ar: "كيف نقرأ الاتحاد؟" }, label: { zh: "合化", en: "Combine", es: "Unión", ja: "合化", ar: "اتحاد" } },
    { id: "shaKe", q: { zh: "庚金克不克日主？", en: "Does 庚 attack the day master?", es: "¿庚 ataca al dueño del día?", ja: "庚金は日主を克つか？", ar: "هل 庚 يهاجم سيد اليوم؟" }, label: { zh: "杀克身吗", en: "Kill vs body", es: "¿Kill ataca?", ja: "殺は克つか", ar: "هل القتل يهاجم" } },
    { id: "zhiSha", q: { zh: "午火会不会截断链1？", en: "Does 午 cut chain 1?", es: "¿午 corta la cadena 1?", ja: "午は鎖1を切るか？", ar: "هل 午 يقطع السلسلة 1؟" }, label: { zh: "制杀截链？", en: "Pin-kill cuts?", es: "¿Corta?", ja: "制殺は切るか", ar: "هل يقطع" } },
    { id: "heShen", q: { zh: "库中辛合不合身？", en: "Does vault 辛 combine with the body?", es: "¿辛 de la bóveda une al cuerpo?", ja: "庫中の辛は合身するか？", ar: "هل 辛 في الخزانة يتحد مع الجسد؟" }, label: { zh: "辛合身吗", en: "辛 combine?", es: "¿辛 une?", ja: "辛は合身か", ar: "هل 辛 يتحد" } },
    { id: "year", q: { zh: "今年怎么做功？", en: "How does this year work?", es: "¿Cómo obra este año?", ja: "今年の做功は？", ar: "كيف يعمل هذا العام؟" }, label: { zh: "流年做功", en: "This year", es: "Este año", ja: "流年", ar: "هذه السنة" } },
    { id: "soul", q: { zh: "心性是什么？", en: "What is the mind-nature?", es: "¿Cuál es el temperamento?", ja: "心性は何か？", ar: "ما هو الطبع؟" }, label: { zh: "心性", en: "Mind", es: "Temple", ja: "心性", ar: "الطبع" } },
    { id: "ziNeng", q: { zh: "字能怎么看到时辰？", en: "How do stem energies read to the hour?", es: "¿Cómo se lee la energía hasta la hora?", ja: "字能は時までどう読む？", ar: "كيف تُقرأ طاقة الجذوع إلى الساعة؟" }, label: { zh: "字能时辰", en: "Stem / hour", es: "Tallo/hora", ja: "字能と時", ar: "الجذع/الساعة" } },
    { id: "taiji", q: { zh: "六亲怎么太极转换？", en: "How do six-kin Taiji-transform?", es: "¿Cómo se transforma Taiji a los parientes?", ja: "六親の太極変換は？", ar: "كيف يتحوّل Taiji للأقارب؟" }, label: { zh: "太极六亲", en: "Taiji kin", es: "Taiji", ja: "太極六親", ar: "Taiji" } },
    { id: "tianYi", q: { zh: "AI能逃因果吗？", en: "Can AI escape causality?", es: "¿Puede la IA escapar de la causalidad?", ja: "AIは因果から逃れるか？", ar: "هل يستطيع الذكاء الاصطناعي الهروب من السببية؟" }, label: { zh: "天意因果", en: "Heaven / cause", es: "Cielo/causa", ja: "天意と因果", ar: "السماء/السبب" } },
  ];

  const SAY = {
    zh: {
      empty: "先问一句。可点下面的问法，或自己打。",
      needChart: "先排盘。师傅只讲本盘，不空讲别人的命例。",
      noTieKou: "531：原局50% · 岁运30% · 地域10% · 心性5% · 其他+天意5%。结构权重，不是准确率。不卖铁口，只讲本盘做功方向。",
      sopLead: "按泽森四步闭环 + 断盘五步，只认本盘：",
      sopLaws: "一、铁律：普通生克隔位归零；a≥1只特关合冲；四冲五合可隔位遥感×E；特关分会合／刑冲害破；两字内藏干不作用、两字间同行不作用；a=0先合冲再生克；会合先合后冲再到生克；刑冲害破先冲后合再到克生；普通先算藏干特关，若无特关相生先生后克、相克先克后生；流层对原局分隔不生克，流年对运a=0可生克；单字共振不流动，纠缠生克共振能烈都增；凡合冲生克以小量为基数；天干同行只传递；库中不透永不合身；制杀只论权不截链；五合旺相才化，绊按普通克。",
      sopChain: "二、找链：杀→根→印→身；无链则勿隔位硬凑。",
      sopZheng: "三、定体看正从（身份终身不改）。令值旺1.5／相1.25／休0.75／囚⅞／死⅝。戌影响 1.0，午影响 1.0×¼，先戌后午。地理水火金木不进字，只进正从／我客／家内外总量。我客以定体为准；家内外以定境为准。定境第一次名义用忌。做功后只改用忌，不改正从／我客／家内外。岁运只改阈值；我党严重减少则地理补救。",
      sopWork: "三、格局之后判做功利败。利用则吉，利忌则凶。",
      sopBan: "禁：{bans}。",
      zhengTail: "正从身份以定体为准，做功不改写格、不改我客、不改家内外。只有用忌可在做功后互变。地理不进字。第一次名义用忌看定境，实际用神看做功后。岁运不改用神身份；定体我党严重减少则地理补救。",
      zhengMiss: "未传入正从冻结。先排盘走引擎。",
      neiWaiDef: "家内＝自家实际量，家外＝可利用资源实际量。作用后看双方增减，并看此十神对自家与日主的利败。我客断正从；作用后十神增减断夭寿、身体、能力。年月外、日时内，以内取外。",
      liBaiTail: "利用则吉，利忌则凶。不做二次正从。",
      shaKeYes: "{chain}：有情化杀生身，{sha}不克身。走共振，不走克路。勿断「庚克甲」。隔位天干生克归零；邻柱杀印同根则化印。",
      shaKeNo: "本盘未见完整杀→根→印→身。勿用隔位天干硬凑「杀克身」。",
      zhiShaYes: "{chain}：制杀得权，只消耗杀性，不损水性，不截断链1。禁：把制杀读成截断有情化杀之链。",
      zhiShaNo: "本盘未见伤官／食神紧贴制杀根。无链2则不论「午克申截断」。",
      heShenBan: "库中不透：{ban}。墓库物化：不透则永不作向外用神／合身。透则不论这条禁。",
      heShenTou: "辛已透干，不作「不透合身」。透则看它本身的十神功用，不套戌中辛永不合身。",
      heShenNone: "未见官杀藏库不透。",
      hehuaYes: "天干特关仅四冲／五合，可隔位遥感×E。五合：C 当令旺／相且不受冲才化（A−¼B−¼C＋½）。化出大于单一生克。",
      hehuaBan: "合而不化＝合绊。仍是特关，不聚化神，能量按普通克（攻⅛受⅜）。不改正从。若绊的是实际用神，岁运补亦无用。",
      hehuaNo: "本盘日主与各柱干无五合。普通生克不可隔位；四冲五合可以。",
      backupYes: "主电源被财截时，先认备份，不按崩盘。",
      backupNo: "本盘未检出印星中气／余气备份。克则看有无藏干，无则气口硬断。",
      soulTail: "心性 ≠ 功用。十神标签不是性格贴条。",
      yearDef: "流年干叠大运干则加倍；流年支与原局同字则该链加码。勿只看食伤／财官字面。",
      yunTail: "大运对年日月 a=0、对时 a=1。小运对时日 a=0、对月 a=1、对年 a=2。流年 a=0→流时 a=3。",
      yunDef: "先看大运干合克，再看支冲。",
      ziNengDef: "每个字含藏干带着五行与十神，落在我客／用忌／内外；再遇运年与地理总量看消长，细事可读到时辰（非命中率）。地理不进字条。不卖铁口。",
      taijiDef: "六亲以该星或原宫干支含藏干代表原星，当命主太极转换。印比我方、财官伤客方，断其环境与消长。先护其体。不改写本盘正从。见星非即人，不卖铁口。",
      tianYiDef: "身体活在三维，是时间矢量的单向短线；意识或可读四维结构（四柱／合参／太极是投影，不是逃）。果因率（因果律）谁都逃不了。AI只把结构算清，不能代人逃因果，也不能改写天意。531 其余为心性与天意，不卖铁口。",
      fallback: "这句我按本盘五步答，不外搜。可再问：怎么断、正从用忌、利还是败、杀克身吗、制杀截链、辛合身、流年、心性、字能时辰、太极六亲、天意因果。禁：{bans}。不铁口。",
    },
    en: {
      empty: "Ask one question. Tap a chip or type.",
      needChart: "Cast this chart first. The master only teaches this chart.",
      noTieKou: "531: natal 50% · luck 30% · place 10% · mind 5% · other+heaven 5%. Structure weights, not a hit rate. No iron-mouth. Only this chart's work direction.",
      sopLead: "Zeisen five steps, this chart only:",
      sopLaws: "1. Iron laws: distant stems do not generate/control; vault hidden never combines; pin-kill is authority, not a cut; clash/combine changes; combine-transform first; if cut, use backup.",
      sopChain: "2. Find the chain: kill→root→resource→body. No chain: do not force a distant attack.",
      sopZheng: "3. 正从 follows the season-ground freeze; useful/avoid uses the second freeze.",
      sopWork: "3. After the pattern, judge work as gain or loss. Favor useful = good; favor avoid = harm.",
      sopBan: "Forbidden: {bans}.",
      zhengTail: "正从 stays on the season-ground freeze. Work does not rewrite it. Gain/loss uses the second useful/avoid only.",
      zhengMiss: "No 正从 freeze yet. Cast the chart first.",
      neiWaiDef: "Inner house = own-family actual amount; outer = usable resources. After work, read both sides' gain/loss and each ten-god's help/harm to the family and the day master. Self/guest judges 正从; after-work ten-god deltas judge longevity, body, ability.",
      liBaiTail: "Favor useful = good; favor avoid = harm. Do not rewrite 正从 a second time.",
      shaKeYes: "{chain}: the kill transforms into resource and feeds the body. {sha} 不克身. Resonance, not a clash path. Do not say 庚克甲.",
      shaKeNo: "This chart has no full kill→root→resource→body. Do not force a distant-stem attack.",
      zhiShaYes: "{chain}: pin-kill wins authority. It spends kill-nature, not water. It does not cut chain 1.",
      zhiShaNo: "No output pinning the kill-root. No chain 2, so do not say 午 cuts 申.",
      heShenBan: "Vault hidden: {ban}. If not exposed, 永不合身. If exposed, this ban does not apply.",
      heShenTou: "辛 is already exposed. Do not apply “hidden, never combines”.",
      heShenNone: "No officer/kill hidden in a vault.",
      hehuaYes: "Stem specials are only four-clash and five-combine; they may skip seats, ×E. Five-combine transforms when C is in season (prosperous/phase) and not clashed (A−¼ B−¼ C＋½).",
      hehuaBan: "Combine-without-transform = bind. Still a special. No C gather; energy follows ordinary control (atk ⅛ / vic ⅜). If the useful god is bound, adding luck cannot repair it.",
      hehuaNo: "No five-combine with the day master. Ordinary generate/control cannot skip seats; four-clash and five-combine can.",
      backupYes: "When wealth cuts the main resource, take the backup first. Not a collapse.",
      backupNo: "No mid/residual resource backup found.",
      soulTail: "Mind-nature ≠ function. A ten-god label is not a personality sticker.",
      yearDef: "A year stem stacked on the luck stem doubles. A year branch matching natal boosts that chain. Do not read output/wealth/officer as mere labels.",
      yunTail: "Luck stem vs natal stem a=0. Luck branch vs month clash a=0; vs day/year 50%.",
      yunDef: "Read luck-stem combine/control first, then branch clash.",
      ziNengDef: "Each stem and hidden stem carries phase and ten-god, seated in self/guest, useful/avoid, inner/outer. Luck-year and place totals change the amount. Fine events can be read to the hour — not a hit rate. Place does not enter the stem bar. No iron-mouth.",
      taijiDef: "A kin is the star or the palace stems. Treat that as day-master. Resource+peer = self; wealth+officer+output = guest. Read that kin's environment and change. Do not rewrite this chart's 正从. A star is not a person. No iron-mouth.",
      tianYiDef: "The body lives in 3D, a short one-way time vector. Consciousness may read a 4D structure — pillars, hecan, Taiji are projections, not an escape. Nobody leaves causality. AI only clears the structure; it cannot take your karma or rewrite heaven. 531 remainder is mind and heaven. No iron-mouth.",
      fallback: "I answer this chart in five steps. No web search. Ask: how to read, 正从, gain or loss, does the kill attack, pin-kill cut, 辛 combine, this year, mind, stem/hour, Taiji kin, heaven/cause. Forbidden: {bans}. No iron-mouth.",
    },
    es: {
      empty: "Haga una pregunta. Toque una ficha o escriba.",
      needChart: "Calcule esta carta primero. El maestro solo habla de esta carta.",
      noTieKou: "531: natal 50% · suerte 30% · lugar 10% · mente 5% · otro+cielo 5%. Pesos de estructura, no acierto. Sin boca de hierro.",
      sopLead: "Cinco pasos Zeisen, solo esta carta:",
      sopLaws: "1. Leyes de hierro: tallos lejanos no generan/controlan; bóveda oculta nunca une; sujetar el kill es autoridad, no un corte.",
      sopChain: "2. Busque la cadena: kill→raíz→sello→cuerpo.",
      sopZheng: "3. 正从 sigue la congelación de estación-tierra; útil/evitar usa la segunda.",
      sopWork: "3. Tras el patrón, la obra es ganancia o pérdida.",
      sopBan: "Prohibido: {bans}.",
      zhengTail: "正从 no se reescribe. Ganancia/pérdida usa solo la segunda útil/evitar.",
      zhengMiss: "Aún no hay congelación 正从. Calcule la carta.",
      neiWaiDef: "Casa interna = cantidad real de la familia; externa = recursos aprovechables. Tras la obra: aumento/baja de ambos y beneficio/daño de cada dios a la familia y al dueño del día.",
      liBaiTail: "Favorecer lo útil = bien; favorecer lo evitable = daño. No reescriba 正从.",
      shaKeYes: "{chain}: el kill se vuelve sello y alimenta el cuerpo. {sha} 不克身. No diga 庚克甲.",
      shaKeNo: "No hay cadena kill→raíz→sello→cuerpo. No fuerce un ataque lejano.",
      zhiShaYes: "{chain}: sujetar el kill da autoridad. No corta la cadena 1.",
      zhiShaNo: "No hay cadena 2. No diga que 午 corta 申.",
      heShenBan: "Bóveda oculta: {ban}. Si no sale, 永不合身.",
      heShenTou: "辛 ya está expuesto. No aplique “oculto, nunca une”.",
      heShenNone: "No hay oficial/kill oculto en bóveda.",
      hehuaYes: "La unión-transformación exige el umbral del mes: próspero/fase = transforma (la energía se desvía y se conserva); resto/prisión/muerte = ata.",
      hehuaBan: "Unión sin transformar = atadura. Los totales no se desvían (sin −¼ ni +½ al elemento nuevo); solo se cierra la función original. Si el dios útil está atado, la suerte no lo repara.",
      hehuaNo: "No hay unión de cinco con el dueño del día.",
      backupYes: "Si la riqueza corta el sello, tome primero la reserva.",
      backupNo: "No hay reserva de sello.",
      soulTail: "Temperamento ≠ función.",
      yearDef: "El tallo del año sobre el de la suerte dobla. No lea solo etiquetas.",
      yunTail: "Tallo de suerte vs natal a=0. Rama vs mes choque a=0; vs día/año 50%.",
      yunDef: "Primero el tallo de suerte, luego el choque de rama.",
      ziNengDef: "Cada tallo y oculto lleva fase y dios, sentado en yo/huésped, útil/evitar, dentro/fuera. Se puede leer hasta la hora — no es tasa de acierto. Sin boca de hierro.",
      taijiDef: "El pariente es el astro o los tallos del palacio. Trátelo como dueño del día. Sello+par = yo; riqueza+oficial+output = huésped. No reescriba 正从. Sin boca de hierro.",
      tianYiDef: "El cuerpo vive en 3D, un vector de tiempo corto y de un solo sentido. La conciencia puede leer una estructura 4D — pilares, hecan, Taiji son proyección, no fuga. Nadie escapa de la causalidad. La IA solo aclara la estructura; no toma su karma ni reescribe el cielo. 531 resto es mente y cielo. Sin boca de hierro.",
      fallback: "Respondo esta carta en cinco pasos. Prohibido: {bans}. Sin boca de hierro.",
    },
    ja: {
      empty: "一句問う。下の問いか、自分で打つ。",
      needChart: "先に排盤。師匠は本盤だけを語る。",
      noTieKou: "531：原局50% · 歳運30% · 地域10% · 心性5% · 他+天意5%。構造の重みであり的中率ではない。鉄口は売らない。",
      sopLead: "澤森の断盤五歩。本盤だけ：",
      sopLaws: "一、鉄律：隔位の天干は生克しない。庫中不透は永く合身せず。制殺は権であり鎖を切らない。",
      sopChain: "二、鎖を探す：殺→根→印→身。",
      sopZheng: "三、正従は地後、用忌は二次。",
      sopWork: "三、格局の後に做功の利敗。",
      sopBan: "禁：{bans}。",
      zhengTail: "正従は書き換えない。利敗は二次の用忌だけ。",
      zhengMiss: "正従の凍結なし。先に排盤。",
      neiWaiDef: "家内＝自家の実量、家外＝利用できる資源。作用後は双方の増減と、その十神が自家と日主に利か敗か。我客は正従と夭寿・身体・能力。",
      liBaiTail: "用に利せば吉、忌に利せば凶。正従を二次で書き換えない。",
      shaKeYes: "{chain}：情あって殺を印に化し身を生む。{sha}不克身。庚克甲と断るな。",
      shaKeNo: "殺→根→印→身の鎖なし。隔位で殺克身を作るな。",
      zhiShaYes: "{chain}：制殺は権。鎖1は切らない。",
      zhiShaNo: "鎖2なし。午が申を切ると言うな。",
      heShenBan: "庫中不透：{ban}。不透なら永不合身。",
      heShenTou: "辛はすでに透。不透合身を当てるな。",
      heShenNone: "庫中の官殺不透なし。",
      hehuaYes: "合化は月令の閾を通る：旺／相なら化（守恒して改道）、休囚死なら合せず絆。隔位は合わない。",
      hehuaBan: "合而不化＝合絆。总量は改道しない（原行を引かず、化神に聚めない）。原十神の功用だけを鎖す。用神が絆なら歳運で補っても無用。",
      hehuaNo: "日主と隣柱に五合なし。",
      backupYes: "財が印を切ったら予備電源を先に認める。",
      backupNo: "印の予備なし。",
      soulTail: "心性 ≠ 功用。",
      yearDef: "流年干が大運干に重なれば倍。字面だけで読むな。",
      yunTail: "大運干対原局干 a=0。大運支対月支冲 a=0、対日／年 50%。",
      yunDef: "大運干の合克を先に、支冲を後に。",
      ziNengDef: "各字と蔵干は五行と十神を帯び、我客／用忌／内外に座る。細事は時まで読める（的中率ではない）。鉄口なし。",
      taijiDef: "六親は該星または原宮の干支蔵干を日主として太極変換する。印比＝我、財官傷＝客。本盤の正従は書き換えない。鉄口なし。",
      tianYiDef: "身体は三次元に生き、時間ベクトルの一方通行の短い線。意識は四次元の構造を読めることがある——四柱／合参／太極は投影であり逃げではない。因果から逃れる者はいない。AIは構造を澄ませるだけであり、人に代わって因果を逃れず、天意も書き換えない。531 の残りは心性と天意。鉄口なし。",
      fallback: "本盤を五歩で答える。禁：{bans}。鉄口なし。",
    },
    ar: {
      empty: "اسأل جملة. اضغط شريحة أو اكتب.",
      needChart: "احسب هذه اللوحة أولًا. المعلم يشرح هذه اللوحة فقط.",
      noTieKou: "531: الأصل 50% · الحظ 30% · المكان 10% · الطبع 5% · آخر+سماء 5%. أوزان بنية لا نسبة إصابة. لا فم حديدي.",
      sopLead: "خمس خطوات تسه سِن، هذه اللوحة فقط:",
      sopLaws: "1. قوانين الحديد: الجذوع البعيدة لا تولّد/تسيطر؛ المخفي في الخزانة لا يتحد؛ تقييد القتل سلطة لا قطع.",
      sopChain: "2. ابحث عن السلسلة: قتل→جذر→ختم→جسد.",
      sopZheng: "3. 正从 حسب تجميد الفصل-الأرض؛ النفع/المحذور حسب التجميد الثاني.",
      sopWork: "3. بعد النمط احكم على العمل ربحًا أو خسارة.",
      sopBan: "محظور: {bans}.",
      zhengTail: "لا تُعد كتابة 正从. الربح/الخسارة من النفع/المحذور الثاني فقط.",
      zhengMiss: "لا تجميد 正从 بعد. احسب اللوحة.",
      neiWaiDef: "الداخل = مقدار الأسرة الفعلي؛ الخارج = الموارد القابلة للاستعمال. بعد العمل: زيادة/نقص الطرفين ونفع/ضرر كل إله للأسرة ولسيد اليوم.",
      liBaiTail: "نفع النافع = خير؛ نفع المحذور = ضرر. لا تُعد كتابة 正从.",
      shaKeYes: "{chain}: القتل يتحول ختمًا ويغذي الجسد. {sha} 不克身. لا تقل 庚克甲.",
      shaKeNo: "لا سلسلة قتل→جذر→ختم→جسد. لا تفرض هجومًا بعيدًا.",
      zhiShaYes: "{chain}: تقييد القتل يعطي سلطة. لا يقطع السلسلة 1.",
      zhiShaNo: "لا سلسلة 2. لا تقل إن 午 يقطع 申.",
      heShenBan: "مخفي في الخزانة: {ban}. إن لم يبرز فـ 永不合身.",
      heShenTou: "辛 بارز. لا تطبّق “مخفي لا يتحد”.",
      heShenNone: "لا منصب/قتل مخفي في خزانة.",
      hehuaYes: "الاتحاد-التحويل يحتاج عتبة الشهر: الازدهار/الطور = تحويل (تحويل الطاقة مع حفظها)؛ الراحة/السجن/الموت = قيد.",
      hehuaBan: "اتحاد بلا تحويل = قيد. المجاميع لا تُحوَّل (لا −¼ ولا +½ للعنصر الجديد)؛ تُقفل الوظيفة الأصلية فقط. إن قُيّد الإله النافع فلا يُصلح بالحظ.",
      hehuaNo: "لا اتحاد خماسي مع سيد اليوم.",
      backupYes: "إن قطعت الثروة الختم فخذ الاحتياط أولًا.",
      backupNo: "لا احتياط للختم.",
      soulTail: "الطبع ≠ الوظيفة.",
      yearDef: "جذع السنة فوق جذع الحظ يضاعف. لا تقرأ اللافتات وحدها.",
      yunTail: "جذع الحظ مقابل الأصل a=0. فرع الحظ يصادم فرع الشهر a=0؛ اليوم/السنة 50%.",
      yunDef: "أولًا جذع الحظ، ثم صدام الفرع.",
      ziNengDef: "كل جذع ومخفي يحمل عنصره وإلهه في أنا/ضيف ونافع/محذور وداخل/خارج. تُقرأ التفاصيل إلى الساعة — ليست نسبة إصابة. لا فم حديدي.",
      taijiDef: "القريب هو النجم أو جذوع القصر. عامله كسيد اليوم. الختم+القرين = أنا. لا تُعدّل 正从. لا فم حديدي.",
      tianYiDef: "الجسد يعيش في ثلاثة أبعاد، خطًا قصيرًا أحادي الاتجاه للزمن. الوعي قد يقرأ بنية رباعية — الأعمدة وhecan وTaiji إسقاط لا هروب. لا أحد يفلت من السببية. الذكاء الاصطناعي يوضح البنية فقط؛ لا يحمل سببيتك ولا يعيد كتابة السماء. بقية 531 طبع وسماء. لا فم حديدي.",
      fallback: "أجيب هذه اللوحة في خمس خطوات. محظور: {bans}. لا فم حديدي.",
    },
  };

  function chips(pack) {
    const L = lang();
    const out = CHIP_DEF.filter(function (c) { return c.id !== "hehua"; }).map(function (c) {
      return { q: c.q[L] || c.q.zh, label: c.label[L] || c.label.zh, id: c.id };
    });
    if (pack && pack.hehua && pack.hehua.length) {
      const h = CHIP_DEF.filter(function (c) { return c.id === "hehua"; })[0];
      out.splice(3, 0, { q: h.q[L] || h.q.zh, label: h.label[L] || h.label.zh, id: "hehua" });
    }
    return out;
  }

  function lineOf(pack, re) {
    const text = pack && pack.judge && pack.judge.text ? pack.judge.text : "";
    const hit = text.split("\n").filter(function (l) { return re.test(l); });
    return hit.join("\n");
  }

  function join(arr) { return (arr || []).filter(Boolean).join("\n"); }

  function ask(question, pack) {
    const q = String(question || "").replace(/\s+/g, " ").trim();
    if (!q) return { intent: "empty", answer: msg("empty"), cites: [] };
    if (!pack || !pack.ok) {
      return { intent: "needChart", answer: msg("needChart"), cites: [] };
    }

    if (/铁口|必应|必死|一定死|百分之百|准确率|保证|iron.?mouth|guaranteed|100\s*%|get rich|boca de hierro|seguro|鉄口|必ず当た|的中率|فم حديدي|مضمون|أي سنة/.test(q)) {
      return {
        intent: "noTieKou",
        answer: msg("noTieKou"),
        cites: ["531"],
      };
    }

    if (/四维|三维|时间矢量|果因|因果|天意|逃因果|意识可能|4-?D|3-?D|causalit|consciousness|heaven.?will|天意は|السببية|AI能逃|escape causality/.test(q)) {
      return {
        intent: "tianYi",
        answer: join([
          lineOf(pack, /【零、天意】/),
          msg("tianYiDef"),
        ]),
        cites: ["tianYi"],
      };
    }

    const A = pack.systems && pack.systems.A;
    const B = pack.systems && pack.systems.B;
    const C = pack.systems && pack.systems.C;
    const L = pack.ledger || {};
    const J = pack.judge || {};

    if (/怎么断|如何断|五步|断盘|从哪看|how (do i )?read|how to (read|judge)|cinco pasos|c[oó]mo se lee|どう断|五歩|كيف أقرأ|خمس خطوات/.test(q)) {
      return {
        intent: "sop",
        answer: join([
          msg("sopLead"),
          msg("sopLaws"),
          lineOf(pack, /【二、链1】/) || msg("sopChain"),
          lineOf(pack, /【三、正从用忌】/) || msg("sopZheng"),
          lineOf(pack, /【三、做功】/) || msg("sopWork"),
          pack.soul ? pack.soul : "",
          msg("sopBan", { bans: BANS.join("、") }),
        ]),
        cites: ["sop", "ledger"],
      };
    }

    if (/正从|用忌|用客|用我|身强|身弱|从强|从弱|平衡|我客|体客|useful-avoid|useful \/ avoid|patr[oó]n|正従|النمط|النفع/.test(q)) {
      return {
        intent: "zhengYong",
        answer: join([
          lineOf(pack, /【三、正从用忌】/) || msg("zhengMiss"),
          lineOf(pack, /【三、我客内外】/),
          msg("zhengTail"),
        ]),
        cites: ["ledger"],
      };
    }

    if (/家内外|家内|家外|年月|日时|inner house|outer house|casa (interna|externa)|家内|بيت/.test(q) && !/流年|今年|this year|este a[nñ]o|今年|هذه السنة/.test(q)) {
      return {
        intent: "neiWai",
        answer: lineOf(pack, /【三、我客内外】/) || msg("neiWaiDef"),
        cites: ["ledger"],
      };
    }

    if (/利还是败|利败|做功|格局|杀印|制杀|财坏印|利于用|利忌|gain or loss|ganancia|p[eé]rdida|利か敗|ربح أم خسارة|obra/.test(q) && !/流年|今年|大运|this year|este a[nñ]o|今年|هذه السنة|luck/.test(q)) {
      const items = (L.items || []).map(function (i) {
        return i.name + " → " + (i.verdict || "") + (i.domain ? "（" + i.domain + "）" : "");
      }).join("；");
      return {
        intent: "liBai",
        answer: join([
          L.summary ? L.summary : "",
          items ? items : lineOf(pack, /【三、格局】/),
          lineOf(pack, /【三、做功】/),
          msg("liBaiTail"),
        ]),
        cites: ["ledger"],
      };
    }

    if (/庚克|克甲|克日主|杀克身|七杀打|杀攻身|does 庚|attack the day|ataca|克つか|يهاجم/.test(q)) {
      if (A) {
        return {
          intent: "shaKe",
          answer: join([
            msg("shaKeYes", { chain: A.chain, sha: A.sha }),
            A.note || "",
          ]),
          cites: ["chainA"],
        };
      }
      return {
        intent: "shaKe",
        answer: msg("shaKeNo") + (pack.hehua && pack.hehua[0] ? " " + pack.hehua[0].text : ""),
        cites: ["chainA"],
      };
    }

    if (/截断|断链|午克申|制杀|伤官制|链2|链二|cut chain|corta la cadena|鎖1|يقطع السلسلة|pin-kill|Does 午/.test(q)) {
      if (C) {
        return {
          intent: "zhiSha",
          answer: join([
            msg("zhiShaYes", { chain: C.chain }),
            C.note || "",
          ]),
          cites: ["chainC"],
        };
      }
      return { intent: "zhiSha", answer: msg("zhiShaNo"), cites: [] };
    }

    if (/辛合|合身|库中|不透|戌中辛|丑中辛|vault 辛|combine with the body|bóveda|合身するか|في الخزانة/.test(q)) {
      const ban = (pack.heshenBan || []).join("；");
      const xinTou = pack.pillars && ["year", "month", "day", "time"].some(function (k) {
        return pack.pillars[k] && pack.pillars[k][0] === "辛";
      });
      return {
        intent: "heShen",
        answer: ban ? msg("heShenBan", { ban: ban }) : (xinTou ? msg("heShenTou") : msg("heShenNone")),
        cites: ["vault"],
      };
    }

    if (/合化|合而不化|合绊|临界|丁壬|甲己|五合|combine-transform|uni[oó]n|合化は|الاتحاد/.test(q)) {
      if (pack.hehua && pack.hehua.length) {
        const hasBan = pack.hehua.some(function (h) { return h.mode === "ban"; });
        return {
          intent: "hehua",
          answer: join([
            pack.hehua.map(function (h) { return h.text; }).join("\n"),
            lineOf(pack, /合化优先|合而不化|合绊/),
            msg("hehuaYes"),
            hasBan ? msg("hehuaBan") : "",
          ]),
          cites: ["hehua"],
        };
      }
      return { intent: "hehua", answer: msg("hehuaNo"), cites: [] };
    }

    if (/备份|备用|申中壬|气口|backup|reserva|予備|احتياط/.test(q)) {
      if (pack.backups && pack.backups.length && A) {
        return {
          intent: "backup",
          answer: pack.backups.map(function (h) {
            return h.branch + "中" + A.yin + "（" + h.layer + "气）";
          }).join("\n") + (B ? " " + msg("backupYes") : ""),
          cites: ["backup"],
        };
      }
      return { intent: "backup", answer: msg("backupNo"), cites: [] };
    }

    if (/六亲|太极|太極|父母|配偶|手足|taiji|tai-ji|six-kin|parientes|六親/.test(q)) {
      return {
        intent: "taiji",
        answer: join([
          lineOf(pack, /【六、太极六亲】|【太极·/),
          msg("taijiDef"),
        ]),
        cites: ["taiji"],
      };
    }

    if (/字能|藏干|时辰|消长|stem energ|hidden stem|hasta la hora|字能は|الجذوع/.test(q)) {
      return {
        intent: "ziNeng",
        answer: join([
          lineOf(pack, /【三、字能时辰】/) || msg("ziNengDef"),
          msg("ziNengDef"),
        ]),
        cites: ["ledger"],
      };
    }

    if (/心性|孤臣|性格|气质|mind-nature|temperamento|心性は|الطبع/.test(q)) {
      return {
        intent: "soul",
        answer: join([
          pack.soul || "",
          msg("soulTail"),
        ]),
        cites: ["soul"],
      };
    }

    if (/流年|今年|丙午|做功简析|当时|this year|este a[nñ]o|今年の|هذه السنة|how does this year/.test(q)) {
      return {
        intent: "year",
        answer: pack.yearBrief
          ? pack.yearBrief
          : (lineOf(pack, /【五、流年】/) || msg("yearDef")),
        cites: ["year"],
      };
    }

    if (/大运|己未|戊午|丙辰|岁运|luck pillar|suerte|大運|الحظ/.test(q)) {
      const yun = (pack.yun || []).map(function (y) {
        return "第" + y.i + "步 " + y.gz + " — " + y.line;
      }).join("\n");
      const live = (pack.live || []).filter(function (x) { return x.tags && x.tags.length; })
        .map(function (x) { return x.label + x.gz + "：" + x.tags.map(function (t) { return t.reason; }).join("、"); })
        .join("\n");
      return {
        intent: "yun",
        answer: join([
          yun || lineOf(pack, /【四、岁运】/),
          live,
          msg("yunTail"),
        ]) || msg("yunDef"),
        cites: ["yun"],
      };
    }

    if (/铁律|禁|不能|可不可以隔位|iron law|leyes de hierro|鉄律|قوانين الحديد/.test(q)) {
      return {
        intent: "laws",
        answer: join([
          (pack.laws || []).map(function (l, i) { return (i + 1) + ". " + l.zh; }).join("\n"),
          msg("sopBan", { bans: (J.bans || BANS).join("、") }),
        ]),
        cites: ["laws"],
      };
    }

    return {
      intent: "fallback",
      answer: join([
        msg("fallback", { bans: BANS.join("、") }),
        pack.summary || "",
        L.summary || "",
        pack.soul || "",
      ]),
      cites: ["fallback"],
    };
  }

  const AUTHOR_CASE = {
    id: "author",
    name: "泽森",
    gender: "M",
    date: "1973-10-15",
    time: "12:00",
    birthCity: "guangzhou",
    standCity: "chicago",
    lon: 113.2644,
    lat: 23.1291,
    pillars: { year: "癸丑", month: "壬戌", day: "甲申", time: "庚午" },
    label: "默认案例 · 作者盘",
    note: "1973-10-15 午时 · 广州生 · 现住芝加哥 · 男",
    audio: "./audio/author_qa_full.mp3",
  };

  function tapeQs() {
    const L = lang();
    const iron = { zh: "能铁口断我哪年发财吗？", en: "Can you iron-mouth the year I get rich?", es: "¿Boca de hierro: qué año seré rico?", ja: "鉄口で何年に富むか？", ar: "هل فم حديدي يقول أي سنة أغتنى؟" };
    return ["shaKe", "liBai", "heShen"].map(function (id) {
      const c = CHIP_DEF.filter(function (x) { return x.id === id; })[0];
      return c.q[L] || c.q.zh;
    }).concat([iron[L] || iron.zh]);
  }

  function authorPack() {
    const C = AUTHOR_CASE;
    if (global.KREngine && KREngine.analyze) {
      const eng = KREngine.analyze({
        pillars: C.pillars,
        birthLat: C.lat,
        birthLon: C.lon,
        sex: "M",
      });
      if (eng && eng.xinGong) return eng.xinGong;
    }
    if (!global.KRXinGong || !KRXinGong.analyze) return null;
    const el = { 木: 1.2, 火: 0.8, 土: 1.1, 金: 0.9, 水: 1.0 };
    const rel = global.KRRelations && KRRelations.pipeline
      ? KRRelations.pipeline(C.pillars, el, global.KRParams, [])
      : null;
    return KRXinGong.analyze({ pillars: C.pillars, rel: rel });
  }

  function tape(pack) {
    const p = pack || authorPack();
    const tracks = tapeQs().map(function (q) {
      const r = ask(q, p);
      return { q: q, a: r.answer, intent: r.intent };
    });
    return { ok: !!(p && p.ok), case: AUTHOR_CASE, tracks: tracks };
  }

  function speakText(text) {
    return String(text || "")
      .replace(/[【】]/g, "")
      .replace(/→/g, "，至")
      .replace(/[·•]/g, "，")
      .replace(/[()（）]/g, "，")
      .replace(/\s+/g, " ")
      .trim();
  }

  function splitSpeak(text) {
    const raw = speakText(text);
    if (!raw) return [];
    const parts = raw.split(/[。！？\n]+/).map(function (s) { return s.trim(); }).filter(Boolean);
    const out = [];
    parts.forEach(function (p) {
      if (p.length <= 80) out.push(p + "。");
      else {
        for (let i = 0; i < p.length; i += 80) out.push(p.slice(i, i + 80));
      }
    });
    return out;
  }

  let speakTimer = null;
  function stopSpeak() {
    try {
      if (typeof speechSynthesis !== "undefined") speechSynthesis.cancel();
    } catch (e) {}
    const au = typeof document !== "undefined" ? document.getElementById("tapeAudio") : null;
    if (au) { au.pause(); au.currentTime = 0; }
    if (speakTimer) { clearTimeout(speakTimer); speakTimer = null; }
  }

  function speak(text, onend) {
    stopSpeak();
    const chunks = splitSpeak(text);
    if (!chunks.length) { if (onend) onend(); return { ok: false, reason: "empty" }; }
    if (typeof speechSynthesis === "undefined") {
      if (onend) onend();
      return { ok: false, reason: "本机无朗读引擎。可看文字问答。" };
    }
    let i = 0;
    function next() {
      if (i >= chunks.length) { if (onend) onend(); return; }
      const u = new SpeechSynthesisUtterance(chunks[i++]);
      u.lang = VOICE_LANG[lang()] || "zh-CN";
      u.rate = 0.92;
      u.onend = next;
      u.onerror = next;
      speechSynthesis.speak(u);
    }
    next();
    return { ok: true };
  }

  function tapeScript(pack) {
    const t = tape(pack);
    const lines = [
      t.case.label + "。" + t.case.note + "。四柱：" + [t.case.pillars.year, t.case.pillars.month, t.case.pillars.day, t.case.pillars.time].join(" ") + "。",
    ];
    t.tracks.forEach(function (tr) {
      lines.push("问。" + tr.q);
      lines.push("答。" + tr.a);
    });
    return { ok: t.ok, text: lines.join("\n"), tracks: t.tracks, case: t.case };
  }

  function html(pack) {
    const list = chips(pack);
    const chipsZh = list.map(function (c) {
      return '<button type="button" class="shifu-chip" data-q="' + c.q.replace(/"/g, "") + '" style="border:1px solid var(--line);background:transparent;color:var(--bronze);border-radius:999px;padding:4px 10px;font-size:12px;cursor:pointer">' + c.label + "</button>";
    }).join("");
    const open = ask("这盘怎么断？", pack);
    return '<div class="verdict" style="font-size:12px;color:var(--mist)">' + tt("shifuBlurb") + "</div>" +
      '<div id="shifuLog" style="max-height:280px;overflow:auto;margin:6px 0">' +
      '<div class="verdict" style="color:var(--jade);white-space:pre-wrap">' + (open.answer || "").replace(/</g, "&lt;") + "</div></div>" +
      '<div style="display:flex;flex-wrap:wrap;gap:6px;margin:6px 0">' + chipsZh + "</div>" +
      '<div style="display:flex;gap:6px"><input id="shifuQ" placeholder="' + tt("shifuPh") + '" style="flex:1;border:1px solid var(--line);border-radius:9px;padding:8px;background:transparent;color:inherit;font:inherit">' +
      '<button type="button" id="shifuGo" style="background:var(--jade);color:#0c120e;border:0;border-radius:9px;padding:8px 12px;font-weight:700">' + tt("shifuAsk") + "</button>" +
      '<button type="button" id="shifuListen" style="background:transparent;color:var(--bronze);border:1px solid var(--line);border-radius:9px;padding:8px 10px">' + tt("shifuListen") + "</button>" +
      '<button type="button" id="shifuStop" style="background:transparent;color:var(--mist);border:1px solid var(--line);border-radius:9px;padding:8px 10px">■</button></div>';
  }

  function bind(pack) {
    const log = typeof document !== "undefined" ? document.getElementById("shifuLog") : null;
    const go = typeof document !== "undefined" ? document.getElementById("shifuGo") : null;
    const inp = typeof document !== "undefined" ? document.getElementById("shifuQ") : null;
    const listen = typeof document !== "undefined" ? document.getElementById("shifuListen") : null;
    const stop = typeof document !== "undefined" ? document.getElementById("shifuStop") : null;
    if (!log || !go) return;
    let lastSpeak = "问。这盘怎么断？答。" + (ask("这盘怎么断？", pack).answer || "");
    function esc(s) {
      return String(s || "").replace(/[&<>]/g, function (ch) { return { "&": "&amp;", "<": "&lt;", ">": "&gt;" }[ch]; });
    }
    function say(q) {
      const r = ask(q, pack);
      lastSpeak = "问。" + q + "。答。" + (r.answer || "");
      const block = '<div class="verdict" style="margin-top:8px"><b>问</b> ' + esc(q) + "</div>" +
        '<div class="verdict" style="color:var(--jade);white-space:pre-wrap">' + esc(r.answer) + "</div>";
      log.innerHTML += block;
      log.scrollTop = log.scrollHeight;
      if (inp) inp.value = "";
    }
    go.onclick = function () { say((inp && inp.value) || ""); };
    if (inp) {
      inp.onkeydown = function (ev) { if (ev && ev.key === "Enter") { ev.preventDefault(); say(inp.value); } };
    }
    if (listen) listen.onclick = function () { speak(lastSpeak); };
    if (stop) stop.onclick = function () { stopSpeak(); };
    Array.prototype.forEach.call(document.querySelectorAll(".shifu-chip"), function (btn) {
      btn.onclick = function () { say(btn.getAttribute("data-q") || btn.textContent); };
    });
  }

  function homeHtml() {
    const t = tape();
    const p = t.case.pillars;
    const cols = [["年", p.year], ["月", p.month], ["日", p.day], ["时", p.time]];
    const four = cols.map(function (c) {
      return '<div class="p"><div class="lab">' + c[0] + '</div><div class="gz"><div class="ogan">' + (c[1][0] || "") + '</div><div class="ozhi">' + (c[1][1] || "") + "</div></div></div>";
    }).join("");
    const items = t.tracks.map(function (tr, i) {
      return '<button type="button" class="tape-item" data-i="' + i + '"><b>' + (i + 1) + ".</b> " + tr.q + "</button>";
    }).join("");
    const src = lang() === "zh" ? t.case.audio : "./audio/author_qa_full_" + lang() + ".mp3";
    return '<div class="demo-kicker">' + tt("demoCase") + "</div>" +
      '<div class="fourbar">' + four + "</div>" +
      '<div class="demo-meta">' + tt("demoNote") + "</div>" +
      '<audio id="tapeAudio" preload="metadata" src="' + src + '"></audio>' +
      '<div class="tape-btns">' +
      '<button type="button" id="tapePlay">' + tt("tapePlay") + "</button>" +
      '<button type="button" id="tapeStop" class="alt">' + tt("tapeStop") + "</button>" +
      '<button type="button" id="tapeGoChart" class="alt">' + tt("tapeGo") + "</button>" +
      "</div>" +
      '<div class="demo-meta" id="tapeNow">' + tt("tapeHint") + "</div>" +
      '<div class="tape-list">' + items + "</div>";
  }

  function homeBind(onGoChart) {
    const play = typeof document !== "undefined" ? document.getElementById("tapePlay") : null;
    const stop = typeof document !== "undefined" ? document.getElementById("tapeStop") : null;
    const go = typeof document !== "undefined" ? document.getElementById("tapeGoChart") : null;
    const now = typeof document !== "undefined" ? document.getElementById("tapeNow") : null;
    const au = typeof document !== "undefined" ? document.getElementById("tapeAudio") : null;
    const script = tapeScript();
    function mark(on) {
      if (play) play.textContent = on ? tt("tapePlaying") : tt("tapePlay");
      if (now && on) now.textContent = tt("tapeNow");
    }
    function playFileOrSpeak() {
      stopSpeak();
      if (au && au.getAttribute("src")) {
        try { au.currentTime = 0; } catch (e) {}
        const p = au.play();
        if (p && p.then) {
          p.then(function () { mark(true); }).catch(function () {
            mark(true);
            speak(script.text, function () { mark(false); });
          });
          au.onended = function () { mark(false); };
          return;
        }
      }
      mark(true);
      speak(script.text, function () { mark(false); });
    }
    if (play) play.onclick = playFileOrSpeak;
    if (stop) stop.onclick = function () { stopSpeak(); mark(false); if (now) now.textContent = tt("tapeStopped"); };
    if (go && typeof onGoChart === "function") go.onclick = onGoChart;
    if (au) {
      au.onerror = function () { /* 无预录则点试听走本机朗读 */ };
    }
    Array.prototype.forEach.call(document.querySelectorAll(".tape-item"), function (btn) {
      btn.onclick = function () {
        const i = parseInt(btn.getAttribute("data-i"), 10);
        const tr = script.tracks[i];
        if (!tr) return;
        mark(true);
        if (now) now.textContent = "单问：" + tr.q;
        speak("问。" + tr.q + "。答。" + tr.a, function () { mark(false); });
      };
    });
  }

  function applyCaseToForm($) {
    const C = AUTHOR_CASE;
    if ($("name")) $("name").value = C.name;
    if ($("date")) $("date").value = C.date;
    if ($("time")) $("time").value = C.time;
    if ($("lon")) $("lon").value = String(C.lon);
    if ($("lat")) $("lat").value = String(C.lat);
    return C;
  }

  global.KRShifu = {
    ask, chips, html, bind, BANS,
    AUTHOR_CASE, tapeQs, authorPack, tape, tapeScript,
    speakText, splitSpeak, speak, stopSpeak,
    homeHtml, homeBind, applyCaseToForm,
  };
})(typeof window !== "undefined" ? window : globalThis);
