{"id":2332,"date":"2026-07-04T10:51:32","date_gmt":"2026-07-04T02:51:32","guid":{"rendered":"https:\/\/aifed.now\/conference\/?page_id=2332"},"modified":"2026-07-04T10:51:32","modified_gmt":"2026-07-04T02:51:32","slug":"%e8%88%87%e6%9c%83%e8%80%85%e6%8f%90%e5%95%8f%e4%ba%92%e5%8b%95%e5%b9%b3%e5%8f%b0","status":"publish","type":"page","link":"https:\/\/aifed.now\/conference\/?page_id=2332","title":{"rendered":"\u8207\u6703\u8005\u63d0\u554f\u4e92\u52d5\u5e73\u53f0"},"content":{"rendered":"<!DOCTYPE html>\r\n<html lang=\"zh-Hant\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n<title>AI \u6559\u80b2\u5e74\u6703 \u2014 \u73fe\u5834\u63d0\u554f\u5e73\u53f0<\/title>\r\n\r\n<!-- Noto Sans TC \u5b57\u9ad4 + Material Icons -->\r\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\r\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Noto+Sans+TC:wght@400;500;700;900&display=swap\" rel=\"stylesheet\">\r\n<link href=\"https:\/\/fonts.googleapis.com\/icon?family=Material+Icons\" rel=\"stylesheet\">\r\n\r\n<style>\r\n\/* ============== \u6de1\u85cd\u8272\u7cfb \u00b7 \u6e05\u65b0\u73fe\u4ee3\u98a8 ============== *\/\r\n:root{\r\n  \/* \u6de1\u85cd\u4e3b\u8272\u76e4 *\/\r\n  --primary:#5B9BD5;          \/* \u4e3b\u85cd *\/\r\n  --primary-dark:#3D7FC0;\r\n  --secondary:#6FCBD8;        \/* \u9752\u85cd *\/\r\n  --accent:#7FC6E8;           \/* \u5929\u7a7a\u85cd *\/\r\n  --accent-deep:#4AA3DC;      \/* \u4eae\u85cd *\/\r\n  --lemon:#BFE3F5;            \/* \u6dfa\u85cd(\u66ff\u4ee3\u539f\u9d5d\u9ec3\u9ede\u7db4) *\/\r\n  --sky:#9BD3F0;\r\n  --warn:#F0909C;             \/* \u8393\u679c\u7d05(\u67d4) *\/\r\n  --warn-text:#D6455A;\r\n  --ok:#7FCBA8;               \/* \u8584\u8377\u7da0 *\/\r\n  --ok-text:#2F9E68;\r\n  --like:#8FCBEF;             \/* \u9ede\u8b9a\u85cd *\/\r\n  --like-deep:#3D8FD0;\r\n\r\n  --bg:#F2F8FD;\r\n  --surface:#FFFFFF;\r\n  --surface-tint:#F4FAFE;     \/* \u5e36\u85cd\u7684\u5361\u7247\u5e95 *\/\r\n  --on-surface:#234567;       \/* \u67d4\u548c\u6df1\u85cd\u7070\u6587\u5b57 *\/\r\n  --muted:#7E96AE;\r\n  --border:#DCEDF9;\r\n  --shadow:0 4px 14px rgba(91,155,213,.16), 0 12px 40px rgba(74,163,220,.10);\r\n  --shadow-lg:0 10px 30px rgba(91,155,213,.25), 0 20px 60px rgba(74,163,220,.15);\r\n  --radius:22px;\r\n\r\n  \/* \u62db\u724c\u6f38\u5c64 *\/\r\n  --grad-main:linear-gradient(120deg,#5B9BD5 0%,#7FC6E8 55%,#BFE3F5 110%);\r\n  --grad-accent:linear-gradient(120deg,#7FC6E8,#4AA3DC);\r\n  --grad-mint:linear-gradient(120deg,#6FCBD8,#9BD3F0);\r\n}\r\n*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}\r\nhtml,body{margin:0;padding:0}\r\nbody{\r\n  font-family:\"Noto Sans TC\",-apple-system,\"Segoe UI\",Roboto,\"PingFang TC\",\"Microsoft JhengHei\",sans-serif;\r\n  color:var(--on-surface);line-height:1.65;\r\n  padding-bottom:90px;\r\n  \/* \u6e05\u65b0\u6de1\u85cd\u67d4\u5149\u80cc\u666f *\/\r\n  background:\r\n    radial-gradient(1100px 600px at 8% -5%, rgba(127,198,232,.30), transparent 60%),\r\n    radial-gradient(900px 600px at 105% 5%, rgba(91,155,213,.28), transparent 55%),\r\n    radial-gradient(800px 700px at 50% 115%, rgba(111,203,216,.22), transparent 55%),\r\n    linear-gradient(180deg,#F2F8FD, #EAF4FC 60%, #F0F7FD);\r\n  background-attachment:fixed;\r\n}\r\nh1,h2,h3{margin:0 0 .4em;font-weight:700}\r\na{color:var(--primary)}\r\nbutton{font-family:inherit;cursor:pointer;border:none;outline:none}\r\n.material-icons{vertical-align:middle;font-size:20px}\r\n\r\n\/* ---------- \u9802\u90e8\u6a19\u984c\u5217 ---------- *\/\r\nheader.appbar{\r\n  position:sticky;top:0;z-index:50;\r\n  background:var(--grad-main);\r\n  color:#fff;padding:18px 20px;\r\n  box-shadow:0 8px 28px rgba(91,155,213,.40);\r\n  display:flex;align-items:center;gap:14px;\r\n  border-bottom:3px solid rgba(255,255,255,.5);\r\n}\r\nheader.appbar .logo{\r\n  width:50px;height:50px;border-radius:16px;\r\n  background:rgba(255,255,255,.30);backdrop-filter:blur(6px);\r\n  display:flex;align-items:center;justify-content:center;font-size:28px;flex:0 0 auto;\r\n  box-shadow:inset 0 0 0 2px rgba(255,255,255,.5), 0 4px 12px rgba(0,0,0,.12);\r\n}\r\nheader.appbar .title{font-size:1.35rem;font-weight:900;letter-spacing:.5px;line-height:1.2;\r\n  text-shadow:0 2px 6px rgba(122,76,160,.35)}\r\nheader.appbar .subtitle{font-size:.74rem;opacity:.95;font-weight:500}\r\n\r\n\/* ---------- \u53f3\u4e0a\u61f8\u6d6e\u6309\u9215 ---------- *\/\r\n.fab-group{\r\n  position:fixed;top:16px;right:16px;z-index:60;display:flex;gap:8px;\r\n}\r\n.fab-group button{\r\n  background:rgba(255,255,255,.92);color:var(--primary-dark);font-weight:700;\r\n  padding:9px 15px;border-radius:30px;\r\n  box-shadow:0 6px 16px rgba(91,155,213,.35);backdrop-filter:blur(4px);\r\n  font-size:.82rem;display:flex;align-items:center;gap:5px;\r\n  transition:transform .15s,box-shadow .15s;border:1.5px solid rgba(255,255,255,.8);\r\n}\r\n.fab-group button:hover{transform:translateY(-3px);box-shadow:0 10px 22px rgba(91,155,213,.5)}\r\n.fab-group button.admin{background:var(--grad-accent);color:#fff;border-color:rgba(255,255,255,.6)}\r\n\/* \u5f8c\u53f0\u6309\u9215\uff1a\u53ea\u7559\u5716\u793a\u3001\u7e2e\u5c0f\u3001\u964d\u4f4e\u5b58\u5728\u611f *\/\r\n.fab-group button.admin-mini{padding:7px;width:34px;height:34px;justify-content:center;\r\n  background:rgba(255,255,255,.55);color:var(--primary-dark);opacity:.6;\r\n  box-shadow:0 2px 6px rgba(91,155,213,.25)}\r\n.fab-group button.admin-mini:hover{opacity:1;transform:translateY(-2px)}\r\n.fab-group button.admin-mini .material-icons{font-size:19px}\r\n\r\n\/* ---------- \u5bb9\u5668 ---------- *\/\r\n.container{max-width:880px;margin:0 auto;padding:20px}\r\n\r\n\/* ---------- \u5361\u7247 ---------- *\/\r\n.card{\r\n  background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow);\r\n  padding:22px;margin-bottom:18px;border:1.5px solid var(--border);\r\n  position:relative;overflow:hidden;\r\n}\r\n.card::before{content:\"\";position:absolute;top:0;left:0;right:0;height:5px;background:var(--grad-main)}\r\n\r\n\/* ---------- \u5f15\u5c0e\u5340 ---------- *\/\r\n.welcome{\r\n  text-align:center;padding:40px 22px;background:var(--surface);border-radius:var(--radius);\r\n  box-shadow:var(--shadow);margin-bottom:18px;\r\n}\r\n.welcome .big-icon{font-size:72px;background:var(--grad-main);-webkit-background-clip:text;\r\n  background-clip:text;-webkit-text-fill-color:transparent}\r\n.welcome h2{font-size:1.6rem}\r\n.welcome p{color:var(--muted);max-width:520px;margin:8px auto 22px}\r\n.welcome .btns{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}\r\n.btn{\r\n  padding:14px 28px;border-radius:30px;font-weight:700;font-size:1rem;color:#fff;\r\n  display:inline-flex;align-items:center;gap:8px;transition:transform .15s,box-shadow .15s,filter .15s;\r\n  box-shadow:0 6px 16px rgba(91,155,213,.28);\r\n}\r\n.btn:hover{transform:translateY(-3px);filter:brightness(1.04);box-shadow:0 10px 24px rgba(91,155,213,.4)}\r\n.btn-primary{background:var(--grad-main)}\r\n.btn-secondary{background:var(--grad-mint);color:#1d5e54}\r\n.btn-accent{background:var(--grad-accent)}\r\n.btn-warn{background:linear-gradient(120deg,#F8A5B5,#F08FA0);color:#fff}\r\n.btn-ghost{background:#E6F2FB;color:var(--primary-dark);box-shadow:none}\r\n.btn-ghost:hover{background:#D5EAF8;box-shadow:0 4px 12px rgba(91,155,213,.2)}\r\n.btn-block{width:100%;justify-content:center}\r\n.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}\r\n\r\n\/* ---------- \u5206\u9801 Tabs ---------- *\/\r\n.tabs{display:flex;background:rgba(255,255,255,.7);backdrop-filter:blur(6px);border-radius:30px;padding:6px;\r\n  box-shadow:var(--shadow);margin-bottom:16px;border:1.5px solid rgba(255,255,255,.8)}\r\n.tabs button{\r\n  flex:1;padding:11px;border-radius:24px;background:transparent;color:var(--muted);\r\n  font-weight:700;font-size:.95rem;display:flex;align-items:center;justify-content:center;gap:6px;\r\n  transition:.25s;\r\n}\r\n.tabs button.active{background:var(--grad-main);color:#fff;box-shadow:0 6px 16px rgba(91,155,213,.45)}\r\n\r\n\/* ---------- \u8868\u55ae\u5143\u4ef6 ---------- *\/\r\nlabel.field{display:block;margin:12px 0 5px;font-weight:600;font-size:.9rem}\r\ninput[type=text],input[type=password],select,textarea{\r\n  width:100%;padding:12px 15px;border:1.8px solid var(--border);border-radius:14px;\r\n  font-size:.95rem;font-family:inherit;background:var(--surface-tint);transition:border .15s,box-shadow .15s;\r\n}\r\ninput:focus,select:focus,textarea:focus{\r\n  border-color:var(--primary);background:#fff;box-shadow:0 0 0 4px rgba(91,155,213,.18);\r\n}\r\ntextarea{resize:vertical}\r\n.row{display:flex;gap:10px;flex-wrap:wrap}\r\n.row>*{flex:1;min-width:0}\r\n.chk{display:inline-flex;align-items:center;gap:6px;font-size:.9rem;cursor:pointer;user-select:none}\r\n.chk input{width:auto}\r\n\r\n\/* ---------- \u554f\u984c\u5361\u7247 ---------- *\/\r\n.q-card{background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow);\r\n  padding:16px;margin-bottom:14px;border-left:5px solid var(--primary);transition:.2s}\r\n.q-card.hidden-q{opacity:.55;border-left-color:var(--warn);background:#fff5f5}\r\n.q-head{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;flex-wrap:wrap}\r\n.q-asker{font-weight:700;font-size:.95rem}\r\n.q-asker .anon{color:var(--muted);font-weight:400}\r\n.q-target{display:inline-flex;align-items:center;gap:4px;background:linear-gradient(120deg,#E6F2FB,#D9EEFA);\r\n  color:var(--primary-dark);padding:4px 12px;border-radius:20px;font-size:.78rem;font-weight:700;margin-top:4px}\r\n.q-time{font-size:.74rem;color:var(--muted)}\r\n.q-body{margin:10px 0;font-size:1.04rem;white-space:pre-wrap;word-break:break-word}\r\n.q-img{max-width:100%;border-radius:14px;margin-top:8px;display:block;box-shadow:0 4px 12px rgba(91,155,213,.18)}\r\n.q-link{display:inline-flex;align-items:center;gap:6px;margin-top:10px;max-width:100%;\r\n  background:linear-gradient(120deg,#E6F2FB,#D9EEFA);color:var(--primary-dark);\r\n  padding:8px 14px;border-radius:14px;font-size:.85rem;font-weight:600;text-decoration:none;\r\n  border:1.5px solid var(--border);transition:.15s}\r\n.q-link:hover{background:#D5EAF8;box-shadow:0 4px 12px rgba(91,155,213,.25);transform:translateY(-1px)}\r\n.q-link .material-icons{font-size:18px;flex:0 0 auto}\r\n.q-link-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\r\n.q-actions{display:flex;align-items:center;gap:12px;margin-top:10px;flex-wrap:wrap}\r\n.like-btn{display:inline-flex;align-items:center;gap:5px;background:#E3F1FB;color:var(--like-deep);\r\n  padding:7px 15px;border-radius:22px;font-weight:700;font-size:.85rem;transition:.18s}\r\n.like-btn:hover{transform:scale(1.06)}\r\n.like-btn.liked{background:linear-gradient(120deg,#8FCBEF,#3D8FD0);color:#fff;box-shadow:0 4px 12px rgba(61,143,208,.4)}\r\n.reply-toggle{color:var(--primary-dark);font-weight:700;font-size:.85rem;background:#E6F2FB;\r\n  padding:7px 14px;border-radius:22px;display:inline-flex;align-items:center;gap:4px;transition:.15s}\r\n.reply-toggle:hover{background:#D5EAF8}\r\n.pdf-btn{color:#1d5e54;font-weight:700;font-size:.85rem;background:linear-gradient(120deg,#CFF3EA,#D5EEFB);\r\n  padding:7px 14px;border-radius:22px;display:inline-flex;align-items:center;gap:4px;transition:.15s}\r\n.pdf-btn:hover{transform:translateY(-2px);box-shadow:0 6px 14px rgba(125,211,192,.4)}\r\n.del-btn{color:var(--warn-text);font-weight:700;font-size:.85rem;background:#FDE7EC;\r\n  padding:7px 14px;border-radius:22px;display:inline-flex;align-items:center;gap:4px;transition:.15s}\r\n.del-btn:hover{background:#FBD5DE;transform:translateY(-2px)}\r\n.admin-q-tools{display:flex;gap:8px;margin-top:8px}\r\n.icon-btn{background:#E6F2FB;color:var(--muted);border-radius:12px;padding:7px 12px;font-size:.8rem;font-weight:700;display:inline-flex;align-items:center;gap:4px}\r\n.icon-btn.danger{background:#FDE7EC;color:var(--warn-text)}\r\n\r\n\/* ---------- \u7559\u8a00\u4e32 ---------- *\/\r\n.replies{margin-top:14px;border-top:2px dashed var(--border);padding-top:12px}\r\n.reply{padding:10px 0 10px 14px;border-left:3px solid var(--accent);margin-bottom:8px}\r\n.reply .r-name{font-weight:700;font-size:.88rem}\r\n.reply .r-name .speaker-badge{background:linear-gradient(120deg,#6FCBD8,#9BD3F0);color:#0c4650;font-size:.68rem;\r\n  padding:2px 9px;border-radius:12px;margin-left:6px;font-weight:700}\r\n.reply .r-body{font-size:.93rem;white-space:pre-wrap;word-break:break-word}\r\n.reply .r-time{font-size:.7rem;color:var(--muted)}\r\n.reply-form{margin-top:10px;display:none}\r\n.reply-form.show{display:block}\r\n.reply-form-sm{margin-top:8px;background:var(--surface-tint);border:1.5px solid var(--border);border-radius:14px;padding:12px}\r\n.reply-to-btn{background:none;color:var(--accent-deep);font-weight:700;font-size:.78rem;\r\n  display:inline-flex;align-items:center;gap:3px;margin-top:4px;padding:2px 0}\r\n.reply-to-btn .material-icons{font-size:16px}\r\n.reply-children{margin-left:14px;border-left:3px solid var(--secondary);padding-left:8px;margin-top:6px}\r\n\r\n\/* ---------- \u767c\u8868\u8005\u6e05\u55ae ---------- *\/\r\n.speaker-item{background:var(--surface);border-radius:var(--radius);box-shadow:var(--shadow);\r\n  margin-bottom:16px;overflow:hidden;border:1.5px solid var(--border)}\r\n.speaker-head{padding:16px 18px;display:flex;justify-content:space-between;align-items:center;\r\n  gap:10px;background:linear-gradient(120deg,#F4FAFE,#EAF4FC)}\r\n.speaker-head.static{cursor:default}\r\n.speaker-name{font-weight:900;font-size:1.12rem;display:flex;align-items:center;gap:8px}\r\n.speaker-topic{font-size:.82rem;color:var(--muted);margin-top:3px}\r\n.speaker-count{background:var(--grad-main);color:#fff;padding:5px 14px;border-radius:20px;font-size:.8rem;font-weight:700;\r\n  box-shadow:0 4px 10px rgba(91,155,213,.35)}\r\n.speaker-body{padding:6px 16px 14px;display:none}\r\n.speaker-body.open{display:block}\r\n\r\n\/* ---------- \u7a7a\u72c0\u614b ---------- *\/\r\n.empty{text-align:center;padding:40px 16px;color:var(--muted)}\r\n.empty .material-icons{font-size:48px;opacity:.4;display:block;margin-bottom:8px}\r\n\r\n\/* ---------- Modal ---------- *\/\r\n.modal-mask{\r\n  position:fixed;inset:0;background:rgba(28,27,31,.55);backdrop-filter:blur(2px);\r\n  display:flex;align-items:center;justify-content:center;z-index:100;padding:16px;\r\n  opacity:0;pointer-events:none;transition:opacity .22s;\r\n}\r\n.modal-mask.show{opacity:1;pointer-events:auto}\r\n.modal{\r\n  background:var(--surface);border-radius:26px;box-shadow:0 24px 70px rgba(122,76,160,.4);\r\n  max-width:560px;width:100%;max-height:90vh;overflow:auto;\r\n  transform:translateY(20px) scale(.97);transition:transform .25s;\r\n  border:2px solid rgba(255,255,255,.9);\r\n}\r\n.modal-mask.show .modal{transform:translateY(0) scale(1)}\r\n.modal-head{padding:20px 24px;display:flex;justify-content:space-between;align-items:center;\r\n  border-bottom:1.5px solid var(--border);position:sticky;top:0;z-index:2;\r\n  background:linear-gradient(120deg,#F4FAFE,#EAF4FC)}\r\n.modal-head h3{margin:0;font-size:1.2rem;display:flex;align-items:center;gap:8px;color:var(--primary-dark)}\r\n.modal-close{background:#E6F2FB;border-radius:50%;width:36px;height:36px;display:flex;\r\n  align-items:center;justify-content:center;color:var(--muted);transition:.15s}\r\n.modal-close:hover{background:#D5EAF8;transform:rotate(90deg)}\r\n.modal-body{padding:22px 24px}\r\n.modal-foot{padding:16px 24px;border-top:1.5px solid var(--border);display:flex;gap:10px;justify-content:flex-end;flex-wrap:wrap}\r\n.help-step{display:flex;gap:12px;margin-bottom:16px}\r\n.help-step .num{flex:0 0 30px;height:30px;border-radius:50%;background:var(--grad-main);color:#fff;\r\n  display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.85rem;\r\n  box-shadow:0 3px 8px rgba(91,155,213,.4)}\r\n.help-step .txt{font-size:.92rem}\r\n.code-hint{background:#1E3A52;color:#CDEAFB;padding:14px;border-radius:14px;font-family:Consolas,monospace;\r\n  font-size:.8rem;white-space:pre;overflow-x:auto;margin:8px 0}\r\n.note{background:linear-gradient(120deg,#EAF5FD,#DDEEFA);border-left:4px solid var(--accent-deep);padding:11px 14px;border-radius:12px;font-size:.85rem;margin:10px 0}\r\n.note.danger{background:linear-gradient(120deg,#FDE7EC,#FBE0E8);border-left-color:var(--warn)}\r\n\r\n\/* ---------- Toast ---------- *\/\r\n#toast-wrap{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:200;\r\n  display:flex;flex-direction:column;gap:8px;align-items:center;width:calc(100% - 32px);max-width:420px}\r\n.toast{background:rgba(74,59,92,.96);color:#fff;padding:14px 20px;border-radius:30px;box-shadow:0 8px 26px rgba(122,76,160,.4);\r\n  font-size:.9rem;display:flex;align-items:center;gap:8px;animation:toastIn .3s;max-width:100%;backdrop-filter:blur(6px)}\r\n.toast.ok{background:linear-gradient(120deg,#8CD9A8,#5FBF85);color:#0c3d24}\r\n.toast.err{background:linear-gradient(120deg,#F8A5B5,#F0728A)}\r\n.toast.info{background:var(--grad-main)}\r\n@keyframes toastIn{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}\r\n\r\n\/* ---------- QR \/ \u5206\u4eab ---------- *\/\r\n.share-box{background:var(--surface-tint);border:1.5px dashed var(--primary);border-radius:18px;padding:16px;margin-top:14px}\r\n.qr-img{display:block;margin:12px auto;border:8px solid #fff;border-radius:16px;box-shadow:var(--shadow);background:#fff}\r\n.short-url{display:flex;gap:8px;align-items:center;background:#fff;border:1.5px solid var(--border);\r\n  border-radius:12px;padding:9px 12px;font-size:.85rem;word-break:break-all}\r\n\r\n\/* ---------- \u5f8c\u53f0\u7d71\u8a08 ---------- *\/\r\n.stat-row{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:8px}\r\n.stat{flex:1;min-width:120px;background:var(--grad-main);\r\n  color:#fff;border-radius:18px;padding:16px;text-align:center;box-shadow:0 8px 20px rgba(91,155,213,.3)}\r\n.stat .n{font-size:1.7rem;font-weight:900;text-shadow:0 2px 4px rgba(0,0,0,.12)}\r\n.stat .l{font-size:.78rem;opacity:.95}\r\n.stat.s2{background:var(--grad-accent)}\r\n.stat.s3{background:var(--grad-mint);color:#0f4f44}\r\n\r\n\/* ---------- \u5f8c\u53f0\u767c\u8868\u8005\u7dca\u6e4a\u5217 ---------- *\/\r\n.sp-row{display:flex;align-items:center;gap:6px;padding:5px 0;border-bottom:1px solid var(--border)}\r\n.sp-row input{padding:7px 10px;font-size:.85rem;min-width:0}\r\n.mini-btn{flex:0 0 auto;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;\r\n  justify-content:center;transition:filter .12s,transform .12s}\r\n.mini-btn:hover{transform:translateY(-1px);filter:brightness(1.05)}\r\n.mini-btn .material-icons{font-size:19px}\r\n.mini-btn.save{background:#DBEDFA;color:var(--primary-dark)}\r\n.mini-btn.del{background:#FDE7EC;color:var(--warn-text)}\r\n\r\n.divider{height:1px;background:var(--border);margin:16px 0}\r\n.muted{color:var(--muted);font-size:.85rem}\r\n.badge-status{display:inline-flex;align-items:center;gap:5px;padding:6px 13px;border-radius:20px;font-size:.8rem;font-weight:700}\r\n.badge-status.on{background:linear-gradient(120deg,#D6F5E3,#C3F0D6);color:var(--ok-text)}\r\n.badge-status.off{background:linear-gradient(120deg,#FDE7EC,#FBDCE3);color:var(--warn-text)}\r\n\r\n\/* ---------- \u95dc\u9375\u5b57\u641c\u5c0b\u5217 ---------- *\/\r\n.search-bar{display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.85);backdrop-filter:blur(6px);\r\n  border:1.8px solid var(--border);border-radius:30px;padding:9px 16px;margin-bottom:14px;\r\n  box-shadow:var(--shadow);transition:border .15s,box-shadow .15s}\r\n.search-bar:focus-within{border-color:var(--primary);box-shadow:0 0 0 4px rgba(91,155,213,.18)}\r\n.search-bar .material-icons{color:var(--accent-deep)}\r\n.search-bar input{flex:1;border:none;outline:none;background:transparent;padding:4px;font-size:.95rem;box-shadow:none}\r\n.search-bar input:focus{box-shadow:none;background:transparent}\r\n.search-clear{background:#E6F2FB;border-radius:50%;width:30px;height:30px;display:flex;\r\n  align-items:center;justify-content:center;color:var(--muted);flex:0 0 auto}\r\nmark{background:linear-gradient(120deg,#BFE3F5,#9BD3F0);color:#13496e;border-radius:4px;padding:0 3px;font-weight:700}\r\n\r\n\/* ---------- \u6211\u8981\u63d0\u554f \u6d6e\u52d5\u6309\u9215 (FAB) ---------- *\/\r\n.ask-fab{position:fixed;right:22px;bottom:22px;z-index:70;\r\n  background:var(--grad-accent);color:#fff;font-weight:800;font-size:1.02rem;\r\n  padding:17px 26px;border-radius:34px;box-shadow:0 10px 28px rgba(74,163,220,.5);\r\n  display:flex;align-items:center;gap:8px;transition:transform .15s,box-shadow .15s;\r\n  border:2px solid rgba(255,255,255,.6);animation:fabPulse 2.6s ease-in-out infinite}\r\n.ask-fab:hover{transform:translateY(-4px) scale(1.03);box-shadow:0 14px 34px rgba(74,163,220,.6)}\r\n.ask-fab .material-icons{font-size:24px}\r\n@keyframes fabPulse{0%,100%{box-shadow:0 10px 28px rgba(74,163,220,.5)}50%{box-shadow:0 10px 34px rgba(74,163,220,.75)}}\r\n\r\n\/* ---------- \u9801\u5c3e\u7f72\u540d \/ CC \u6388\u6b0a (\u4f4e\u8abf) ---------- *\/\r\n.site-foot{text-align:center;font-size:.72rem;color:var(--muted);opacity:.7;\r\n  margin:26px auto 8px;line-height:1.7;padding:0 12px}\r\n.site-foot a{color:var(--primary-dark);text-decoration:none;font-weight:600}\r\n.site-foot a:hover{text-decoration:underline}\r\n\r\n\/* ---------- PDF \u6e32\u67d3\u5bb9\u5668 (\u5e73\u6642\u96b1\u85cf\uff1b\u7522\u751f\u6642\u66ab\u6642\u79fb\u81f3\u756b\u9762\u5916\u64f7\u53d6) ---------- *\/\r\n#pdfRender{display:none}\r\n\r\n@media(max-width:600px){\r\n  .ask-fab{padding:16px;border-radius:50%}\r\n  .ask-fab .ask-fab-lbl{display:none}\r\n  header.appbar .subtitle{display:none}\r\n  .fab-group button span.lbl{display:none}\r\n  .fab-group button{padding:9px}\r\n  .welcome .btns{flex-direction:column}\r\n  .btn{width:100%;justify-content:center}\r\n}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n\r\n<!-- ====== \u9802\u90e8\u6a19\u984c\u5217 (\u8996\u89ba\u8b58\u5225) ====== -->\r\n<header class=\"appbar\">\r\n  <div class=\"logo\">\ud83c\udfa4<\/div>\r\n  <div>\r\n    <div class=\"title\">AI \u6559\u80b2\u5e74\u6703 \u00b7 \u73fe\u5834\u63d0\u554f\u5e73\u53f0<\/div>\r\n    <div class=\"subtitle\">\u5373\u6642\u4e92\u52d5 \u00b7 \u9ede\u5c0d\u9ede\u63d0\u554f \u00b7 \u5c08\u5bb6\u77e5\u8b58\u5eab<\/div>\r\n  <\/div>\r\n<\/header>\r\n\r\n<!-- ====== \u53f3\u4e0a\u56fa\u5b9a\u61f8\u6d6e\u6309\u9215 ====== -->\r\n<div class=\"fab-group\">\r\n  <button id=\"fabHelp\" onclick=\"openModal('helpModal')\" title=\"\u5982\u4f55\u8a2d\u5b9a Firebase\">\r\n    <span class=\"material-icons\">help_outline<\/span><span class=\"lbl\">\u8aaa\u660e<\/span>\r\n  <\/button>\r\n  <button id=\"fabSettings\" onclick=\"openModal('settingsModal')\" title=\"\u8a2d\u5b9a Firebase \u9023\u7dda\">\r\n    <span class=\"material-icons\">settings<\/span><span class=\"lbl\">\u8a2d\u5b9a<\/span>\r\n  <\/button>\r\n  <button class=\"admin admin-mini\" onclick=\"openAdminGate()\" title=\"\u5f8c\u53f0\u7ba1\u7406\">\r\n    <span class=\"material-icons\">admin_panel_settings<\/span>\r\n  <\/button>\r\n<\/div>\r\n\r\n<div class=\"container\">\r\n\r\n  <!-- ====== \u9023\u7dda\u72c0\u614b\u63d0\u793a ====== -->\r\n  <div id=\"connBanner\"><\/div>\r\n\r\n  <!-- ====== \u524d\u53f0\u4e3b\u756b\u9762 ====== -->\r\n  <div id=\"frontApp\" style=\"display:none\">\r\n\r\n    <!-- \u96d9\u7dad\u5ea6\u5206\u9801 -->\r\n    <div class=\"tabs\">\r\n      <button id=\"tabQ\" class=\"active\" onclick=\"switchTab('q')\">\r\n        <span class=\"material-icons\">forum<\/span> \u6240\u6709\u63d0\u554f\u6e05\u55ae\r\n      <\/button>\r\n      <button id=\"tabA\" onclick=\"switchTab('a')\">\r\n        <span class=\"material-icons\">public<\/span> \u5c0d\u5168\u9ad4\u63d0\u554f\u6e05\u55ae\r\n      <\/button>\r\n      <button id=\"tabS\" onclick=\"switchTab('s')\">\r\n        <span class=\"material-icons\">groups<\/span> \u5c0d\u767c\u8868\u8005\u63d0\u554f\u6e05\u55ae\r\n      <\/button>\r\n    <\/div>\r\n\r\n    <!-- \u95dc\u9375\u5b57\u641c\u5c0b (\u641c\u5c0b\u63d0\u554f\u5167\u5bb9 \/ \u63d0\u554f\u4eba \/ \u5411\u8ab0\u63d0\u554f) -->\r\n    <div class=\"search-bar\">\r\n      <span class=\"material-icons\">search<\/span>\r\n      <input type=\"text\" id=\"searchInput\" placeholder=\"\u641c\u5c0b\u63d0\u554f\u95dc\u9375\u5b57 (\u5167\u5bb9\u3001\u63d0\u554f\u4eba\u3001\u767c\u8868\u8005)\u2026\" oninput=\"onSearch()\">\r\n      <button id=\"searchClear\" class=\"search-clear\" style=\"display:none\" onclick=\"clearSearch()\" title=\"\u6e05\u9664\">\r\n        <span class=\"material-icons\">close<\/span>\r\n      <\/button>\r\n    <\/div>\r\n\r\n    <!-- \u63d0\u554f\u6e05\u55ae\u6392\u5e8f -->\r\n    <div id=\"qListView\">\r\n      <div class=\"row\" style=\"margin-bottom:12px\">\r\n        <button class=\"btn btn-ghost\" id=\"sortTime\" onclick=\"setSort('time')\">\r\n          <span class=\"material-icons\">schedule<\/span> \u6642\u9593\u6392\u5e8f\r\n        <\/button>\r\n        <button class=\"btn btn-ghost\" id=\"sortLike\" onclick=\"setSort('like')\">\r\n          <span class=\"material-icons\">favorite<\/span> \u71b1\u5ea6\u6392\u5e8f\r\n        <\/button>\r\n      <\/div>\r\n      <div id=\"questionList\"><\/div>\r\n    <\/div>\r\n\r\n    <!-- \u5168\u9ad4\u63d0\u554f (\u5411\u8ab0\u63d0\u554f\u9078\u70ba\u300c\u5168\u9ad4 \/ \u4e0d\u6307\u5b9a\u300d\u7684\u554f\u984c) -->\r\n    <div id=\"aListView\" style=\"display:none\">\r\n      <div id=\"allQuestionList\"><\/div>\r\n    <\/div>\r\n\r\n    <!-- \u767c\u8868\u8005\u6e05\u55ae -->\r\n    <div id=\"sListView\" style=\"display:none\">\r\n      <div id=\"speakerList\"><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ====== \u6211\u8981\u63d0\u554f\uff1a\u53f3\u4e0b\u6d6e\u52d5\u6309\u9215 (FAB) ====== -->\r\n  <button id=\"askFab\" class=\"ask-fab\" style=\"display:none\" onclick=\"openAskModal()\">\r\n    <span class=\"material-icons\">edit<\/span><span class=\"ask-fab-lbl\">\u6211\u8981\u63d0\u554f<\/span>\r\n  <\/button>\r\n\r\n  <!-- ====== \u672a\u8a2d\u5b9a\u5f15\u5c0e\u756b\u9762 ====== -->\r\n  <div id=\"welcomeScreen\" style=\"display:none\">\r\n    <div class=\"welcome\">\r\n      <span class=\"material-icons big-icon\">cloud_off<\/span>\r\n      <h2>\u6b61\u8fce\u4f7f\u7528\u73fe\u5834\u63d0\u554f\u5e73\u53f0<\/h2>\r\n      <p>\u6b64\u5e73\u53f0\u9700\u9023\u7dda\u60a8\u81ea\u5df1\u7684 <b>Firebase Firestore<\/b> \u8cc7\u6599\u5eab (\u5b8c\u5168\u514d\u8cbb)\u3002\r\n         \u8acb\u5148\u5b8c\u6210\u9023\u7dda\u8a2d\u5b9a\uff0c\u5373\u53ef\u958b\u59cb\u63a5\u53d7\u73fe\u5834\u63d0\u554f\u3002<\/p>\r\n      <div class=\"btns\">\r\n        <button class=\"btn btn-primary\" onclick=\"openModal('settingsModal')\">\r\n          <span class=\"material-icons\">settings<\/span> \u8a2d\u5b9a Firebase \u9023\u7dda\r\n        <\/button>\r\n        <button class=\"btn btn-secondary\" onclick=\"openModal('helpModal')\">\r\n          <span class=\"material-icons\">help_outline<\/span> \u5982\u4f55\u8a2d\u5b9a Firebase\r\n        <\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ====== \u5f8c\u53f0\u7ba1\u7406\u756b\u9762 ====== -->\r\n  <div id=\"adminApp\" style=\"display:none\">\r\n    <div class=\"card\">\r\n      <div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px\">\r\n        <h3 style=\"margin:0\"><span class=\"material-icons\" style=\"color:var(--accent)\">dashboard<\/span> \u5f8c\u53f0\u7ba1\u7406\u5100\u8868\u677f<\/h3>\r\n        <button class=\"btn btn-ghost\" onclick=\"exitAdmin()\"><span class=\"material-icons\">logout<\/span> \u96e2\u958b\u5f8c\u53f0<\/button>\r\n      <\/div>\r\n      <div class=\"stat-row\" style=\"margin-top:14px\">\r\n        <div class=\"stat\"><div class=\"n\" id=\"statQ\">0<\/div><div class=\"l\">\u7e3d\u63d0\u554f\u6578<\/div><\/div>\r\n        <div class=\"stat s2\"><div class=\"n\" id=\"statR\">0<\/div><div class=\"l\">\u7e3d\u56de\u61c9\u6578<\/div><\/div>\r\n        <div class=\"stat s3\"><div class=\"n\" id=\"statS\">0<\/div><div class=\"l\">\u767c\u8868\u8005\u6578<\/div><\/div>\r\n      <\/div>\r\n      <div class=\"divider\"><\/div>\r\n      <div style=\"display:flex;align-items:center;gap:12px;flex-wrap:wrap\">\r\n        <span>\u63d0\u554f\u5165\u53e3\uff1a<\/span>\r\n        <span id=\"askStatusBadge\" class=\"badge-status on\"><span class=\"material-icons\">check_circle<\/span>\u958b\u653e\u4e2d<\/span>\r\n        <button class=\"btn btn-ghost\" onclick=\"toggleAskOpen()\"><span class=\"material-icons\">power_settings_new<\/span> \u5207\u63db\u958b\u95dc<\/button>\r\n        <button class=\"btn btn-secondary\" onclick=\"exportData()\"><span class=\"material-icons\">download<\/span> \u532f\u51fa\u5c0d\u7167\u8868(CSV)<\/button>\r\n      <\/div>\r\n      <div class=\"divider\"><\/div>\r\n      <div style=\"display:flex;align-items:center;gap:12px;flex-wrap:wrap\">\r\n        <span>\u53f3\u4e0a\u300c\u8aaa\u660e\u300d\u6309\u9215\uff1a<\/span>\r\n        <span id=\"helpStatusBadge\" class=\"badge-status on\"><span class=\"material-icons\">visibility<\/span>\u986f\u793a\u4e2d<\/span>\r\n        <button class=\"btn btn-ghost\" onclick=\"toggleHelpBtn()\"><span class=\"material-icons\">swap_horiz<\/span> \u5207\u63db\u986f\u793a<\/button>\r\n      <\/div>\r\n      <div style=\"display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:10px\">\r\n        <span>\u53f3\u4e0a\u300c\u8a2d\u5b9a\u300d\u6309\u9215\uff1a<\/span>\r\n        <span id=\"settingsStatusBadge\" class=\"badge-status on\"><span class=\"material-icons\">visibility<\/span>\u986f\u793a\u4e2d<\/span>\r\n        <button class=\"btn btn-ghost\" onclick=\"toggleSettingsBtn()\"><span class=\"material-icons\">swap_horiz<\/span> \u5207\u63db\u986f\u793a<\/button>\r\n      <\/div>\r\n      <p class=\"muted\" style=\"margin-top:8px\">\u96b1\u85cf\u5f8c\uff0c\u6240\u6709\u53c3\u52a0\u8005\u88dd\u7f6e\u90fd\u4e0d\u6703\u770b\u5230\u8a72\u6309\u9215\uff08\u5f8c\u53f0\u4ecd\u53ef\u9032\u5165\uff09\u3002<\/p>\r\n    <\/div>\r\n\r\n    <!-- \u767c\u8868\u8005\u7ba1\u7406 -->\r\n    <div class=\"card\">\r\n      <h3><span class=\"material-icons\" style=\"color:var(--primary)\">record_voice_over<\/span> \u767c\u8868\u8005\u8cc7\u6599\u7ba1\u7406<\/h3>\r\n      <label class=\"field\">\u65b0\u589e \/ \u7de8\u8f2f\u55ae\u7b46<\/label>\r\n      <div class=\"row\">\r\n        <input type=\"text\" id=\"spName\" placeholder=\"\u59d3\u540d\">\r\n        <input type=\"text\" id=\"spTopic\" placeholder=\"\u4e3b\u984c\">\r\n      <\/div>\r\n      <button class=\"btn btn-primary btn-block\" style=\"margin-top:10px\" onclick=\"addSpeaker()\">\r\n        <span class=\"material-icons\">person_add<\/span> \u65b0\u589e\u767c\u8868\u8005\r\n      <\/button>\r\n\r\n      <div class=\"divider\"><\/div>\r\n      <label class=\"field\">\u6279\u6b21\u532f\u5165 (CSV\uff1a\u59d3\u540d,\u4e3b\u984c\uff0c\u6bcf\u884c\u4e00\u7b46\uff0c\u542b\u81ea\u52d5\u6821\u5c0d\u53bb\u91cd)<\/label>\r\n      <textarea id=\"csvInput\" rows=\"4\" placeholder=\"\u738b\u5c0f\u660e,\u751f\u6210\u5f0fAI\u5728\u8ab2\u5802\u7684\u61c9\u7528&#10;\u674e\u7f8e\u83ef,AI\u7d20\u990a\u8207\u8a55\u91cf\"><\/textarea>\r\n      <div class=\"row\" style=\"margin-top:10px\">\r\n        <input type=\"file\" id=\"csvFile\" accept=\".csv,text\/csv\" onchange=\"loadCsvFile(event)\" style=\"flex:2\">\r\n        <button class=\"btn btn-secondary\" onclick=\"importCsv()\" style=\"flex:1\">\r\n          <span class=\"material-icons\">upload_file<\/span> \u532f\u5165\r\n        <\/button>\r\n      <\/div>\r\n\r\n      <div class=\"divider\"><\/div>\r\n      <div id=\"adminSpeakerList\"><\/div>\r\n    <\/div>\r\n\r\n    <!-- \u5167\u5bb9\u5be9\u6838 -->\r\n    <div class=\"card\">\r\n      <h3><span class=\"material-icons\" style=\"color:var(--warn)\">gavel<\/span> \u5167\u5bb9\u5be9\u6838\u8207\u63a7\u7ba1<\/h3>\r\n      <label class=\"field\">\u9055\u898f\u95dc\u9375\u5b57\u8a5e\u5eab (\u4ee5\u9017\u865f\u5206\u9694\uff0c\u547d\u4e2d\u5c07\u81ea\u52d5\u96b1\u85cf)<\/label>\r\n      <div class=\"row\">\r\n        <input type=\"text\" id=\"kwInput\" placeholder=\"\u4f8b\u5982\uff1a\u5ee3\u544a,\u6d17\u7248,\u5783\u573e\" style=\"flex:2\">\r\n        <button class=\"btn btn-primary\" onclick=\"saveKeywords()\" style=\"flex:1\"><span class=\"material-icons\">save<\/span> \u5132\u5b58\u8a5e\u5eab<\/button>\r\n      <\/div>\r\n      <p class=\"muted\" id=\"kwCurrent\" style=\"margin-top:8px\"><\/p>\r\n      <div class=\"divider\"><\/div>\r\n      <p class=\"muted\">\u5728\u300c\u63d0\u554f\u6e05\u55ae\u300d\u5206\u9801\u4e2d\uff0c\u6bcf\u5247\u554f\u984c\u4e0b\u65b9\u6709 <b>\u96b1\u85cf \/ \u9084\u539f \/ \u522a\u9664<\/b> \u5de5\u5177\u5217\u3002<\/p>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- \u9801\u5c3e\uff1a\u4f5c\u8005\u7f72\u540d\u8207 CC \u6388\u6b0a (\u4f4e\u8abf) -->\r\n  <footer class=\"site-foot\">\r\n    Made by <a href=\"https:\/\/kentxchang.blogspot.tw\" target=\"_blank\" rel=\"noopener noreferrer\">\u963f\u525b\u8001\u5e2b<\/a>\r\n    \u00b7\u3000\u672c\u4f5c\u54c1\u63a1\r\n    <a href=\"https:\/\/creativecommons.org\/licenses\/by-nc-sa\/4.0\/deed.zh_TW\" target=\"_blank\" rel=\"noopener noreferrer\">CC BY-NC-SA 4.0<\/a>\r\n    \uff08\u59d3\u540d\u6a19\u793a\u2014\u975e\u5546\u696d\u6027\u2014\u76f8\u540c\u65b9\u5f0f\u5206\u4eab\uff09\u6388\u6b0a\r\n  <\/footer>\r\n\r\n<\/div><!-- \/container -->\r\n\r\n<!-- ============================================================== -->\r\n<!-- ====================  \u5404\u985e Modal \u8996\u7a97  ======================== -->\r\n<!-- ============================================================== -->\r\n\r\n<!-- \u6211\u8981\u63d0\u554f Modal (\u6d6e\u5c64\u5716\u5c64) -->\r\n<div class=\"modal-mask\" id=\"askModal\">\r\n  <div class=\"modal\">\r\n    <div class=\"modal-head\">\r\n      <h3><span class=\"material-icons\" style=\"color:var(--accent)\">campaign<\/span> \u6211\u8981\u63d0\u554f<\/h3>\r\n      <button class=\"modal-close\" onclick=\"closeModal('askModal')\"><span class=\"material-icons\">close<\/span><\/button>\r\n    <\/div>\r\n    <div class=\"modal-body\">\r\n      <div id=\"askClosedNotice\" style=\"display:none\" class=\"note danger\">\r\n        <span class=\"material-icons\" style=\"color:var(--warn)\">lock<\/span> \u4e3b\u8fa6\u55ae\u4f4d\u76ee\u524d\u5df2\u95dc\u9589\u63d0\u554f\u5165\u53e3\u3002\r\n      <\/div>\r\n      <div id=\"askFormWrap\">\r\n        <label class=\"field\">\u60a8\u7684\u7a31\u547c<\/label>\r\n        <input type=\"text\" id=\"askerName\" placeholder=\"\u8acb\u8f38\u5165\u60a8\u7684\u59d3\u540d\u6216\u66b1\u7a31\" maxlength=\"30\">\r\n        <label class=\"field\">\u5411\u8ab0\u63d0\u554f<\/label>\r\n        <input type=\"text\" id=\"targetSearch\" placeholder=\"\ud83d\udd0d \u8f38\u5165\u95dc\u9375\u5b57\u5feb\u901f\u7be9\u9078\u767c\u8868\u8005\u2026\" oninput=\"filterTargetSpeaker()\" autocomplete=\"off\">\r\n        <select id=\"targetSpeaker\" style=\"margin-top:8px\">\r\n          <option value=\"\">\u2014 \u5168\u9ad4 \/ \u4e0d\u6307\u5b9a \u2014<\/option>\r\n        <\/select>\r\n        <label class=\"field\">\u554f\u984c\u5167\u5bb9<\/label>\r\n        <textarea id=\"qContent\" rows=\"3\" placeholder=\"\u8acb\u8f38\u5165\u60a8\u7684\u554f\u984c\u2026\" maxlength=\"800\"><\/textarea>\r\n        <label class=\"field\">\u9644\u52a0\u7db2\u5740 (\u9078\u586b)<\/label>\r\n        <input type=\"text\" id=\"qLink\" placeholder=\"https:\/\/\u2026 (\u76f8\u95dc\u9023\u7d50\u3001\u7c21\u5831\u3001\u5f71\u7247\u7b49)\" maxlength=\"500\">\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"modal-foot\">\r\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('askModal')\">\u53d6\u6d88<\/button>\r\n      <button class=\"btn btn-primary\" id=\"askSubmitBtn\" onclick=\"submitQuestion()\">\r\n        <span class=\"material-icons\">send<\/span> \u9001\u51fa\u63d0\u554f\r\n      <\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- \u8aaa\u660e Modal -->\r\n<div class=\"modal-mask\" id=\"helpModal\">\r\n  <div class=\"modal\">\r\n    <div class=\"modal-head\">\r\n      <h3><span class=\"material-icons\">help_outline<\/span> \u5982\u4f55\u53d6\u5f97 Firebase \u8a2d\u5b9a<\/h3>\r\n      <button class=\"modal-close\" onclick=\"closeModal('helpModal')\"><span class=\"material-icons\">close<\/span><\/button>\r\n    <\/div>\r\n    <div class=\"modal-body\">\r\n      <div class=\"help-step\"><div class=\"num\">1<\/div><div class=\"txt\">\u524d\u5f80 <a href=\"https:\/\/console.firebase.google.com\" target=\"_blank\">Firebase Console<\/a>\uff0c\u9ede\u300c\u65b0\u589e\u5c08\u6848\u300d(\u514d\u8cbb\u65b9\u6848 Spark \u5373\u53ef)\u3002<\/div><\/div>\r\n      <div class=\"help-step\"><div class=\"num\">2<\/div><div class=\"txt\">\u9032\u5165\u5c08\u6848\u5f8c\uff0c\u5de6\u5074\u9078\u55ae\u5efa\u7acb <b>Cloud Firestore<\/b> \u8cc7\u6599\u5eab (\u6e2c\u8a66\u6a21\u5f0f\u555f\u7528)\u3002<\/div><\/div>\r\n      <div class=\"help-step\"><div class=\"num\">3<\/div><div class=\"txt\">\u5728\u5c08\u6848\u8a2d\u5b9a (\u9f52\u8f2a \u2699\ufe0f) \u2192 \u4e00\u822c \u2192 \u300c\u60a8\u7684\u61c9\u7528\u7a0b\u5f0f\u300d\uff0c\u9ede <b>&lt;\/&gt; Web<\/b> \u5716\u793a\u8a3b\u518a\u4e00\u500b\u7db2\u9801 App\u3002<\/div><\/div>\r\n      <div class=\"help-step\"><div class=\"num\">4<\/div><div class=\"txt\">\u7cfb\u7d71\u6703\u986f\u793a\u4e00\u6bb5 <code>firebaseConfig<\/code> \u7269\u4ef6\uff0c\u5167\u5bb9\u5982\u4e0b\uff1a<\/div><\/div>\r\n      <div class=\"code-hint\">const firebaseConfig = {\r\n  apiKey: \"AIzaSy...\",\r\n  authDomain: \"xxx.firebaseapp.com\",\r\n  projectId: \"xxx\",\r\n  storageBucket: \"xxx.appspot.com\",\r\n  messagingSenderId: \"1234567890\",\r\n  appId: \"1:1234:web:abcd\"\r\n};<\/div>\r\n      <div class=\"help-step\"><div class=\"num\">5<\/div><div class=\"txt\">\u8907\u88fd\u5927\u62ec\u865f <code>{ }<\/code> \u4e4b\u9593\u7684 <b>\u516d\u884c\u5167\u5bb9<\/b>\uff0c\u6309\u53f3\u4e0a\u300c\u8a2d\u5b9a\u300d\u8cbc\u4e0a\u5373\u53ef\u9023\u7dda\u3002<\/div><\/div>\r\n      <div class=\"note\"><span class=\"material-icons\" style=\"color:#FFB300\">tips_and_updates<\/span> \u8a2d\u5b9a\u4e00\u6b21\u5f8c\u6703\u8a18\u4f4f\u5728\u672c\u6a5f\uff1b\u4e5f\u53ef\u7528\u300c\u7522\u751f\u5206\u4eab\u9023\u7d50 \/ QR Code\u300d\u8b93\u5176\u4ed6\u5de5\u4f5c\u4eba\u54e1\u4e00\u9375\u5e36\u8a2d\u5b9a\u9032\u5834\u3002<\/div>\r\n    <\/div>\r\n    <div class=\"modal-foot\">\r\n      <button class=\"btn btn-primary\" onclick=\"closeModal('helpModal');openModal('settingsModal')\">\u524d\u5f80\u8a2d\u5b9a \u2192<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- \u8a2d\u5b9a Modal -->\r\n<div class=\"modal-mask\" id=\"settingsModal\">\r\n  <div class=\"modal\">\r\n    <div class=\"modal-head\">\r\n      <h3><span class=\"material-icons\">settings<\/span> \u8a2d\u5b9a Firebase \u9023\u7dda<\/h3>\r\n      <button class=\"modal-close\" onclick=\"closeModal('settingsModal')\"><span class=\"material-icons\">close<\/span><\/button>\r\n    <\/div>\r\n    <div class=\"modal-body\">\r\n      <p style=\"font-weight:700\"><span class=\"material-icons\" style=\"color:var(--primary)\">content_paste<\/span> \u8cbc\u4e0a Firebase \u8a2d\u5b9a<\/p>\r\n      <p class=\"muted\">\u8acb\u5f9e\u60a8\u7684 Firebase \u5c08\u6848\u4e2d\uff0c\u8907\u88fd firebaseConfig \u7269\u4ef6 <code>{<\/code> \u548c <code>}<\/code> \u4e4b\u9593\u7684<b>\u516d\u884c\u5167\u5bb9 (Key: Value)<\/b>\uff0c\u8cbc\u5230\u4e0b\u65b9\u3002<\/p>\r\n      <div class=\"note\"><b>\u8acb\u52ff\u8cbc\u4e0a\u5927\u62ec\u865f {} \u6216\u8b8a\u6578\u5ba3\u544a<\/b>\uff0c\u53ea\u9700\u8cbc\u4e0a <code>apiKey: \"...\"<\/code> \u5230 <code>appId: \"...\"<\/code> \u7684\u5167\u5bb9\u3002\uff08\u4e0d\u904e\u8cbc\u4e0a\u6574\u6bb5\u4e5f\u6c92\u95dc\u4fc2\uff0c\u7cfb\u7d71\u6703\u81ea\u52d5\u89e3\u6790\u3002\uff09<\/div>\r\n      <textarea id=\"configInput\" style=\"min-height:150px;font-family:Consolas,monospace;font-size:.85rem\" placeholder='apiKey: \"AIza...\",\r\nauthDomain: \"xxx.firebaseapp.com\",\r\nprojectId: \"xxx\",\r\nstorageBucket: \"xxx.appspot.com\",\r\nmessagingSenderId: \"1234567890\",\r\nappId: \"1:1234:web:abcd...\"'><\/textarea>\r\n      <button class=\"btn btn-primary btn-block\" style=\"margin-top:12px\" onclick=\"saveConfigFromInput()\">\r\n        <span class=\"material-icons\">link<\/span> \u89e3\u6790\u4e26\u9023\u7dda\r\n      <\/button>\r\n\r\n      <!-- \u5206\u4eab\u5de5\u5177 -->\r\n      <div class=\"share-box\" id=\"shareBox\">\r\n        <p style=\"font-weight:700;margin:0\"><span class=\"material-icons\" style=\"color:var(--primary)\">qr_code_2<\/span> \u7522\u751f\u5206\u4eab\u9023\u7d50 \/ QR Code<\/p>\r\n        <p class=\"muted\" style=\"margin:6px 0\">\u5c07\u76ee\u524d\u8a2d\u5b9a\u7de8\u78bc\u70ba\u4e00\u689d\u7db2\u5740\uff0c\u5176\u4ed6\u4eba\u958b\u555f\u5373\u81ea\u52d5\u5e36\u5165\u8a2d\u5b9a\u4e26\u9023\u7dda\u3002<\/p>\r\n        <button class=\"btn btn-secondary btn-block\" onclick=\"generateShareLink()\">\r\n          <span class=\"material-icons\">share<\/span> \u7522\u751f\u5206\u4eab\u9023\u7d50\u8207 QR Code\r\n        <\/button>\r\n        <div id=\"shareResult\" style=\"display:none\">\r\n          <div class=\"short-url\" style=\"margin-top:12px\">\r\n            <span id=\"shortUrlText\" style=\"flex:1\"><\/span>\r\n            <button class=\"icon-btn\" onclick=\"copyShareUrl()\"><span class=\"material-icons\">content_copy<\/span><\/button>\r\n          <\/div>\r\n          <img id=\"qrImg\" class=\"qr-img\" alt=\"QR Code\">\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <!-- \u6e05\u9664\u8a2d\u5b9a -->\r\n      <div class=\"note danger\" style=\"margin-top:16px\">\r\n        <b>\u6e05\u9664\u672c\u6a5f\u8a2d\u5b9a<\/b>\uff1a\u5c07\u79fb\u9664\u6b64\u700f\u89bd\u5668\u4e2d\u5132\u5b58\u7684 Firebase \u8a2d\u5b9a\u4e26\u91cd\u65b0\u6574\u7406\uff0c\u56de\u5230\u672a\u8a2d\u5b9a\u72c0\u614b\u3002\r\n      <\/div>\r\n      <button class=\"btn btn-warn btn-block\" onclick=\"resetConfig()\">\r\n        <span class=\"material-icons\">delete_forever<\/span> \u6e05\u9664\u672c\u6a5f\u8a2d\u5b9a\r\n      <\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- \u5f8c\u53f0\u5bc6\u78bc Modal -->\r\n<div class=\"modal-mask\" id=\"adminGateModal\">\r\n  <div class=\"modal\" style=\"max-width:400px\">\r\n    <div class=\"modal-head\">\r\n      <h3><span class=\"material-icons\">lock<\/span> \u5f8c\u53f0\u7ba1\u7406\u767b\u5165<\/h3>\r\n      <button class=\"modal-close\" onclick=\"closeModal('adminGateModal')\"><span class=\"material-icons\">close<\/span><\/button>\r\n    <\/div>\r\n    <div class=\"modal-body\">\r\n      <p class=\"muted\">\u8acb\u8f38\u5165\u7ba1\u7406\u54e1\u5bc6\u78bc\u9032\u5165\u5f8c\u53f0\u3002<\/p>\r\n      <input type=\"password\" id=\"adminPwd\" placeholder=\"\u7ba1\u7406\u54e1\u5bc6\u78bc\" onkeydown=\"if(event.key==='Enter')checkAdminPwd()\">\r\n      <p class=\"muted\" style=\"margin-top:8px\"><code><\/code> <code><\/code><\/p>\r\n    <\/div>\r\n    <div class=\"modal-foot\">\r\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('adminGateModal')\">\u53d6\u6d88<\/button>\r\n      <button class=\"btn btn-primary\" onclick=\"checkAdminPwd()\"><span class=\"material-icons\">login<\/span> \u9032\u5165<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- \u522a\u9664\u63d0\u554f\u5bc6\u78bc\u9a57\u8b49 Modal (\u8207\u5f8c\u53f0\u540c\u5bc6\u78bc) -->\r\n<div class=\"modal-mask\" id=\"delPwdModal\">\r\n  <div class=\"modal\" style=\"max-width:400px\">\r\n    <div class=\"modal-head\">\r\n      <h3><span class=\"material-icons\" style=\"color:var(--warn-text)\">delete_forever<\/span> \u522a\u9664\u63d0\u554f<\/h3>\r\n      <button class=\"modal-close\" onclick=\"closeModal('delPwdModal')\"><span class=\"material-icons\">close<\/span><\/button>\r\n    <\/div>\r\n    <div class=\"modal-body\">\r\n      <div class=\"note danger\"><b>\u6b64\u64cd\u4f5c\u7121\u6cd5\u5fa9\u539f<\/b>\uff0c\u5c07\u4e00\u4f75\u522a\u9664\u8a72\u63d0\u554f\u7684\u6240\u6709\u56de\u61c9\u3002<\/div>\r\n      <p class=\"muted\">\u8acb\u8f38\u5165\u7ba1\u7406\u54e1\u5bc6\u78bc (\u8207\u9032\u5165\u5f8c\u53f0\u76f8\u540c) \u4ee5\u78ba\u8a8d\u522a\u9664\u3002<\/p>\r\n      <input type=\"password\" id=\"delPwd\" placeholder=\"\u7ba1\u7406\u54e1\u5bc6\u78bc\" onkeydown=\"if(event.key==='Enter')confirmDeleteQuestion()\">\r\n    <\/div>\r\n    <div class=\"modal-foot\">\r\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('delPwdModal')\">\u53d6\u6d88<\/button>\r\n      <button class=\"btn btn-warn\" onclick=\"confirmDeleteQuestion()\"><span class=\"material-icons\">delete<\/span> \u78ba\u8a8d\u522a\u9664<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- \u901a\u7528\u78ba\u8a8d Modal -->\r\n<div class=\"modal-mask\" id=\"confirmModal\">\r\n  <div class=\"modal\" style=\"max-width:400px\">\r\n    <div class=\"modal-head\">\r\n      <h3 id=\"confirmTitle\"><span class=\"material-icons\">help<\/span> \u78ba\u8a8d<\/h3>\r\n    <\/div>\r\n    <div class=\"modal-body\"><p id=\"confirmMsg\"><\/p><\/div>\r\n    <div class=\"modal-foot\">\r\n      <button class=\"btn btn-ghost\" onclick=\"closeModal('confirmModal')\">\u53d6\u6d88<\/button>\r\n      <button class=\"btn btn-warn\" id=\"confirmOkBtn\">\u78ba\u5b9a<\/button>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- Toast \u5bb9\u5668 -->\r\n<div id=\"toast-wrap\"><\/div>\r\n\r\n<!-- PDF \u5217\u5370\u6e32\u67d3\u5bb9\u5668 (\u5e73\u6642\u96b1\u85cf\uff0c\u6309\u4e0b\u4e0b\u8f09\u6642\u586b\u5165\u7cbe\u7f8e\u6392\u7248\u4e26\u547c\u53eb\u5217\u5370) -->\r\n<div id=\"pdfRender\"><\/div>\r\n\r\n<!-- ============================================================== -->\r\n<!-- ====================  Firebase SDK (v8 Compat)  ============== -->\r\n<!-- ============================================================== -->\r\n<script src=\"https:\/\/www.gstatic.com\/firebasejs\/8.10.1\/firebase-app.js\"><\/script>\r\n<script src=\"https:\/\/www.gstatic.com\/firebasejs\/8.10.1\/firebase-auth.js\"><\/script>\r\n<script src=\"https:\/\/www.gstatic.com\/firebasejs\/8.10.1\/firebase-firestore.js\"><\/script>\r\n<!-- PDF \u76f4\u63a5\u4e0b\u8f09\u6240\u9700\uff1ahtml2canvas (\u64f7\u53d6\u6392\u7248) + jsPDF (\u8f38\u51fa PDF) -->\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/html2canvas\/1.4.1\/html2canvas.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\r\n\r\n<script>\r\n\/* =====================================================================\r\n   \u2605\u2605\u2605\u2605\u2605  Firebase \u8a2d\u5b9a (\u9810\u8a2d\u503c \/ Hardcoded)  \u2605\u2605\u2605\u2605\u2605\r\n   ---------------------------------------------------------------------\r\n   \u4f7f\u7528\u8005\u8acb\u76f4\u63a5\u4fee\u6539\u4e0b\u65b9\u516d\u884c\u3010\u5f15\u865f\u5167\u3011\u7684\u6578\u503c\uff0c\u586b\u5165\u60a8\u771f\u5be6\u7684 Firebase \u8a2d\u5b9a\u5373\u53ef\r\n   (\u7121\u9700\u66f4\u52d5\u8b8a\u6578\u540d\u7a31\u6216\u5927\u62ec\u865f)\u3002\r\n   \u4f8b\u5982\u628a apiKey: \"demo\" \u6539\u6210 apiKey: \"AIzaSyXXXX...\"\uff0c\u5176\u9918\u4e94\u884c\u6bd4\u7167\u8fa6\u7406\u3002\r\n   \u82e5\u7dad\u6301 \"demo\" \u4e0d\u8b8a\uff0c\u7a0b\u5f0f\u6703\u6539\u7528\u7db2\u5740\u53c3\u6578\u6216\u672c\u6a5f(LocalStorage)\u8a2d\u5b9a\u3002\r\n   ===================================================================== *\/\r\nconst firebaseConfig = {\r\n  apiKey: \"AIzaSyDX3juxLNI4LbwJdHuTJOv1tvQqVegqW0M\",\r\n  authDomain: \"aifed2026.firebaseapp.com\",\r\n  projectId: \"aifed2026\",\r\n  storageBucket: \"aifed2026.firebasestorage.app\",\r\n  messagingSenderId: \"4345096676\",\r\n  appId: \"1:4345096676:web:d0c15f0dbadc3bff53bff1\"\r\n};\r\n\r\n\/* ===== \u7ba1\u7406\u54e1\u5bc6\u78bc (\u524d\u7aef\u6bd4\u5c0d\uff1b\u6b63\u5f0f\u4f7f\u7528\u8acb\u4fee\u6539) ===== *\/\r\nconst ADMIN_PASSWORD = \"tpet\";\r\n\r\n\/* ===== tpet \u7d71\u4e00\u8a8d\u8b49\u63a5\u53e3 (\u9810\u7559\u4f4d\u7f6e) =================================\r\n   \u9700\u6c42\u63d0\u53ca\u300c\u6574\u5408 tpet \u7d71\u4e00\u8a8d\u8b49\u63a5\u53e3\u300d\u3002\u7d14\u524d\u7aef\u55ae\u6a94 App \u7121\u6cd5\u5b89\u5168\u4e32\u63a5\u5916\u90e8\r\n   SSO\uff0c\u6545\u6b64\u8655\u9810\u7559\u63a5\u53e3\u51fd\u5f0f\u3002\u82e5\u65e5\u5f8c\u8981\u6539\u7528 tpet \u9a57\u8b49\uff0c\u65bc\u6b64\u5be6\u4f5c\u5373\u53ef\u3002\r\n   ================================================================= *\/\r\nfunction tpetAuthenticate(\/* token *\/) {\r\n    \/\/ TODO: \u5728\u6b64\u547c\u53eb tpet \u8a8d\u8b49\u7aef\u9ede\u9a57\u8b49\u7ba1\u7406\u54e1\u8eab\u5206\u5f8c return true\/false\r\n    return null; \/\/ \u56de\u50b3 null \u4ee3\u8868\u672a\u555f\u7528\uff0c\u6cbf\u7528\u524d\u7aef\u5bc6\u78bc\u6bd4\u5c0d\r\n}\r\n\r\n\/* ===== LocalStorage Key ===== *\/\r\nconst LS_KEY = \"firebaseConfig\";\r\n\r\n\/* ===== \u5168\u57df\u72c0\u614b ===== *\/\r\nlet db = null, auth = null, fbReady = false;\r\nlet currentSort = \"time\";\r\nlet currentTab = \"q\";\r\nlet searchTerm = \"\";   \/\/ \u95dc\u9375\u5b57\u641c\u5c0b\u5b57\u4e32 (\u5c0f\u5beb)\r\nlet isAdmin = false;\r\nlet cacheQuestions = [], cacheSpeakers = [], cacheReplies = {};\r\nlet settings = { askOpen: true, keywords: [], hideHelp: false, hideSettings: false };\r\nlet likedSet = new Set(JSON.parse(localStorage.getItem(\"likedQs\") || \"[]\"));\r\nlet unsub = []; \/\/ \u5373\u6642\u76e3\u807d\u89e3\u9664\u51fd\u5f0f\r\n\r\n\/* =====================================================================\r\n   \u4e00\u3001Config \u591a\u91cd\u4f86\u6e90 \u2014 \u56b4\u683c\u512a\u5148\u7d1a\r\n   \u512a\u5148\u7d1a1 URL ?config= \u2192 \u512a\u5148\u7d1a2 Hardcoded \u2192 \u512a\u5148\u7d1a3 LocalStorage\r\n   ===================================================================== *\/\r\nfunction resolveConfig(){\r\n  \/\/ --- \u512a\u5148\u7d1a 1\uff1a\u7db2\u5740\u53c3\u6578 (\u6700\u9ad8) ---\r\n  const params = new URLSearchParams(location.search);\r\n  if(params.has(\"config\")){\r\n    try{\r\n      const decoded = JSON.parse(atob(params.get(\"config\")));\r\n      if(decoded && decoded.apiKey && decoded.apiKey !== \"demo\"){\r\n        localStorage.setItem(LS_KEY, JSON.stringify(decoded));      \/\/ \u8986\u84cb\u820a\u8a2d\u5b9a\r\n        \/\/ \u6e05\u9664\u7db2\u5740\u53c3\u6578\uff0c\u907f\u514d\u5916\u6d29\u65bc\u7db2\u5740\u5217\r\n        window.history.replaceState({}, document.title, location.pathname);\r\n        toast(\"\u5df2\u5f9e\u5206\u4eab\u9023\u7d50\u532f\u5165\u8a2d\u5b9a\", \"ok\");\r\n        return decoded;\r\n      }\r\n    }catch(e){ console.warn(\"config \u53c3\u6578\u89e3\u78bc\u5931\u6557\", e); }\r\n  }\r\n  \/\/ --- \u512a\u5148\u7d1a 2\uff1a\u7a0b\u5f0f\u78bc\u5beb\u6b7b (Hardcoded) ---\r\n  if(firebaseConfig.apiKey && firebaseConfig.apiKey !== \"demo\"){\r\n    return firebaseConfig;\r\n  }\r\n  \/\/ --- \u512a\u5148\u7d1a 3\uff1a\u4ecb\u9762\u52d5\u614b\u8a2d\u5b9a (LocalStorage) ---\r\n  const saved = localStorage.getItem(LS_KEY);\r\n  if(saved){\r\n    try{\r\n      const cfg = JSON.parse(saved);\r\n      if(cfg && cfg.apiKey && cfg.apiKey !== \"demo\") return cfg;\r\n    }catch(e){}\r\n  }\r\n  \/\/ --- \u7686\u7121\u6548\uff1a\u672a\u8a2d\u5b9a ---\r\n  return null;\r\n}\r\n\r\n\/* =====================================================================\r\n   \u521d\u59cb\u5316 Firebase\uff1b\u6210\u529f\u5f8c\u624d\u57f7\u884c\u533f\u540d\u767b\u5165\r\n   ===================================================================== *\/\r\nasync function initFirebase(){\r\n  const cfg = resolveConfig();\r\n  if(!cfg){\r\n    showWelcome();\r\n    return;\r\n  }\r\n  try{\r\n    if(!firebase.apps.length) firebase.initializeApp(cfg);\r\n    db = firebase.firestore();\r\n    auth = firebase.auth();\r\n    \/\/ \u2605 \u50c5\u5728\u521d\u59cb\u5316\u6210\u529f\u5f8c\u57f7\u884c\u533f\u540d\u767b\u5165\r\n    await auth.signInAnonymously();\r\n    fbReady = true;\r\n    showFront();\r\n    startListeners();\r\n    toast(\"\u8cc7\u6599\u5eab\u9023\u7dda\u6210\u529f\", \"ok\");\r\n  }catch(err){\r\n    console.error(err);\r\n    fbReady = false;\r\n    showWelcome();\r\n    toast(\"Firebase \u9023\u7dda\u5931\u6557\uff1a\" + (err.message||err), \"err\");\r\n  }\r\n}\r\n\r\n\/* =====================================================================\r\n   \u756b\u9762\u5207\u63db\r\n   ===================================================================== *\/\r\nfunction showWelcome(){\r\n  document.getElementById(\"welcomeScreen\").style.display = \"block\";\r\n  document.getElementById(\"frontApp\").style.display = \"none\";\r\n  document.getElementById(\"adminApp\").style.display = \"none\";\r\n  document.getElementById(\"askFab\").style.display = \"none\";\r\n  setBanner(false);\r\n}\r\nfunction showFront(){\r\n  document.getElementById(\"welcomeScreen\").style.display = \"none\";\r\n  document.getElementById(\"frontApp\").style.display = \"block\";\r\n  document.getElementById(\"adminApp\").style.display = \"none\";\r\n  document.getElementById(\"askFab\").style.display = \"flex\";  \/\/ \u986f\u793a\u63d0\u554f\u6d6e\u52d5\u6309\u9215\r\n  isAdmin = false;\r\n  setBanner(true);\r\n}\r\nfunction setBanner(connected){\r\n  const b = document.getElementById(\"connBanner\");\r\n  b.innerHTML = connected\r\n    ? ''\r\n    : '<div class=\"note\"><span class=\"material-icons\" style=\"color:#FFB300\">cloud_off<\/span> \u5c1a\u672a\u9023\u7dda\u8cc7\u6599\u5eab\uff0c\u8acb\u5148\u5b8c\u6210 Firebase \u8a2d\u5b9a\u3002<\/div>';\r\n}\r\n\r\n\/* =====================================================================\r\n   \u5373\u6642\u76e3\u807d (Firestore onSnapshot = \u5373\u6642\u540c\u6b65\uff0c\u514d\u91cd\u6574)\r\n   ===================================================================== *\/\r\nfunction startListeners(){\r\n  \/\/ \u6e05\u9664\u820a\u76e3\u807d\r\n  unsub.forEach(u=>{try{u()}catch(e){}}); unsub = [];\r\n\r\n  \/\/ \u767c\u8868\u8005\r\n  unsub.push(db.collection(\"speakers\").orderBy(\"createdAt\",\"asc\")\r\n    .onSnapshot(s=>{\r\n      cacheSpeakers = s.docs.map(d=>({id:d.id,...d.data()}));\r\n      renderSpeakerSelect(); renderSpeakerList(); renderAdminSpeakers(); updateStats();\r\n    }, e=>console.error(e)));\r\n\r\n  \/\/ \u554f\u984c\r\n  unsub.push(db.collection(\"questions\").orderBy(\"createdAt\",\"desc\")\r\n    .onSnapshot(s=>{\r\n      cacheQuestions = s.docs.map(d=>({id:d.id,...d.data()}));\r\n      renderQuestions(); renderSpeakerList(); updateStats();\r\n    }, e=>console.error(e)));\r\n\r\n  \/\/ \u56de\u61c9\r\n  unsub.push(db.collection(\"replies\").orderBy(\"createdAt\",\"asc\")\r\n    .onSnapshot(s=>{\r\n      cacheReplies = {};\r\n      s.docs.forEach(d=>{\r\n        const r = {id:d.id,...d.data()};\r\n        (cacheReplies[r.questionId] = cacheReplies[r.questionId]||[]).push(r);\r\n      });\r\n      renderQuestions(); renderSpeakerList(); updateStats();\r\n    }, e=>console.error(e)));\r\n\r\n  \/\/ \u8a2d\u5b9a\u6587\u4ef6 (\u63d0\u554f\u958b\u95dc \/ \u95dc\u9375\u5b57)\r\n  unsub.push(db.collection(\"config\").doc(\"settings\")\r\n    .onSnapshot(d=>{\r\n      if(d.exists){ settings = Object.assign({askOpen:true,keywords:[],hideHelp:false,hideSettings:false}, d.data()); }\r\n      applySettings();\r\n    }, e=>console.error(e)));\r\n}\r\n\r\n\/* =====================================================================\r\n   \u5de5\u5177\uff1a\u6642\u9593\u683c\u5f0f \/ HTML \u8df3\u812b \/ \u95dc\u9375\u5b57\u6aa2\u67e5\r\n   ===================================================================== *\/\r\nfunction fmtTime(ts){\r\n  if(!ts) return \"\";\r\n  const d = ts.toDate ? ts.toDate() : new Date(ts);\r\n  const p = n=>String(n).padStart(2,\"0\");\r\n  return `${p(d.getMonth()+1)}\/${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;\r\n}\r\nfunction esc(s){return (s||\"\").replace(\/[&<>\"']\/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));}\r\nfunction hitKeyword(text){\r\n  if(!settings.keywords || !settings.keywords.length) return false;\r\n  const t = (text||\"\").toLowerCase();\r\n  return settings.keywords.some(k=>k && t.includes(k.toLowerCase()));\r\n}\r\nfunction requireDb(){\r\n  if(!fbReady){ toast(\"\u8acb\u5148\u5b8c\u6210 Firebase \u8a2d\u5b9a\u518d\u64cd\u4f5c\", \"err\"); openModal(\"settingsModal\"); return false; }\r\n  return true;\r\n}\r\n\r\n\/* =====================================================================\r\n   \u4e8c\u3001\u524d\u53f0 \u2014 \u63d0\u554f\r\n   ===================================================================== *\/\r\nfunction renderSpeakerSelect(){\r\n  const sel = document.getElementById(\"targetSpeaker\");\r\n  const cur = sel.value;\r\n  \/\/ \u4f9d\u300c\u5411\u8ab0\u63d0\u554f\u300d\u641c\u5c0b\u6846\u95dc\u9375\u5b57\u904e\u6ffe (\u59d3\u540d\u6216\u4e3b\u984c)\r\n  const term = (document.getElementById(\"targetSearch\")?.value || \"\").trim().toLowerCase();\r\n  const list = term\r\n    ? cacheSpeakers.filter(sp=> sp.name.toLowerCase().includes(term) || (sp.topic||\"\").toLowerCase().includes(term))\r\n    : cacheSpeakers;\r\n  sel.innerHTML = '<option value=\"\">\u2014 \u5168\u9ad4 \/ \u4e0d\u6307\u5b9a \u2014<\/option>' +\r\n    list.map(sp=>`<option value=\"${sp.id}\">${esc(sp.name)}${sp.topic?(' \u2014 '+esc(sp.topic)):''}<\/option>`).join(\"\") +\r\n    (term && !list.length ? '<option value=\"\" disabled>\uff08\u67e5\u7121\u7b26\u5408\u7684\u767c\u8868\u8005\uff09<\/option>' : '');\r\n  \/\/ \u7dad\u6301\u539f\u9078\u64c7 (\u82e5\u4ecd\u5728\u904e\u6ffe\u7d50\u679c\u5167)\uff1b\u5426\u5247\u4fdd\u7559\u7a7a\u503c\r\n  sel.value = list.some(sp=>sp.id===cur) ? cur : \"\";\r\n}\r\n\/\/ \u300c\u5411\u8ab0\u63d0\u554f\u300d\u641c\u5c0b\u6846\u8f38\u5165\u6642\u5373\u6642\u904e\u6ffe\u4e0b\u62c9\u9078\u55ae\r\nfunction filterTargetSpeaker(){\r\n  renderSpeakerSelect();\r\n  const sel = document.getElementById(\"targetSpeaker\");\r\n  const opts = [...sel.options].filter(o=>o.value);\r\n  \/\/ \u53ea\u6709\u4e00\u4f4d\u7b26\u5408\u6642\u81ea\u52d5\u9078\u53d6\uff0c\u65b9\u4fbf\u5feb\u901f\u63d0\u554f\r\n  if(opts.length===1){ sel.value = opts[0].value; }\r\n}\r\n\r\n\/\/ \u958b\u555f\u300c\u6211\u8981\u63d0\u554f\u300d\u6d6e\u5c64\r\nfunction openAskModal(){\r\n  if(!requireDb()) return;\r\n  if(!settings.askOpen){\r\n    \/\/ \u4ecd\u958b\u555f\u6d6e\u5c64\uff0c\u4f46\u986f\u793a\u95dc\u9589\u63d0\u793a (applySettings \u5df2\u5207\u63db\u5167\u90e8\u986f\u793a)\r\n    openModal(\"askModal\");\r\n    toast(\"\u4e3b\u8fa6\u55ae\u4f4d\u76ee\u524d\u5df2\u95dc\u9589\u63d0\u554f\u5165\u53e3\",\"info\");\r\n    return;\r\n  }\r\n  \/\/ \u91cd\u7f6e\u300c\u5411\u8ab0\u63d0\u554f\u300d\u641c\u5c0b\u6846\uff0c\u4e0b\u62c9\u986f\u793a\u5b8c\u6574\u540d\u55ae\r\n  document.getElementById(\"targetSearch\").value = \"\";\r\n  renderSpeakerSelect();\r\n  openModal(\"askModal\");\r\n  setTimeout(()=>document.getElementById(\"qContent\").focus(),200);\r\n}\r\n\r\n\/* ---------- \u95dc\u9375\u5b57\u641c\u5c0b ---------- *\/\r\nfunction onSearch(){\r\n  searchTerm = document.getElementById(\"searchInput\").value.trim().toLowerCase();\r\n  document.getElementById(\"searchClear\").style.display = searchTerm ? \"flex\" : \"none\";\r\n  renderQuestions();\r\n  renderSpeakerList();\r\n}\r\nfunction clearSearch(){\r\n  document.getElementById(\"searchInput\").value = \"\";\r\n  searchTerm = \"\";\r\n  document.getElementById(\"searchClear\").style.display = \"none\";\r\n  renderQuestions();\r\n  renderSpeakerList();\r\n}\r\nfunction matchSearch(q){\r\n  if(!searchTerm) return true;\r\n  const hay = [q.content, q.askerName, q.targetName].join(\" \").toLowerCase();\r\n  return hay.includes(searchTerm);\r\n}\r\n\/\/ \u5728\u5df2 esc \u904e\u7684 HTML \u4e0a\u6a19\u4eae\u95dc\u9375\u5b57\r\nfunction highlight(escapedHtml){\r\n  if(!searchTerm) return escapedHtml;\r\n  const safe = searchTerm.replace(\/[.*+?^${}()|[\\]\\\\]\/g,\"\\\\$&\");\r\n  return escapedHtml.replace(new RegExp(\"(\"+safe+\")\",\"gi\"), \"<mark>$1<\/mark>\");\r\n}\r\n\r\n\/\/ \u5c07\u4f7f\u7528\u8005\u8f38\u5165\u6b63\u898f\u5316\u70ba\u6709\u6548\u7db2\u5740 (\u6c92\u6709 http \u524d\u7db4\u6642\u81ea\u52d5\u88dc https:\/\/)\uff1b\u7121\u6548\u5247\u56de\u50b3\u7a7a\u5b57\u4e32\r\nfunction normalizeLink(raw){\r\n  let s = (raw||\"\").trim();\r\n  if(!s) return \"\";\r\n  if(!\/^https?:\\\/\\\/\/i.test(s)) s = \"https:\/\/\" + s;\r\n  try{ const u = new URL(s); return (u.protocol===\"http:\"||u.protocol===\"https:\") ? u.href : \"\"; }\r\n  catch(e){ return \"\"; }\r\n}\r\n\r\nasync function submitQuestion(){\r\n  if(!requireDb()) return;\r\n  if(!settings.askOpen){ toast(\"\u63d0\u554f\u5165\u53e3\u5df2\u95dc\u9589\", \"err\"); return; }\r\n  const content = document.getElementById(\"qContent\").value.trim();\r\n  if(!content){ toast(\"\u8acb\u8f38\u5165\u554f\u984c\u5167\u5bb9\", \"err\"); return; }\r\n  if(content.length > 800){ toast(\"\u5167\u5bb9\u904e\u9577\", \"err\"); return; }\r\n\r\n  const name = document.getElementById(\"askerName\").value.trim();\r\n  if(!name){ toast(\"\u8acb\u8f38\u5165\u60a8\u7684\u7a31\u547c\", \"err\"); return; }\r\n  const targetId = document.getElementById(\"targetSpeaker\").value;\r\n  const targetSp = cacheSpeakers.find(s=>s.id===targetId);\r\n\r\n  \/\/ \u9644\u52a0\u7db2\u5740\uff1a\u9a57\u8b49\u4e26\u6b63\u898f\u5316\r\n  const rawLink = document.getElementById(\"qLink\").value.trim();\r\n  const link = normalizeLink(rawLink);\r\n  if(rawLink && !link){ toast(\"\u9644\u52a0\u7db2\u5740\u683c\u5f0f\u4e0d\u6b63\u78ba\", \"err\"); return; }\r\n\r\n  \/\/ \u95dc\u9375\u5b57\u5be9\u6838 \u2192 \u547d\u4e2d\u5247\u9810\u8a2d\u96b1\u85cf\r\n  const hidden = hitKeyword(content) || hitKeyword(name);\r\n\r\n  try{\r\n    await db.collection(\"questions\").add({\r\n      content,\r\n      askerName: name,\r\n      anon: false,\r\n      targetId: targetId || \"\",\r\n      targetName: targetSp ? targetSp.name : \"\",\r\n      link: link || \"\",\r\n      likes: 0,\r\n      hidden,\r\n      createdAt: firebase.firestore.FieldValue.serverTimestamp()\r\n    });\r\n    \/\/ \u6e05\u7a7a\u8868\u55ae\u4e26\u95dc\u9589\u63d0\u554f\u6d6e\u5c64\r\n    document.getElementById(\"qContent\").value = \"\";\r\n    document.getElementById(\"qLink\").value = \"\";\r\n    document.getElementById(\"askerName\").value = \"\";\r\n    document.getElementById(\"targetSearch\").value = \"\";\r\n    closeModal(\"askModal\");\r\n    toast(hidden ? \"\u5df2\u9001\u51fa\uff0c\u5167\u5bb9\u5f85\u5be9\u6838\u4e2d\" : \"\u63d0\u554f\u9001\u51fa\u6210\u529f\uff01\", hidden?\"info\":\"ok\");\r\n  }catch(e){ toast(\"\u9001\u51fa\u5931\u6557\uff1a\" + e.message, \"err\"); }\r\n}\r\n\r\n\/* ---------- \u6392\u5e8f \/ \u5206\u9801 ---------- *\/\r\nfunction setSort(s){\r\n  currentSort = s;\r\n  document.getElementById(\"sortTime\").classList.toggle(\"btn-primary\", s===\"time\");\r\n  document.getElementById(\"sortLike\").classList.toggle(\"btn-primary\", s===\"like\");\r\n  document.getElementById(\"sortTime\").classList.toggle(\"btn-ghost\", s!==\"time\");\r\n  document.getElementById(\"sortLike\").classList.toggle(\"btn-ghost\", s!==\"like\");\r\n  renderQuestions();\r\n}\r\nfunction switchTab(t){\r\n  currentTab = t;\r\n  document.getElementById(\"tabQ\").classList.toggle(\"active\", t===\"q\");\r\n  document.getElementById(\"tabA\").classList.toggle(\"active\", t===\"a\");\r\n  document.getElementById(\"tabS\").classList.toggle(\"active\", t===\"s\");\r\n  document.getElementById(\"qListView\").style.display = t===\"q\"?\"block\":\"none\";\r\n  document.getElementById(\"aListView\").style.display = t===\"a\"?\"block\":\"none\";\r\n  document.getElementById(\"sListView\").style.display = t===\"s\"?\"block\":\"none\";\r\n  if(t===\"q\") renderQuestions();\r\n  else if(t===\"a\") renderAllQuestions();\r\n  else if(t===\"s\") renderSpeakerList();\r\n}\r\n\r\n\/* ---------- \u6e32\u67d3\uff1a\u63d0\u554f\u6e05\u55ae ---------- *\/\r\nfunction visibleQuestions(){\r\n  \/\/ \u5f8c\u53f0\u770b\u5168\u90e8\uff1b\u524d\u53f0\u53ea\u770b\u672a\u96b1\u85cf\r\n  return cacheQuestions.filter(q=> isAdmin ? true : !q.hidden);\r\n}\r\nfunction sortedQuestions(){\r\n  const arr = visibleQuestions().filter(matchSearch);\r\n  if(currentSort===\"like\") arr.sort((a,b)=>(b.likes||0)-(a.likes||0));\r\n  else arr.sort((a,b)=>tsNum(b.createdAt)-tsNum(a.createdAt));\r\n  return arr;\r\n}\r\nfunction tsNum(ts){ return ts&&ts.toMillis ? ts.toMillis() : 0; }\r\n\r\nfunction renderQuestions(){\r\n  const box = document.getElementById(\"allQuestionList\");\r\n  \/\/ \u907f\u514d\u91cd\u8907 element id\uff1a\u53ea\u6e32\u67d3\u76ee\u524d\u4f5c\u7528\u4e2d\u7684\u63d0\u554f\u5206\u9801\uff0c\u53e6\u4e00\u500b\u6e05\u7a7a\r\n  if(currentTab===\"a\"){ document.getElementById(\"questionList\").innerHTML=\"\"; renderAllQuestions(); return; }\r\n  if(box) box.innerHTML=\"\";\r\n  renderQuestionListInto(\"questionList\", sortedQuestions(), false);\r\n}\r\n\r\n\/\/ \u5168\u9ad4\u63d0\u554f\uff1a\u5411\u8ab0\u63d0\u554f\u9078\u70ba\u300c\u5168\u9ad4 \/ \u4e0d\u6307\u5b9a\u300d(targetId \u70ba\u7a7a) \u7684\u554f\u984c\r\nfunction renderAllQuestions(){\r\n  document.getElementById(\"questionList\").innerHTML=\"\";  \/\/ \u6e05\u7a7a\u53e6\u4e00\u5206\u9801\u907f\u514d id \u885d\u7a81\r\n  renderQuestionListInto(\"allQuestionList\", sortedQuestions().filter(q=>!q.targetId), true);\r\n}\r\n\r\n\/\/ \u5171\u7528\u6e32\u67d3\uff1a\u5c07\u554f\u984c\u9663\u5217\u6e32\u67d3\u5230\u6307\u5b9a\u5bb9\u5668\r\nfunction renderQuestionListInto(boxId, arr, isAll){\r\n  const box = document.getElementById(boxId);\r\n  if(!box) return;\r\n  if(!arr.length){\r\n    box.innerHTML = searchTerm\r\n      ? `<div class=\"empty\"><span class=\"material-icons\">search_off<\/span>\u627e\u4e0d\u5230\u7b26\u5408\u300c${esc(searchTerm)}\u300d\u7684${isAll?'\u5168\u9ad4':''}\u63d0\u554f\u3002<\/div>`\r\n      : (isAll\r\n          ? `<div class=\"empty\"><span class=\"material-icons\">public<\/span>\u76ee\u524d\u6c92\u6709\u5411\u5168\u9ad4\u7684\u63d0\u554f\u3002<\/div>`\r\n          : `<div class=\"empty\"><span class=\"material-icons\">forum<\/span>\u76ee\u524d\u9084\u6c92\u6709\u554f\u984c\uff0c\u6436\u982d\u9999\u5427\uff01<\/div>`);\r\n    return;\r\n  }\r\n  box.innerHTML = arr.map(q=>questionCardHTML(q)).join(\"\");\r\n}\r\n\r\nfunction questionCardHTML(q){\r\n  const replies = (cacheReplies[q.id]||[]);\r\n  const liked = likedSet.has(q.id);\r\n  const askerDisplay = q.anon || !q.askerName\r\n      ? '<span class=\"anon\">\ud83d\ude48 \u533f\u540d<\/span>'\r\n      : highlight(esc(q.askerName));\r\n  const targetTag = q.targetName\r\n      ? `<div class=\"q-target\"><span class=\"material-icons\" style=\"font-size:15px\">arrow_forward<\/span>\u5411\u8ab0\u63d0\u554f\uff1a${highlight(esc(q.targetName))}<\/div>`\r\n      : '';\r\n  const linkHtml = q.link ? `<a class=\"q-link\" href=\"${esc(q.link)}\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n      <span class=\"material-icons\">link<\/span><span class=\"q-link-text\">${esc(q.link)}<\/span>\r\n    <\/a>` : '';\r\n\r\n  \/\/ \u5f8c\u53f0\u5de5\u5177\u5217\r\n  const adminTools = isAdmin ? `\r\n    <div class=\"admin-q-tools\">\r\n      <button class=\"icon-btn\" onclick=\"toggleHideQ('${q.id}',${!q.hidden})\">\r\n        <span class=\"material-icons\">${q.hidden?'visibility':'visibility_off'}<\/span>${q.hidden?'\u9084\u539f':'\u96b1\u85cf'}\r\n      <\/button>\r\n      <button class=\"icon-btn danger\" onclick=\"deleteQ('${q.id}')\">\r\n        <span class=\"material-icons\">delete<\/span>\u522a\u9664\r\n      <\/button>\r\n    <\/div>` : '';\r\n\r\n  return `\r\n  <div class=\"q-card ${q.hidden?'hidden-q':''}\">\r\n    <div class=\"q-head\">\r\n      <div>\r\n        <div class=\"q-asker\">${askerDisplay}${q.hidden?' <span style=\"color:var(--warn);font-size:.75rem\">[\u5df2\u96b1\u85cf]<\/span>':''}<\/div>\r\n        ${targetTag}\r\n      <\/div>\r\n      <div class=\"q-time\">${fmtTime(q.createdAt)}<\/div>\r\n    <\/div>\r\n    <div class=\"q-body\">${highlight(esc(q.content))}<\/div>\r\n    ${linkHtml}\r\n    <div class=\"q-actions\">\r\n      <button class=\"like-btn ${liked?'liked':''}\" onclick=\"toggleLike('${q.id}')\">\r\n        <span class=\"material-icons\">${liked?'favorite':'favorite_border'}<\/span> ${q.likes||0}\r\n      <\/button>\r\n      <button class=\"reply-toggle\" onclick=\"toggleReplyForm('${q.id}')\">\r\n        <span class=\"material-icons\">chat_bubble_outline<\/span> \u56de\u61c9 (${replies.length})\r\n      <\/button>\r\n      <button class=\"pdf-btn\" onclick=\"downloadQuestionPDF('${q.id}')\" title=\"\u4e0b\u8f09\u554f\u984c\u8207\u6240\u6709\u56de\u61c9 PDF\">\r\n        <span class=\"material-icons\">picture_as_pdf<\/span> \u4e0b\u8f09 PDF\r\n      <\/button>\r\n      <button class=\"del-btn\" onclick=\"requestDeleteQuestion('${q.id}')\" title=\"\u522a\u9664\u6b64\u63d0\u554f (\u9700\u5bc6\u78bc)\">\r\n        <span class=\"material-icons\">delete_outline<\/span> \u522a\u9664\r\n      <\/button>\r\n    <\/div>\r\n    ${adminTools}\r\n    <div class=\"replies\" ${replies.length?'':'style=\"display:none\"'} id=\"replies-${q.id}\">\r\n      ${repliesTreeHTML(q.id)}\r\n    <\/div>\r\n    <div class=\"reply-form\" id=\"rform-${q.id}\">\r\n      <div class=\"row\">\r\n        <input type=\"text\" id=\"rname-${q.id}\" placeholder=\"\u60a8\u7684\u59d3\u540d (\u5fc5\u586b)\">\r\n      <\/div>\r\n      <textarea id=\"rbody-${q.id}\" rows=\"2\" placeholder=\"\u8f38\u5165\u56de\u61c9\u2026\" style=\"margin-top:8px\"><\/textarea>\r\n      <button class=\"btn btn-secondary btn-block\" style=\"margin-top:8px\" onclick=\"submitReply('${q.id}','${q.id}','')\">\r\n        <span class=\"material-icons\">send<\/span> \u9001\u51fa\u56de\u61c9\r\n      <\/button>\r\n    <\/div>\r\n  <\/div>`;\r\n}\r\n\r\n\/\/ \u5c07\u67d0\u554f\u984c\u7684\u6240\u6709\u56de\u61c9 (\u6241\u5e73) \u7d44\u6210 parentId \u6a39\u72c0\u7d50\u69cb\uff0c\u56de\u50b3\u9802\u5c64\u56de\u61c9\u7684 HTML\r\n\/\/ ns\uff1a\u547d\u540d\u7a7a\u9593\u524d\u7db4 (\u63d0\u554f\u6e05\u55ae\u7528 \"q\"\u3001\u767c\u8868\u8005\u6e05\u55ae\u7528 \"sp\")\uff0c\u907f\u514d\u5169\u5206\u9801\u7684\u8868\u55ae id \u885d\u7a81\r\nfunction repliesTreeHTML(qid, ns){\r\n  ns = ns || \"q\";\r\n  const all = (cacheReplies[qid]||[]);\r\n  const byParent = {};\r\n  all.forEach(r=>{ const p=r.parentId||\"\"; (byParent[p]=byParent[p]||[]).push(r); });\r\n  Object.values(byParent).forEach(a=>a.sort((x,y)=>tsNum(x.createdAt)-tsNum(y.createdAt)));\r\n  return (byParent[\"\"]||[]).map(r=>replyHTML(r, qid, byParent, ns)).join(\"\");\r\n}\r\n\/\/ \u55ae\u5247\u56de\u61c9 + \u5176\u5b50\u56de\u61c9 + \u300c\u56de\u8986\u300d\u8868\u55ae (\u905e\u8ff4)\r\nfunction replyHTML(r, qid, byParent, ns){\r\n  const speakerBadge = r.isSpeaker ? '<span class=\"speaker-badge\">\u767c\u8868\u8005\u672c\u4eba<\/span>' : '';\r\n  const children = (byParent[r.id]||[]).map(c=>replyHTML(c, qid, byParent, ns)).join(\"\");\r\n  const fk = ns+\"-\"+r.id;   \/\/ \u547d\u540d\u7a7a\u9593 + reply id\r\n  return `\r\n    <div class=\"reply\">\r\n      <div class=\"r-name\">${highlight(esc(r.name))}${speakerBadge} <span class=\"r-time\">${fmtTime(r.createdAt)}<\/span><\/div>\r\n      <div class=\"r-body\">${highlight(esc(r.content))}<\/div>\r\n      <button class=\"reply-to-btn\" onclick=\"toggleReplyTo('${fk}')\">\r\n        <span class=\"material-icons\">reply<\/span> \u56de\u8986\r\n      <\/button>\r\n      <div class=\"reply-form reply-form-sm\" id=\"rto-${fk}\">\r\n        <input type=\"text\" id=\"rname-${fk}\" placeholder=\"\u60a8\u7684\u59d3\u540d (\u5fc5\u586b)\">\r\n        <textarea id=\"rbody-${fk}\" rows=\"2\" placeholder=\"\u56de\u8986 ${esc(r.name)}\u2026\" style=\"margin-top:6px\"><\/textarea>\r\n        <button class=\"btn btn-secondary btn-block\" style=\"margin-top:6px\" onclick=\"submitReply('${qid}','${fk}','${r.id}')\">\r\n          <span class=\"material-icons\">send<\/span> \u9001\u51fa\u56de\u8986\r\n        <\/button>\r\n      <\/div>\r\n      ${children ? `<div class=\"reply-children\">${children}<\/div>` : ''}\r\n    <\/div>`;\r\n}\r\n\r\nfunction toggleReplyForm(qid){\r\n  document.getElementById(\"rform-\"+qid).classList.toggle(\"show\");\r\n}\r\n\r\n\/* ---------- \u9ede\u8b9a ---------- *\/\r\nasync function toggleLike(qid){\r\n  if(!requireDb()) return;\r\n  const liked = likedSet.has(qid);\r\n  try{\r\n    await db.collection(\"questions\").doc(qid).update({\r\n      likes: firebase.firestore.FieldValue.increment(liked?-1:1)\r\n    });\r\n    if(liked) likedSet.delete(qid); else likedSet.add(qid);\r\n    localStorage.setItem(\"likedQs\", JSON.stringify([...likedSet]));\r\n  }catch(e){ toast(\"\u64cd\u4f5c\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\r\n\/* ---------- \u56de\u61c9 (\u591a\u5c64\u5de2\u72c0\u7559\u8a00\u4e32 \/ \u81ea\u52d5\u6a19\u8a3b\u767c\u8868\u8005) ----------\r\n   formKey\uff1a\u9802\u5c64\u56de\u61c9\u7528 qid\uff1b\u91dd\u5c0d\u56de\u61c9\u518d\u56de\u61c9\u7528 reply \u7684 id\u3002\r\n   parentId\uff1a\u88ab\u56de\u8986\u7684 reply id (\u9802\u5c64\u70ba \"\")\u3002               *\/\r\nasync function submitReply(qid, formKey, parentId){\r\n  if(!requireDb()) return;\r\n  const name = document.getElementById(\"rname-\"+formKey).value.trim();\r\n  const content = document.getElementById(\"rbody-\"+formKey).value.trim();\r\n  if(!name){ toast(\"\u56de\u61c9\u9700\u586b\u5beb\u59d3\u540d\",\"err\"); return; }\r\n  if(!content){ toast(\"\u8acb\u8f38\u5165\u56de\u61c9\u5167\u5bb9\",\"err\"); return; }\r\n  \/\/ \u81ea\u52d5\u6a19\u8a3b\u300c\u767c\u8868\u8005\u672c\u4eba\u300d\uff1a\u59d3\u540d\u8207\u767c\u8868\u8005\u540d\u55ae\u76f8\u7b26\r\n  const isSpeaker = cacheSpeakers.some(s=>s.name && s.name.trim()===name);\r\n  \/\/ \u95dc\u9375\u5b57\u5be9\u6838\r\n  if(hitKeyword(content)||hitKeyword(name)){ toast(\"\u5167\u5bb9\u542b\u9650\u5236\u5b57\u8a5e\uff0c\u7121\u6cd5\u9001\u51fa\",\"err\"); return; }\r\n  try{\r\n    await db.collection(\"replies\").add({\r\n      questionId: qid, parentId: parentId || \"\", name, content, isSpeaker,\r\n      createdAt: firebase.firestore.FieldValue.serverTimestamp()\r\n    });\r\n    document.getElementById(\"rbody-\"+formKey).value = \"\";\r\n    toast(\"\u56de\u61c9\u9001\u51fa\u6210\u529f\uff01\",\"ok\");\r\n  }catch(e){ toast(\"\u9001\u51fa\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\/\/ \u5207\u63db\u300c\u91dd\u5c0d\u67d0\u5247\u56de\u61c9\u518d\u56de\u61c9\u300d\u7684\u8868\u55ae\r\nfunction toggleReplyTo(rid){\r\n  const el = document.getElementById(\"rto-\"+rid);\r\n  if(el) el.classList.toggle(\"show\");\r\n}\r\n\r\n\/* =====================================================================\r\n   \u767c\u8868\u8005\u6e05\u55ae\u5206\u9801 \u2014 \u5c08\u5bb6\u77e5\u8b58\u5eab\r\n   ===================================================================== *\/\r\nfunction renderSpeakerList(){\r\n  const box = document.getElementById(\"speakerList\");\r\n  if(!cacheSpeakers.length){\r\n    box.innerHTML = `<div class=\"empty\"><span class=\"material-icons\">groups<\/span>\u5c1a\u672a\u5efa\u7acb\u767c\u8868\u8005\u540d\u55ae\u3002<\/div>`;\r\n    return;\r\n  }\r\n  \/\/ \u641c\u5c0b\u6642\uff1a\u767c\u8868\u8005\u540d\u7a31\u547d\u4e2d\u5247\u4fdd\u7559\u5168\u90e8\u63d0\u554f\uff0c\u5426\u5247\u53ea\u7559\u547d\u4e2d\u63d0\u554f\r\n  const list = searchTerm\r\n    ? cacheSpeakers.filter(sp=>{\r\n        const nameHit = sp.name.toLowerCase().includes(searchTerm) || (sp.topic||\"\").toLowerCase().includes(searchTerm);\r\n        const hasQ = visibleQuestions().some(q=>q.targetId===sp.id && matchSearch(q));\r\n        return nameHit || hasQ;\r\n      })\r\n    : cacheSpeakers;\r\n  if(!list.length){\r\n    box.innerHTML = `<div class=\"empty\"><span class=\"material-icons\">search_off<\/span>\u627e\u4e0d\u5230\u7b26\u5408\u300c${esc(searchTerm)}\u300d\u7684\u767c\u8868\u8005\u6216\u63d0\u554f\u3002<\/div>`;\r\n    return;\r\n  }\r\n  box.innerHTML = list.map(sp=>{\r\n    const nameHit = searchTerm && (sp.name.toLowerCase().includes(searchTerm) || (sp.topic||\"\").toLowerCase().includes(searchTerm));\r\n    let qs = visibleQuestions().filter(q=>q.targetId===sp.id);\r\n    if(searchTerm && !nameHit) qs = qs.filter(matchSearch);\r\n    const inner = qs.length\r\n      ? qs.map(q=>speakerQHTML(q)).join(\"\")\r\n      : '<p class=\"muted\" style=\"padding:8px 0\">\u5c1a\u7121\u6307\u5b9a\u7d66\u9019\u4f4d\u767c\u8868\u8005\u7684\u63d0\u554f\u3002<\/p>';\r\n    return `\r\n    <div class=\"speaker-item\">\r\n      <div class=\"speaker-head static\">\r\n        <div>\r\n          <div class=\"speaker-name\"><span class=\"material-icons\" style=\"color:var(--primary)\">person<\/span>${highlight(esc(sp.name))}<\/div>\r\n          <div class=\"speaker-topic\">${highlight(esc(sp.topic||'(\u672a\u8a2d\u4e3b\u984c)'))}<\/div>\r\n        <\/div>\r\n        <span class=\"speaker-count\">${qs.length} \u984c<\/span>\r\n      <\/div>\r\n      <div class=\"speaker-body open\" id=\"sp-body-${sp.id}\">${inner}<\/div>\r\n    <\/div>`;\r\n  }).join(\"\");\r\n}\r\nfunction speakerQHTML(q){\r\n  const asker = q.anon||!q.askerName ? '\u533f\u540d' : highlight(esc(q.askerName));\r\n  const replies = (cacheReplies[q.id]||[]);\r\n  \/\/ \u7528 sp- \u524d\u7db4\u907f\u514d\u8207\u63d0\u554f\u6e05\u55ae\u5206\u9801\u7684\u8868\u55ae id \u885d\u7a81\r\n  const fk = \"sp-\"+q.id;\r\n  return `\r\n    <div class=\"q-card\" style=\"border-left-color:var(--secondary);box-shadow:none;margin:8px 0\">\r\n      <div class=\"q-head\"><div class=\"q-asker\">${asker}<\/div><div class=\"q-time\">${fmtTime(q.createdAt)}<\/div><\/div>\r\n      <div class=\"q-body\">${highlight(esc(q.content))}<\/div>\r\n      ${q.link?`<a class=\"q-link\" href=\"${esc(q.link)}\" target=\"_blank\" rel=\"noopener noreferrer\"><span class=\"material-icons\">link<\/span><span class=\"q-link-text\">${esc(q.link)}<\/span><\/a>`:''}\r\n      <div class=\"replies\" ${replies.length?'':'style=\"border:none;padding:0\"'}>${repliesTreeHTML(q.id,\"sp\")}<\/div>\r\n      <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:6px\">\r\n        <button class=\"reply-to-btn\" onclick=\"toggleReplyTo('${fk}')\">\r\n          <span class=\"material-icons\">chat_bubble_outline<\/span> \u6211\u8981\u56de\u61c9\r\n        <\/button>\r\n        <button class=\"pdf-btn\" onclick=\"downloadQuestionPDF('${q.id}')\" title=\"\u4e0b\u8f09\u554f\u984c\u8207\u6240\u6709\u56de\u61c9 PDF\">\r\n          <span class=\"material-icons\">picture_as_pdf<\/span> \u4e0b\u8f09 PDF\r\n        <\/button>\r\n        <button class=\"del-btn\" onclick=\"requestDeleteQuestion('${q.id}')\" title=\"\u522a\u9664\u6b64\u63d0\u554f (\u9700\u5bc6\u78bc)\">\r\n          <span class=\"material-icons\">delete_outline<\/span> \u522a\u9664\r\n        <\/button>\r\n      <\/div>\r\n      <div class=\"reply-form reply-form-sm\" id=\"rto-${fk}\">\r\n        <input type=\"text\" id=\"rname-${fk}\" placeholder=\"\u60a8\u7684\u59d3\u540d (\u5fc5\u586b)\">\r\n        <textarea id=\"rbody-${fk}\" rows=\"2\" placeholder=\"\u8f38\u5165\u56de\u61c9\u2026\" style=\"margin-top:6px\"><\/textarea>\r\n        <button class=\"btn btn-secondary btn-block\" style=\"margin-top:6px\" onclick=\"submitReply('${q.id}','${fk}','')\">\r\n          <span class=\"material-icons\">send<\/span> \u9001\u51fa\u56de\u61c9\r\n        <\/button>\r\n      <\/div>\r\n    <\/div>`;\r\n}\r\nfunction toggleSpeaker(id){\r\n  document.getElementById(\"sp-body-\"+id).classList.toggle(\"open\");\r\n}\r\n\r\n\/* =====================================================================\r\n   \u4e09\u3001\u5f8c\u53f0\u7ba1\u7406\r\n   ===================================================================== *\/\r\nfunction openAdminGate(){\r\n  if(!fbReady){ toast(\"\u8acb\u5148\u5b8c\u6210 Firebase \u8a2d\u5b9a\",\"err\"); openModal(\"settingsModal\"); return; }\r\n  document.getElementById(\"adminPwd\").value = \"\";\r\n  openModal(\"adminGateModal\");\r\n  setTimeout(()=>document.getElementById(\"adminPwd\").focus(),200);\r\n}\r\nfunction checkAdminPwd(){\r\n  const pwd = document.getElementById(\"adminPwd\").value;\r\n  \/\/ tpet \u63a5\u53e3\u512a\u5148\uff1b\u672a\u555f\u7528\u5247\u56de\u9000\u524d\u7aef\u5bc6\u78bc\u6bd4\u5c0d\r\n  const tpet = tpetAuthenticate(pwd);\r\n  const pass = (tpet===true) || (tpet===null && pwd===ADMIN_PASSWORD);\r\n  if(pass){\r\n    isAdmin = true;\r\n    closeModal(\"adminGateModal\");\r\n    document.getElementById(\"frontApp\").style.display=\"none\";\r\n    document.getElementById(\"welcomeScreen\").style.display=\"none\";\r\n    document.getElementById(\"adminApp\").style.display=\"block\";\r\n    document.getElementById(\"askFab\").style.display=\"none\";\r\n    renderAdminSpeakers(); renderQuestions(); updateStats(); applySettings();\r\n    toast(\"\u5df2\u9032\u5165\u5f8c\u53f0\",\"ok\");\r\n  }else{\r\n    toast(\"\u5bc6\u78bc\u932f\u8aa4\",\"err\");\r\n  }\r\n}\r\nfunction exitAdmin(){ showFront(); renderQuestions(); }\r\n\r\nfunction updateStats(){\r\n  document.getElementById(\"statQ\").textContent = cacheQuestions.length;\r\n  let r = 0; Object.values(cacheReplies).forEach(a=>r+=a.length);\r\n  document.getElementById(\"statR\").textContent = r;\r\n  document.getElementById(\"statS\").textContent = cacheSpeakers.length;\r\n}\r\n\r\n\/* ---------- \u767c\u8868\u8005 CRUD ---------- *\/\r\nasync function addSpeaker(){\r\n  if(!requireDb()) return;\r\n  const name = document.getElementById(\"spName\").value.trim();\r\n  const topic = document.getElementById(\"spTopic\").value.trim();\r\n  if(!name){ toast(\"\u8acb\u8f38\u5165\u59d3\u540d\",\"err\"); return; }\r\n  if(cacheSpeakers.some(s=>s.name===name)){ toast(\"\u6b64\u767c\u8868\u8005\u5df2\u5b58\u5728\",\"err\"); return; }\r\n  try{\r\n    await db.collection(\"speakers\").add({name, topic, createdAt: firebase.firestore.FieldValue.serverTimestamp()});\r\n    document.getElementById(\"spName\").value=\"\"; document.getElementById(\"spTopic\").value=\"\";\r\n    toast(\"\u5df2\u65b0\u589e\u767c\u8868\u8005\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\nfunction renderAdminSpeakers(){\r\n  const box = document.getElementById(\"adminSpeakerList\");\r\n  if(!box) return;\r\n  if(!cacheSpeakers.length){ box.innerHTML='<p class=\"muted\">\u5c1a\u7121\u767c\u8868\u8005\u3002<\/p>'; return; }\r\n  box.innerHTML = cacheSpeakers.map(sp=>`\r\n    <div class=\"sp-row\">\r\n      <input type=\"text\" id=\"ed-name-${sp.id}\" value=\"${esc(sp.name)}\" placeholder=\"\u59d3\u540d\" style=\"flex:1\">\r\n      <input type=\"text\" id=\"ed-topic-${sp.id}\" value=\"${esc(sp.topic||'')}\" placeholder=\"\u4e3b\u984c\" style=\"flex:1.5\">\r\n      <button class=\"mini-btn save\" title=\"\u5132\u5b58\" onclick=\"updateSpeaker('${sp.id}')\"><span class=\"material-icons\">save<\/span><\/button>\r\n      <button class=\"mini-btn del\" title=\"\u522a\u9664\" onclick=\"deleteSpeaker('${sp.id}','${esc(sp.name)}')\"><span class=\"material-icons\">delete<\/span><\/button>\r\n    <\/div>`).join(\"\");\r\n}\r\nasync function updateSpeaker(id){\r\n  const name = document.getElementById(\"ed-name-\"+id).value.trim();\r\n  const topic = document.getElementById(\"ed-topic-\"+id).value.trim();\r\n  if(!name){ toast(\"\u59d3\u540d\u4e0d\u53ef\u7a7a\u767d\",\"err\"); return; }\r\n  try{ await db.collection(\"speakers\").doc(id).update({name,topic}); toast(\"\u5df2\u66f4\u65b0\",\"ok\"); }\r\n  catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\nfunction deleteSpeaker(id,name){\r\n  confirmDialog(\"\u522a\u9664\u767c\u8868\u8005\", `\u78ba\u5b9a\u522a\u9664\u767c\u8868\u8005\u300c${name}\u300d\u55ce\uff1f\u6b64\u64cd\u4f5c\u7121\u6cd5\u5fa9\u539f\u3002`, async()=>{\r\n    try{ await db.collection(\"speakers\").doc(id).delete(); toast(\"\u5df2\u522a\u9664\",\"ok\"); }\r\n    catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n  });\r\n}\r\n\r\n\/* ---------- CSV \u6279\u6b21\u532f\u5165 (\u81ea\u52d5\u6821\u5c0d\u53bb\u91cd) ---------- *\/\r\nfunction loadCsvFile(ev){\r\n  const f = ev.target.files[0]; if(!f) return;\r\n  const reader = new FileReader();\r\n  reader.onload = e=>{ document.getElementById(\"csvInput\").value = e.target.result; toast(\"\u5df2\u8f09\u5165\u6a94\u6848\u5167\u5bb9\uff0c\u8acb\u6309\u532f\u5165\",\"info\"); };\r\n  reader.readAsText(f, \"utf-8\");\r\n}\r\nasync function importCsv(){\r\n  if(!requireDb()) return;\r\n  const raw = document.getElementById(\"csvInput\").value.trim();\r\n  if(!raw){ toast(\"\u8acb\u8cbc\u4e0a\u6216\u9078\u64c7 CSV \u5167\u5bb9\",\"err\"); return; }\r\n  const lines = raw.split(\/\\r?\\n\/);\r\n  const existing = new Set(cacheSpeakers.map(s=>s.name));\r\n  const seen = new Set();\r\n  let ok=0, skip=0, bad=0;\r\n  const batch = db.batch();\r\n  for(let line of lines){\r\n    line = line.trim(); if(!line) continue;\r\n    \/\/ \u8df3\u904e\u6a19\u984c\u5217\r\n    if(\/^(\u59d3\u540d|name)\\s*[,\uff0c]\/i.test(line)){ continue; }\r\n    const parts = line.split(\/[,\uff0c\\t]\/).map(s=>s.trim());\r\n    const name = parts[0], topic = parts[1]||\"\";\r\n    if(!name){ bad++; continue; }                 \/\/ \u683c\u5f0f\u932f\u8aa4(\u7a7a\u767d\u59d3\u540d)\r\n    if(existing.has(name)||seen.has(name)){ skip++; continue; } \/\/ \u53bb\u91cd\r\n    seen.add(name);\r\n    const ref = db.collection(\"speakers\").doc();\r\n    batch.set(ref, {name, topic, createdAt: firebase.firestore.FieldValue.serverTimestamp()});\r\n    ok++;\r\n  }\r\n  if(!ok){ toast(`\u672a\u532f\u5165\u4efb\u4f55\u8cc7\u6599 (\u91cd\u8907${skip}\u3001\u932f\u8aa4${bad})`,\"err\"); return; }\r\n  try{\r\n    await batch.commit();\r\n    document.getElementById(\"csvInput\").value=\"\";\r\n    document.getElementById(\"csvFile\").value=\"\";\r\n    toast(`\u532f\u5165\u5b8c\u6210\uff1a\u6210\u529f ${ok}\u3001\u7565\u904e\u91cd\u8907 ${skip}\u3001\u683c\u5f0f\u932f\u8aa4 ${bad}`,\"ok\");\r\n  }catch(e){ toast(\"\u532f\u5165\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\r\n\/* ---------- \u554f\u984c\u5be9\u6838 ---------- *\/\r\nasync function toggleHideQ(id, hide){\r\n  try{ await db.collection(\"questions\").doc(id).update({hidden:hide}); toast(hide?\"\u5df2\u96b1\u85cf\":\"\u5df2\u9084\u539f\",\"ok\"); }\r\n  catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\/\/ \u5be6\u969b\u522a\u9664 (\u554f\u984c + \u5176\u6240\u6709\u56de\u61c9)\r\nasync function doDeleteQuestion(id){\r\n  try{\r\n    const rs = await db.collection(\"replies\").where(\"questionId\",\"==\",id).get();\r\n    const batch = db.batch();\r\n    rs.forEach(d=>batch.delete(d.ref));\r\n    batch.delete(db.collection(\"questions\").doc(id));\r\n    await batch.commit();\r\n    toast(\"\u5df2\u522a\u9664\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\/\/ \u5f8c\u53f0\u76f4\u63a5\u522a (\u5df2\u5728\u5f8c\u53f0\uff0c\u514d\u518d\u8f38\u5bc6\u78bc)\r\nfunction deleteQ(id){\r\n  confirmDialog(\"\u522a\u9664\u554f\u984c\",\"\u78ba\u5b9a\u522a\u9664\u9019\u5247\u554f\u984c\u55ce\uff1f\u76f8\u95dc\u56de\u61c9\u4e5f\u6703\u4e00\u4f75\u522a\u9664\u3002\",()=>doDeleteQuestion(id));\r\n}\r\n\r\n\/* ---------- \u524d\u53f0\u522a\u9664\uff1a\u9700\u8f38\u5165\u5f8c\u53f0\u5bc6\u78bc ---------- *\/\r\nlet pendingDeleteQid = null;\r\nfunction requestDeleteQuestion(id){\r\n  if(!requireDb()) return;\r\n  \/\/ \u5f8c\u53f0\u6a21\u5f0f\u4e0b\u514d\u518d\u8f38\u5bc6\u78bc\uff0c\u76f4\u63a5\u8d70\u78ba\u8a8d\u6d41\u7a0b\r\n  if(isAdmin){ deleteQ(id); return; }\r\n  pendingDeleteQid = id;\r\n  document.getElementById(\"delPwd\").value = \"\";\r\n  openModal(\"delPwdModal\");\r\n  setTimeout(()=>document.getElementById(\"delPwd\").focus(),200);\r\n}\r\nfunction confirmDeleteQuestion(){\r\n  const pwd = document.getElementById(\"delPwd\").value;\r\n  \/\/ \u8207\u5f8c\u53f0\u76f8\u540c\u7684\u9a57\u8b49\u908f\u8f2f (tpet \u63a5\u53e3\u512a\u5148\uff0c\u5426\u5247\u524d\u7aef\u5bc6\u78bc\u6bd4\u5c0d)\r\n  const tpet = tpetAuthenticate(pwd);\r\n  const pass = (tpet===true) || (tpet===null && pwd===ADMIN_PASSWORD);\r\n  if(!pass){ toast(\"\u5bc6\u78bc\u932f\u8aa4\uff0c\u7121\u6cd5\u522a\u9664\",\"err\"); return; }\r\n  closeModal(\"delPwdModal\");\r\n  if(pendingDeleteQid){ doDeleteQuestion(pendingDeleteQid); pendingDeleteQid = null; }\r\n}\r\n\r\n\/* ---------- \u63d0\u554f\u958b\u95dc \/ \u95dc\u9375\u5b57 ---------- *\/\r\nfunction applySettings(){\r\n  \/\/ \u524d\u53f0\u63d0\u554f\u5340\r\n  const closed = !settings.askOpen;\r\n  document.getElementById(\"askClosedNotice\").style.display = closed?\"block\":\"none\";\r\n  document.getElementById(\"askFormWrap\").style.display = closed?\"none\":\"block\";\r\n  \/\/ \u5f8c\u53f0\u5fbd\u7ae0\r\n  const badge = document.getElementById(\"askStatusBadge\");\r\n  if(badge){\r\n    badge.className = \"badge-status \" + (settings.askOpen?\"on\":\"off\");\r\n    badge.innerHTML = settings.askOpen\r\n      ? '<span class=\"material-icons\">check_circle<\/span>\u958b\u653e\u4e2d'\r\n      : '<span class=\"material-icons\">block<\/span>\u5df2\u95dc\u9589';\r\n  }\r\n  const kw = document.getElementById(\"kwCurrent\");\r\n  if(kw) kw.textContent = settings.keywords && settings.keywords.length\r\n    ? \"\u76ee\u524d\u8a5e\u5eab\uff1a\"+settings.keywords.join(\"\u3001\") : \"\u76ee\u524d\u672a\u8a2d\u5b9a\u4efb\u4f55\u95dc\u9375\u5b57\u3002\";\r\n\r\n  \/\/ \u53f3\u4e0a\u300c\u8aaa\u660e \/ \u8a2d\u5b9a\u300d\u6309\u9215\u986f\u793a\u63a7\u5236 (\u5957\u7528\u5230\u6240\u6709\u88dd\u7f6e)\r\n  const fabHelp = document.getElementById(\"fabHelp\");\r\n  const fabSettings = document.getElementById(\"fabSettings\");\r\n  if(fabHelp) fabHelp.style.display = settings.hideHelp ? \"none\" : \"\";\r\n  if(fabSettings) fabSettings.style.display = settings.hideSettings ? \"none\" : \"\";\r\n  \/\/ \u5f8c\u53f0\u5fbd\u7ae0\r\n  setVisBadge(\"helpStatusBadge\", !settings.hideHelp);\r\n  setVisBadge(\"settingsStatusBadge\", !settings.hideSettings);\r\n}\r\n\/\/ \u986f\u793a\/\u96b1\u85cf\u5fbd\u7ae0\u5171\u7528\u6a23\u5f0f\r\nfunction setVisBadge(id, visible){\r\n  const b = document.getElementById(id);\r\n  if(!b) return;\r\n  b.className = \"badge-status \" + (visible?\"on\":\"off\");\r\n  b.innerHTML = visible\r\n    ? '<span class=\"material-icons\">visibility<\/span>\u986f\u793a\u4e2d'\r\n    : '<span class=\"material-icons\">visibility_off<\/span>\u5df2\u96b1\u85cf';\r\n}\r\nasync function toggleAskOpen(){\r\n  if(!requireDb()) return;\r\n  try{\r\n    await db.collection(\"config\").doc(\"settings\").set(\r\n      {askOpen: !settings.askOpen}, {merge:true});\r\n    toast(\"\u5df2\u5207\u63db\u63d0\u554f\u5165\u53e3\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\nasync function toggleHelpBtn(){\r\n  if(!requireDb()) return;\r\n  try{\r\n    await db.collection(\"config\").doc(\"settings\").set({hideHelp: !settings.hideHelp}, {merge:true});\r\n    toast(settings.hideHelp ? \"\u5df2\u986f\u793a\u300e\u8aaa\u660e\u300f\u6309\u9215\" : \"\u5df2\u96b1\u85cf\u300e\u8aaa\u660e\u300f\u6309\u9215\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\nasync function toggleSettingsBtn(){\r\n  if(!requireDb()) return;\r\n  try{\r\n    await db.collection(\"config\").doc(\"settings\").set({hideSettings: !settings.hideSettings}, {merge:true});\r\n    toast(settings.hideSettings ? \"\u5df2\u986f\u793a\u300e\u8a2d\u5b9a\u300f\u6309\u9215\" : \"\u5df2\u96b1\u85cf\u300e\u8a2d\u5b9a\u300f\u6309\u9215\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\nasync function saveKeywords(){\r\n  if(!requireDb()) return;\r\n  const arr = document.getElementById(\"kwInput\").value.split(\/[,\uff0c]\/).map(s=>s.trim()).filter(Boolean);\r\n  try{\r\n    await db.collection(\"config\").doc(\"settings\").set({keywords:arr},{merge:true});\r\n    toast(\"\u5df2\u5132\u5b58\u9055\u898f\u8a5e\u5eab\",\"ok\");\r\n  }catch(e){ toast(\"\u5931\u6557\uff1a\"+e.message,\"err\"); }\r\n}\r\n\r\n\/* ---------- \u532f\u51fa \u554f\u984c-\u56de\u61c9 \u5c0d\u7167\u8868 CSV ---------- *\/\r\n\/* =====================================================================\r\n   \u55ae\u984c\u4e0b\u8f09 PDF \u2014 \u5c07\u554f\u984c\uff0b\u6240\u6709\u56de\u61c9\u7d44\u6210\u7cbe\u7f8e\u6392\u7248\uff0c\u900f\u904e\u700f\u89bd\u5668\u5217\u5370\u53e6\u5b58 PDF\r\n   ===================================================================== *\/\r\nfunction pdfReplyTree(qid){\r\n  const all = (cacheReplies[qid]||[]);\r\n  const byParent = {};\r\n  all.forEach(r=>{ const p=r.parentId||\"\"; (byParent[p]=byParent[p]||[]).push(r); });\r\n  Object.values(byParent).forEach(a=>a.sort((x,y)=>tsNum(x.createdAt)-tsNum(y.createdAt)));\r\n  function node(r, depth){\r\n    const children = (byParent[r.id]||[]).map(c=>node(c,depth+1)).join(\"\");\r\n    const badge = r.isSpeaker ? '<span class=\"pf-badge\">\u767c\u8868\u8005\u672c\u4eba<\/span>' : '';\r\n    return `<div class=\"pf-reply\" style=\"margin-left:${depth*22}px\">\r\n        <div class=\"pf-rname\">${esc(r.name)}${badge}<span class=\"pf-rtime\">${fmtTime(r.createdAt)}<\/span><\/div>\r\n        <div class=\"pf-rbody\">${esc(r.content)}<\/div>\r\n      <\/div>${children}`;\r\n  }\r\n  const tops = (byParent[\"\"]||[]);\r\n  if(!tops.length) return '<p class=\"pf-empty\">\uff08\u76ee\u524d\u5c1a\u7121\u56de\u61c9\uff09<\/p>';\r\n  return tops.map(r=>node(r,0)).join(\"\");\r\n}\r\nfunction downloadQuestionPDF(qid){\r\n  const q = cacheQuestions.find(x=>x.id===qid);\r\n  if(!q){ toast(\"\u627e\u4e0d\u5230\u8a72\u63d0\u554f\",\"err\"); return; }\r\n  const asker = q.anon||!q.askerName ? \"\u533f\u540d\" : esc(q.askerName);\r\n  const replyCount = (cacheReplies[qid]||[]).length;\r\n  const linkHtml = q.link ? `<div class=\"pf-link\">\ud83d\udd17 \u9644\u52a0\u7db2\u5740\uff1a<span>${esc(q.link)}<\/span><\/div>` : '';\r\n  const targetLine = q.targetName\r\n    ? `<div class=\"pf-target\">\u5411\u8ab0\u63d0\u554f\uff1a${esc(q.targetName)}<\/div>` : '';\r\n  const today = new Date().toLocaleString(\"zh-TW\",{hour12:false});\r\n\r\n  const render = document.getElementById(\"pdfRender\");\r\n  render.innerHTML = `\r\n    <style>\r\n      #pdfRender{font-family:\"Noto Sans TC\",sans-serif;color:#234567;background:#fff}\r\n      .pf-wrap{padding:24px;width:720px;box-sizing:border-box;background:#fff}\r\n      .pf-header{background:linear-gradient(120deg,#5B9BD5,#7FC6E8,#BFE3F5);color:#fff;\r\n        border-radius:20px;padding:22px 26px;margin-bottom:20px;box-shadow:0 6px 18px rgba(91,155,213,.3)}\r\n      .pf-header .pf-event{font-size:13px;opacity:.97;letter-spacing:1px}\r\n      .pf-header .pf-title{font-size:22px;font-weight:900;margin-top:4px;text-shadow:0 2px 4px rgba(40,90,140,.3)}\r\n      .pf-meta{display:flex;gap:18px;flex-wrap:wrap;font-size:12px;margin-top:10px;opacity:.98}\r\n      .pf-qcard{background:#F4FAFE;border:2px solid #DCEDF9;border-left:7px solid #5B9BD5;\r\n        border-radius:18px;padding:20px 22px;margin-bottom:22px;box-shadow:0 4px 14px rgba(91,155,213,.12)}\r\n      .pf-qlabel{display:inline-block;background:linear-gradient(120deg,#5B9BD5,#3D7FC0);color:#fff;\r\n        font-size:12px;font-weight:700;padding:4px 14px;border-radius:16px;margin-bottom:10px}\r\n      .pf-target{font-size:13px;color:#3D7FC0;font-weight:700;margin-bottom:8px}\r\n      .pf-qbody{font-size:17px;line-height:1.7;white-space:pre-wrap;font-weight:500}\r\n      .pf-link{margin-top:12px;font-size:13px;color:#3D7FC0;font-weight:700;word-break:break-all}\r\n      .pf-link span{font-weight:400;color:#4178a8}\r\n      .pf-section{font-size:15px;font-weight:900;color:#3D7FC0;margin:6px 0 12px;\r\n        padding-bottom:8px;border-bottom:2px dashed #DCEDF9;display:flex;align-items:center;gap:8px}\r\n      .pf-section .pf-pill{background:#6FCBD8;color:#0c4650;font-size:12px;padding:2px 12px;border-radius:14px}\r\n      .pf-reply{background:#fff;border:1.5px solid #DCEDF9;border-left:4px solid #7FC6E8;\r\n        border-radius:14px;padding:12px 16px;margin-bottom:10px}\r\n      .pf-rname{font-weight:800;font-size:14px;color:#234567}\r\n      .pf-badge{background:linear-gradient(120deg,#6FCBD8,#9BD3F0);color:#0c4650;font-size:11px;\r\n        padding:2px 9px;border-radius:11px;margin-left:8px;font-weight:700}\r\n      .pf-rtime{font-size:11px;color:#8AA4BE;font-weight:400;margin-left:8px}\r\n      .pf-rbody{font-size:14px;line-height:1.65;margin-top:5px;white-space:pre-wrap}\r\n      .pf-empty{color:#8AA4BE;font-size:14px;text-align:center;padding:14px}\r\n      .pf-foot{margin-top:24px;text-align:center;font-size:11px;color:#8AA4BE;\r\n        border-top:2px dashed #DCEDF9;padding-top:14px}\r\n    <\/style>\r\n    <div class=\"pf-wrap\" id=\"pfWrap\">\r\n      <div class=\"pf-header\">\r\n        <div class=\"pf-event\">\ud83c\udfa4 AI \u6559\u80b2\u5e74\u6703 \u00b7 \u73fe\u5834\u63d0\u554f\u5e73\u53f0<\/div>\r\n        <div class=\"pf-title\">\u63d0\u554f\u8207\u56de\u61c9\u7d00\u9304<\/div>\r\n        <div class=\"pf-meta\">\r\n          <span>\u63d0\u554f\u4eba\uff1a${asker}<\/span>\r\n          <span>\u2764 \u6309\u8b9a\uff1a${q.likes||0}<\/span>\r\n          <span>\ud83d\udcac \u56de\u61c9\u6578\uff1a${replyCount}<\/span>\r\n          <span>\u63d0\u554f\u6642\u9593\uff1a${fmtTime(q.createdAt)}<\/span>\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"pf-qcard\">\r\n        <span class=\"pf-qlabel\">Q\u3000\u554f\u984c<\/span>\r\n        ${targetLine}\r\n        <div class=\"pf-qbody\">${esc(q.content)}<\/div>\r\n        ${linkHtml}\r\n      <\/div>\r\n      <div class=\"pf-section\"><span>\ud83d\udcac \u56de\u61c9\u7d00\u9304<\/span><span class=\"pf-pill\">${replyCount} \u5247<\/span><\/div>\r\n      ${pdfReplyTree(qid)}\r\n      <div class=\"pf-foot\">\u672c\u6587\u4ef6\u7531\u300cAI \u6559\u80b2\u5e74\u6703\u73fe\u5834\u63d0\u554f\u5e73\u53f0\u300d\u65bc ${today} \u7522\u751f<\/div>\r\n    <\/div>`;\r\n\r\n  \/\/ \u78ba\u8a8d CDN \u5df2\u8f09\u5165\r\n  if(typeof html2canvas===\"undefined\" || !window.jspdf){\r\n    toast(\"PDF \u5143\u4ef6\u5c1a\u672a\u8f09\u5165\u5b8c\u6210\uff0c\u8acb\u7a0d\u5019\u518d\u8a66\",\"err\");\r\n    render.innerHTML=\"\"; return;\r\n  }\r\n  toast(\"\u6b63\u5728\u7522\u751f PDF\uff0c\u8acb\u7a0d\u5019\u2026\",\"info\");\r\n\r\n  \/\/ \u8b93\u6e32\u67d3\u5bb9\u5668\u66ab\u6642\u53ef\u898b (\u79fb\u5230\u756b\u9762\u5916) \u4f9b html2canvas \u64f7\u53d6\r\n  render.style.display=\"block\";\r\n  render.style.position=\"fixed\";\r\n  render.style.left=\"-9999px\";\r\n  render.style.top=\"0\";\r\n  render.style.background=\"#fff\";\r\n\r\n  const target = document.getElementById(\"pfWrap\");\r\n  setTimeout(()=>{\r\n    html2canvas(target, {scale:2, backgroundColor:\"#ffffff\", useCORS:true})\r\n      .then(canvas=>{\r\n        const { jsPDF } = window.jspdf;\r\n        const pdf = new jsPDF(\"p\",\"mm\",\"a4\");\r\n        const pageW = pdf.internal.pageSize.getWidth();\r\n        const pageH = pdf.internal.pageSize.getHeight();\r\n        const imgW = pageW;\r\n        const imgH = canvas.height * imgW \/ canvas.width;\r\n        const imgData = canvas.toDataURL(\"image\/jpeg\", 0.92);\r\n        \/\/ \u5167\u5bb9\u904e\u9577\u6642\u81ea\u52d5\u5206\u9801\r\n        let heightLeft = imgH, position = 0;\r\n        pdf.addImage(imgData, \"JPEG\", 0, position, imgW, imgH);\r\n        heightLeft -= pageH;\r\n        while(heightLeft > 0){\r\n          position -= pageH;\r\n          pdf.addPage();\r\n          pdf.addImage(imgData, \"JPEG\", 0, position, imgW, imgH);\r\n          heightLeft -= pageH;\r\n        }\r\n        const safeAsker = (q.anon||!q.askerName?\"\u533f\u540d\":q.askerName).replace(\/[\\\\\/:*?\"<>|]\/g,\"\");\r\n        pdf.save(`\u63d0\u554f\u7d00\u9304_${safeAsker}_${new Date().toISOString().slice(0,10)}.pdf`);\r\n        toast(\"PDF \u5df2\u4e0b\u8f09\",\"ok\");\r\n      })\r\n      .catch(err=>{ console.error(err); toast(\"PDF \u7522\u751f\u5931\u6557\uff1a\"+err.message,\"err\"); })\r\n      .finally(()=>{\r\n        render.style.display=\"none\";\r\n        render.style.position=\"\";\r\n        render.style.left=\"\";\r\n        render.innerHTML=\"\";\r\n      });\r\n  }, 300);\r\n}\r\n\r\nfunction exportData(){\r\n  const rows = [[\"\u6642\u9593\",\"\u63d0\u554f\u4eba\",\"\u5411\u8ab0\u63d0\u554f\",\"\u554f\u984c\u5167\u5bb9\",\"\u6309\u8b9a\",\"\u72c0\u614b\",\"\u56de\u61c9\u4eba\",\"\u56de\u61c9\u5167\u5bb9\",\"\u767c\u8868\u8005\u672c\u4eba\"]];\r\n  cacheQuestions.forEach(q=>{\r\n    const asker = q.anon||!q.askerName ? \"\u533f\u540d\" : q.askerName;\r\n    const status = q.hidden ? \"\u5df2\u96b1\u85cf\":\"\u986f\u793a\u4e2d\";\r\n    const rs = cacheReplies[q.id]||[];\r\n    if(!rs.length){\r\n      rows.push([fmtTime(q.createdAt),asker,q.targetName||\"\",q.content,q.likes||0,status,\"\",\"\",\"\"]);\r\n    }else{\r\n      rs.forEach(r=>rows.push([fmtTime(q.createdAt),asker,q.targetName||\"\",q.content,q.likes||0,status,r.name,r.content,r.isSpeaker?\"\u662f\":\"\"]));\r\n    }\r\n  });\r\n  const csv = \"\ufeff\" + rows.map(r=>r.map(c=>`\"${String(c).replace(\/\"\/g,'\"\"')}\"`).join(\",\")).join(\"\\r\\n\");\r\n  const blob = new Blob([csv],{type:\"text\/csv;charset=utf-8\"});\r\n  const a = document.createElement(\"a\");\r\n  a.href = URL.createObjectURL(blob);\r\n  a.download = `\u5e74\u6703\u63d0\u554f\u5c0d\u7167\u8868_${new Date().toISOString().slice(0,10)}.csv`;\r\n  a.click();\r\n  toast(\"\u5df2\u532f\u51fa CSV\",\"ok\");\r\n}\r\n\r\n\/* =====================================================================\r\n   \u8a2d\u5b9a Modal \u2014 \u89e3\u6790 \/ \u5132\u5b58 \/ \u6e05\u9664 \/ \u5206\u4eab\r\n   ===================================================================== *\/\r\n\/\/ \u5f37\u5065\u89e3\u6790\uff1a\u652f\u63f4\u6574\u6bb5\u8cbc\u4e0a\u3001\u542b\/\u4e0d\u542b\u5927\u62ec\u865f\u3001\u542b const \u5ba3\u544a\r\nfunction parseConfigText(text){\r\n  const keys = [\"apiKey\",\"authDomain\",\"projectId\",\"storageBucket\",\"messagingSenderId\",\"appId\"];\r\n  const cfg = {};\r\n  keys.forEach(k=>{\r\n    \/\/ \u6bd4\u5c0d  key: \"value\"  \u6216  key: 'value'\r\n    const re = new RegExp(k + \"\\\\s*:\\\\s*[\\\"']([^\\\"']*)[\\\"']\");\r\n    const m = text.match(re);\r\n    if(m) cfg[k] = m[1];\r\n  });\r\n  return cfg;\r\n}\r\nfunction saveConfigFromInput(){\r\n  const text = document.getElementById(\"configInput\").value.trim();\r\n  if(!text){ toast(\"\u8acb\u8cbc\u4e0a\u8a2d\u5b9a\u5167\u5bb9\",\"err\"); return; }\r\n  const cfg = parseConfigText(text);\r\n  \/\/ \u81f3\u5c11\u9700\u6709 apiKey \u8207 projectId \u624d\u7b97\u6709\u6548\r\n  if(!cfg.apiKey || !cfg.projectId){\r\n    toast(\"\u89e3\u6790\u5931\u6557\uff0c\u8acb\u78ba\u8a8d\u5305\u542b apiKey \u8207 projectId\",\"err\"); return;\r\n  }\r\n  \/\/ \u88dc\u9f4a\u7f3a\u6f0f\u6b04\u4f4d\u907f\u514d\u932f\u8aa4\r\n  cfg.authDomain = cfg.authDomain || `${cfg.projectId}.firebaseapp.com`;\r\n  cfg.storageBucket = cfg.storageBucket || `${cfg.projectId}.appspot.com`;\r\n  cfg.messagingSenderId = cfg.messagingSenderId || \"\";\r\n  cfg.appId = cfg.appId || \"\";\r\n  localStorage.setItem(LS_KEY, JSON.stringify(cfg));\r\n  toast(\"\u8a2d\u5b9a\u5df2\u5132\u5b58\uff0c\u6b63\u5728\u91cd\u65b0\u9023\u7dda\u2026\",\"ok\");\r\n  setTimeout(()=>location.reload(), 800);\r\n}\r\nfunction resetConfig(){\r\n  confirmDialog(\"\u6e05\u9664\u672c\u6a5f\u8a2d\u5b9a\",\"\u78ba\u5b9a\u8981\u79fb\u9664\u6b64\u700f\u89bd\u5668\u5132\u5b58\u7684 Firebase \u8a2d\u5b9a\u55ce\uff1f\u9801\u9762\u5c07\u91cd\u65b0\u6574\u7406\u3002\",()=>{\r\n    localStorage.removeItem(LS_KEY);\r\n    location.reload();\r\n  });\r\n}\r\n\r\n\/* ---------- \u5206\u4eab\u9023\u7d50 + \u77ed\u7db2\u5740(is.gd) + QR Code(qrserver) ---------- *\/\r\nfunction getActiveConfig(){\r\n  const cfg = resolveConfigForShare();\r\n  return cfg;\r\n}\r\nfunction resolveConfigForShare(){\r\n  if(firebaseConfig.apiKey && firebaseConfig.apiKey!==\"demo\") return firebaseConfig;\r\n  const saved = localStorage.getItem(LS_KEY);\r\n  if(saved){ try{ return JSON.parse(saved); }catch(e){} }\r\n  return null;\r\n}\r\nlet lastShareUrl = \"\";\r\nasync function generateShareLink(){\r\n  const cfg = getActiveConfig();\r\n  if(!cfg){ toast(\"\u5c1a\u7121\u6709\u6548\u8a2d\u5b9a\u53ef\u5206\u4eab\uff0c\u8acb\u5148\u9023\u7dda\",\"err\"); return; }\r\n  const b64 = btoa(JSON.stringify(cfg));\r\n  const fullUrl = `${location.origin}${location.pathname}?config=${b64}`;\r\n  lastShareUrl = fullUrl;\r\n  document.getElementById(\"shareResult\").style.display = \"block\";\r\n  document.getElementById(\"shortUrlText\").textContent = \"\u7e2e\u77ed\u7db2\u5740\u4e2d\u2026\";\r\n  document.getElementById(\"qrImg\").src = \"\";\r\n\r\n  \/\/ \u77ed\u7db2\u5740\uff1ais.gd (\u514d\u91d1\u9470)\r\n  let shortUrl = fullUrl;\r\n  try{\r\n    const r = await fetch(`https:\/\/is.gd\/create.php?format=simple&url=${encodeURIComponent(fullUrl)}`);\r\n    if(r.ok){ const t = (await r.text()).trim(); if(t.startsWith(\"http\")) shortUrl = t; }\r\n  }catch(e){ \/* \u5931\u6557\u5247\u7528\u9577\u7db2\u5740 *\/ }\r\n  lastShareUrl = shortUrl;\r\n  document.getElementById(\"shortUrlText\").textContent = shortUrl;\r\n\r\n  \/\/ QR Code\uff1aqrserver\r\n  const qr = `https:\/\/api.qrserver.com\/v1\/create-qr-code\/?size=220x220&data=${encodeURIComponent(shortUrl)}`;\r\n  document.getElementById(\"qrImg\").src = qr;\r\n  toast(\"\u5df2\u7522\u751f\u5206\u4eab\u9023\u7d50\u8207 QR Code\",\"ok\");\r\n}\r\nfunction copyShareUrl(){\r\n  if(!lastShareUrl) return;\r\n  navigator.clipboard.writeText(lastShareUrl)\r\n    .then(()=>toast(\"\u5df2\u8907\u88fd\u9023\u7d50\",\"ok\"))\r\n    .catch(()=>toast(\"\u8907\u88fd\u5931\u6557\uff0c\u8acb\u624b\u52d5\u9078\u53d6\",\"err\"));\r\n}\r\n\r\n\/* =====================================================================\r\n   \u901a\u7528 Modal \/ Toast \/ \u78ba\u8a8d\u6846\r\n   ===================================================================== *\/\r\nfunction openModal(id){ document.getElementById(id).classList.add(\"show\"); }\r\nfunction closeModal(id){ document.getElementById(id).classList.remove(\"show\"); }\r\n\/\/ \u9ede\u906e\u7f69\u95dc\u9589\r\ndocument.querySelectorAll(\".modal-mask\").forEach(m=>{\r\n  m.addEventListener(\"click\", e=>{ if(e.target===m) m.classList.remove(\"show\"); });\r\n});\r\nfunction toast(msg, type){\r\n  const wrap = document.getElementById(\"toast-wrap\");\r\n  const el = document.createElement(\"div\");\r\n  el.className = \"toast \" + (type||\"info\");\r\n  const icon = {ok:\"check_circle\",err:\"error\",info:\"info\"}[type||\"info\"];\r\n  el.innerHTML = `<span class=\"material-icons\">${icon}<\/span>${esc(msg)}`;\r\n  wrap.appendChild(el);\r\n  setTimeout(()=>{ el.style.opacity=\"0\"; el.style.transition=\"opacity .3s\"; setTimeout(()=>el.remove(),300); }, 3200);\r\n}\r\nfunction confirmDialog(title, msg, onOk){\r\n  document.getElementById(\"confirmTitle\").innerHTML = `<span class=\"material-icons\">help<\/span> ${esc(title)}`;\r\n  document.getElementById(\"confirmMsg\").textContent = msg;\r\n  const btn = document.getElementById(\"confirmOkBtn\");\r\n  const fresh = btn.cloneNode(true);   \/\/ \u79fb\u9664\u820a\u4e8b\u4ef6\r\n  btn.parentNode.replaceChild(fresh, btn);\r\n  fresh.addEventListener(\"click\", ()=>{ closeModal(\"confirmModal\"); onOk(); });\r\n  openModal(\"confirmModal\");\r\n}\r\n\r\n\/* =====================================================================\r\n   \u555f\u52d5\r\n   ===================================================================== *\/\r\nwindow.addEventListener(\"DOMContentLoaded\", ()=>{\r\n  setSort(\"time\");\r\n  initFirebase();\r\n});\r\n<\/script>\r\n<\/body>\r\n<\/html>\r\n\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2332","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/pages\/2332","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2332"}],"version-history":[{"count":3,"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/pages\/2332\/revisions"}],"predecessor-version":[{"id":2336,"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=\/wp\/v2\/pages\/2332\/revisions\/2336"}],"wp:attachment":[{"href":"https:\/\/aifed.now\/conference\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}