/* ============================================================
   cm_app.jsx — ChefsMate 主控台 主程式
   App shell + 分類導航 + 快速捕捉 + 問 AI + 抽屜 + Roadmap(甘特)
   ============================================================ */

const CM_STORE = "cm-console-v4";
const CM_ROAD = "cm-roadmap-v4";

const SYSMAP_LAYERS = [
  { id: "L0", label: "設計哲學", done: 1, total: 1 },
  { id: "L2", label: "架構文件", done: 9, total: 12 },
  { id: "L3", label: "子模組", done: 18, total: 26 },
  { id: "L4", label: "電路圖", done: 12, total: 20 },
  { id: "L5", label: "程式碼層", done: 15, total: 24 },
  { id: "L6", label: "漣漪追蹤", done: 4, total: 8 },
  { id: "L7", label: "欄位反向 index", done: 3, total: 6 },
];
const PLANS = [
  { id: "p1", title: "AI-native POS — Phase 1 Roadmap", status: "進行中", color: "var(--accent)", body: "把 POS 重構成 AI 可直接讀寫狀態的架構，讓夜審與自動修復成為日常。Phase 1 聚焦點餐結帳與訂位兩條主線。" },
  { id: "p2", title: "Boss ↔ AI 同步計劃", status: "進行中", color: "var(--c-purple)", body: "定義老闆與 AI 的非同步協作協定：收件匣決策、夜間自動跑、白話回報。這個主控台就是它的落地。" },
  { id: "p3", title: "集團化 / 多店策略", status: "規劃中", color: "var(--c-bluegrey)", body: "從單店長成連鎖所需的資料隔離、總部儀表板與跨店報表。等架構決策定案後展開。" },
];

// ── 內容(seed) ↔ 使用者狀態 合併 ──────────────────────────────
//  核心修正:內容(dogfood/bugs/audits/ref...)永遠取最新 window.CM seed;
//  使用者的回覆/打勾/留言/新增 用 id 疊上去保留。→ 更新內容不再清掉答案,
//  也不用再 bump 版本。並掃舊版本 key 把之前(登入壞掉時)只存本機的回覆救回。
function freshSeed() { return JSON.parse(JSON.stringify(window.CM)); }
const USER_FIELDS = { inbox: ["chosen", "chosenLabel"], todos: ["status"], dogfood: ["passed"], bugs: ["status"], audits: ["status"], specs: ["status", "title", "body"], wishes: ["status", "aiNote"] };
const APPENDABLE = { todos: 1, wishes: 1 };
function ckKey(x) { return String((x && (x.t || x.text || x.label)) || ""); }
function mergeChecklist(seedList, savedList) {
  if (!Array.isArray(seedList)) return seedList;
  const doneByKey = {}; (savedList || []).forEach((x) => { doneByKey[ckKey(x)] = !!x.done; });
  return seedList.map((x) => (ckKey(x) in doneByKey ? { ...x, done: doneByKey[ckKey(x)] } : x));
}
function mergeArr(key, seedArr, savedArr) {
  if (!Array.isArray(seedArr)) return seedArr;
  const byId = {}; (savedArr || []).forEach((x) => { if (x && x.id) byId[x.id] = x; });
  const fields = USER_FIELDS[key] || [];
  const merged = seedArr.map((s) => {
    const u = byId[s.id]; if (!u) return s;
    const out = { ...s };
    fields.forEach((f) => { if (u[f] !== undefined) out[f] = u[f]; });
    if (s.checklist) out.checklist = mergeChecklist(s.checklist, u.checklist);
    if (s.steps) out.steps = mergeChecklist(s.steps, u.steps);
    if (Array.isArray(u.comments) && u.comments.length) out.comments = u.comments;
    return out;
  });
  if (APPENDABLE[key]) {
    const seedIds = new Set(seedArr.map((x) => x.id));
    (savedArr || []).forEach((u) => { if (u && u.id && !seedIds.has(u.id)) merged.unshift(u); });
  }
  return merged;
}
function mergeState(seed, saved) {
  if (!saved || typeof saved !== "object") return seed;
  const out = { ...seed };
  ["inbox", "todos", "dogfood", "bugs", "audits", "specs", "wishes"].forEach((k) => { out[k] = mergeArr(k, seed[k], saved[k]); });
  return out;
}
function mergeRoad(seedRoad, savedRoad) {
  if (!Array.isArray(savedRoad) || !Array.isArray(seedRoad)) return seedRoad;
  const byId = {}; savedRoad.forEach((t) => { if (t && t.id) byId[t.id] = t; });
  return seedRoad.map((s) => {
    const u = byId[s.id]; if (!u) return s;
    const out = { ...s };
    if (Array.isArray(u.comments) && u.comments.length) out.comments = u.comments;
    if (Array.isArray(u.updates) && u.updates.length) out.updates = u.updates;
    if (Array.isArray(s.stages) && Array.isArray(u.stages)) {
      const ust = {}; u.stages.forEach((x) => { ust[x.key] = x; });
      out.stages = s.stages.map((st) => {
        const us = ust[st.key]; if (!us) return st;
        const o = { ...st };
        if (us.status) o.status = us.status;
        if (Array.isArray(st.subtasks) && Array.isArray(us.subtasks)) {
          const ud = {}; us.subtasks.forEach((x) => { ud[x.id != null ? x.id : x.text] = !!x.done; });
          o.subtasks = st.subtasks.map((x) => { const k = x.id != null ? x.id : x.text; return k in ud ? { ...x, done: ud[k] } : x; });
        }
        return o;
      });
    }
    if (Array.isArray(u.blockers) && Array.isArray(s.blockers)) {
      const ub = {}; u.blockers.forEach((b) => { ub[b.id] = b; });
      out.blockers = s.blockers.map((b) => (ub[b.id] ? { ...b, resolved: ub[b.id].resolved } : b));
    }
    return out;
  });
}
// 真實使用者狀態分數(seed todos 也用 td- 前綴，所以只認 inbox 回覆 + 數字 id 的新增項)
function blobUserScore(b) {
  if (!b) return 0;
  let n = 0;
  if (Array.isArray(b.inbox)) n += b.inbox.filter((x) => x && x.chosen != null).length * 10;
  if (Array.isArray(b.todos)) n += b.todos.filter((x) => /^td-\d{6,}$/.test((x && x.id) || "")).length;
  if (Array.isArray(b.wishes)) n += b.wishes.filter((x) => /^w-\d{6,}$/.test((x && x.id) || "")).length;
  return n;
}
// 取最完整的使用者狀態:現用 key 與舊版本 key 都評分，挑最高(舊 key 有更多回覆就救回)
function readSavedBlob(curKey, oldKeys) {
  let current = null, best = null, bestScore = -1;
  for (const k of [curKey, ...oldKeys]) {
    try { const r = localStorage.getItem(k); if (!r) continue; const b = JSON.parse(r); if (k === curKey) current = b; const s = blobUserScore(b); if (s > bestScore) { best = b; bestScore = s; } } catch (e) {}
  }
  if (current && blobUserScore(current) >= bestScore) return current;
  return best || current;
}
function loadCM() { return mergeState(freshSeed(), readSavedBlob(CM_STORE, ["cm-console-v3", "cm-console-v2", "cm-console-v1"])); }
function loadRoad() {
  let saved = null;
  try { const r = localStorage.getItem(CM_ROAD); if (r) saved = JSON.parse(r); } catch (e) {}
  if (!saved) { for (const k of ["cm-roadmap-v3", "cm-roadmap-v2", "cm-roadmap-v1"]) { try { const r = localStorage.getItem(k); if (r) { saved = JSON.parse(r); break; } } catch (e) {} } }
  return mergeRoad(JSON.parse(JSON.stringify(window.CM.roadmap)), saved);
}
function nowStr() { return new Date().toISOString().slice(5, 10).replace("-", "-") + " " + new Date().toTimeString().slice(0, 5); }

const TYPE_KEY = { inbox: "inbox", todo: "todos", dogfood: "dogfood", spec: "specs", bug: "bugs", audit: "audits" };

function CMApp() {
  const [data, setData] = useState(loadCM);
  const [road, setRoad] = useState(loadRoad);
  const [section, setSection] = useState("overview");
  const [open, setOpen] = useState(null);       // {item, type} for console items
  const [openRoad, setOpenRoad] = useState(null); // roadmap task id
  const [navOpen, setNavOpen] = useState(false);
  const [toasts, setToasts] = useState([]);
  const [justId, setJustId] = useState(null);
  const [askOpen, setAskOpen] = useState(false);
  const flashT = useRef(null);

  // ── Supabase 同步：localStorage 優先 + write-through ──
  const [sync, setSync] = useState({ authed: false, loaded: false });
  const dataRef = useRef(data); dataRef.current = data;
  const roadRef = useRef(road); roadRef.current = road;
  const cloudReady = useRef(false); // 收到雲端資料後才開始 write-through，避免初始覆寫

  useEffect(() => { localStorage.setItem(CM_STORE, JSON.stringify(data)); if (cloudReady.current && window.consoleStore) window.consoleStore.save(data, roadRef.current); }, [data]);
  useEffect(() => { localStorage.setItem(CM_ROAD, JSON.stringify(road)); if (cloudReady.current && window.consoleStore) window.consoleStore.save(dataRef.current, road); }, [road]);
  useEffect(() => { document.documentElement.setAttribute("data-theme", "light"); }, []);

  // 初始化雲端同步：登入則 load 雲端快照（沒列就把目前狀態推上去當初始）
  useEffect(() => {
    let alive = true;
    async function pull() {
      const remote = await window.consoleStore.load();
      if (!alive) return false;
      const remoteData = remote && remote.data;
      // 本機目前已合併的狀態(loadCM 已從各 key 救回的回覆，初次 useEffect 已寫入 CM_STORE)
      const localBlob = readSavedBlob(CM_STORE, ["cm-console-v3", "cm-console-v2", "cm-console-v1"]);
      let localRoad = null; try { localRoad = JSON.parse(localStorage.getItem(CM_ROAD) || "null"); } catch (e) {}
      if (!remoteData) {
        // 雲端沒列 → 用本機(可能含回覆)，回 false 讓 bootstrap push 上去
        if (localBlob) { setData(mergeState(freshSeed(), localBlob)); }
        return false;
      }
      // ⭐ 雲端有列:比較本機 vs 雲端誰的使用者狀態多，多的贏 — 避免空白雲端蓋掉本機回覆
      if (blobUserScore(localBlob) > blobUserScore(remoteData)) {
        const merged = mergeState(freshSeed(), localBlob);
        setData(merged);
        setRoad(mergeRoad(freshSeed().roadmap, localRoad));
        try { window.consoleStore.save(merged, roadRef.current); } catch (e) {} // 把本機回覆推上雲
      } else {
        setData(mergeState(freshSeed(), remoteData));
        setRoad(mergeRoad(freshSeed().roadmap, remote.road));
      }
      return true;
    }
    (async () => {
      if (!window.consoleStore) return;
      let res = {};
      try { res = await window.consoleStore.init(); } catch (e) {}
      if (!alive) return;
      setSync({ authed: !!res.authed, loaded: true });
      if (res.authed) {
        const had = await pull();
        if (alive && !had) { try { await window.consoleStore.save(dataRef.current, roadRef.current); } catch (e) {} }
        cloudReady.current = true;
      }
      window.consoleStore.onRemoteChange(async (info) => {
        if (!info) return;
        if (info.reason === "remote") {
          if (info.data) setData(mergeState(freshSeed(), info.data));
          if (info.road) setRoad(mergeRoad(freshSeed().roadmap, info.road));
        } else if (info.reason === "auth") {
          const a = window.consoleStore.isAuthed();
          setSync({ authed: a, loaded: true });
          if (a) { const had = await pull(); if (!had) { try { await window.consoleStore.save(dataRef.current, roadRef.current); } catch (e) {} } cloudReady.current = true; }
        }
      });
    })();
    return () => { alive = false; };
  }, []);

  function pushToast(t) { const id = Date.now() + Math.random(); setToasts((p) => [...p, { id, ...t }]); setTimeout(() => setToasts((p) => p.filter((x) => x.id !== id)), 4200); }
  function flash(id) { setJustId(id); clearTimeout(flashT.current); flashT.current = setTimeout(() => setJustId(null), 2600); }
  function aiAck(text) { pushToast({ kind: "ai", title: "AI 助理", text }); }

  // generic updater for console item arrays
  function updateItem(type, id, fn) {
    const key = TYPE_KEY[type];
    setData((prev) => ({ ...prev, [key]: prev[key].map((it) => it.id === id ? fn(it) : it) }));
  }
  // keep the open drawer item in sync
  const liveOpen = open ? { ...open, item: (data[TYPE_KEY[open.type]] || []).find((x) => x.id === open.item.id) || open.item } : null;

  /* ---- console actions ---- */
  const actions = {
    onComment(id, type, text) {
      updateItem(type, id, (it) => ({ ...it, comments: [...(it.comments || []), { author: "boss", time: nowStr(), text }] }));
      setTimeout(() => updateItem(type, id, (it) => ({ ...it, comments: [...(it.comments || []), { author: "ai", time: nowStr(), text: "收到，我記下來了，會納入處理並回寫狀態。" }] })), 1300);
    },
    onToggleChecklist(id, type, i) {
      const field = type === "dogfood" ? "steps" : "checklist";
      updateItem(type, id, (it) => { const arr = it[field].map((x, idx) => idx === i ? { ...x, done: !x.done } : x); const patch = { ...it, [field]: arr }; if (type === "todo") { patch.status = arr.every((x) => x.done) ? "done" : arr.some((x) => x.done) ? "doing" : "todo"; } return patch; });
    },
    onSetStatus(id, type, v) {
      if (type === "dogfood") updateItem(type, id, (it) => ({ ...it, passed: v }));
      else updateItem(type, id, (it) => ({ ...it, status: v }));
    },
    onAnswer(id, v, label) {
      updateItem("inbox", id, (it) => ({ ...it, chosen: v, chosenLabel: label, comments: [...(it.comments || []), { author: "boss", time: nowStr(), text: `決定：${label}` }] }));
      flash(id); aiAck(`收到你的決定「${label}」，我照這個方向往下做。`);
      setTimeout(() => updateItem("inbox", id, (it) => ({ ...it, comments: [...(it.comments || []), { author: "ai", time: nowStr(), text: `了解，依「${label}」執行。我會把相關待辦更新並開始處理。` }] })), 1300);
    },
    onEdit(id, type, { title, body }) {
      updateItem(type, id, (it) => ({ ...it, title, ...(type === "spec" ? { body } : {}) }));
      pushToast({ kind: "ok", title: "已儲存", text: title });
    },
    onReview(id) { updateItem("audit", id, (it) => ({ ...it, status: "reviewed" })); pushToast({ kind: "ok", title: "已覆核", text: "夜審項目" }); },
    onToggleStatus(id) { updateItem("bug", id, (it) => ({ ...it, status: it.status === "fixed" ? "open" : "fixed" })); },
  };

  function quickCapture(text) {
    const item = { id: "td-" + Date.now(), title: text, status: "todo", author: "boss", priority: "medium", track: "high", checklist: [], comments: [] };
    setData((prev) => ({ ...prev, todos: [item, ...prev.todos] }));
    pushToast({ kind: "ok", title: "已加入待辦", text });
    aiAck("我看到你新增的待辦了，需要我先做嗎？在卡片裡跟我說一聲。");
  }
  function addWish(text) {
    const w = { id: "w-" + Date.now(), text, status: "new", created: nowStr().slice(0, 5), aiNote: "" };
    setData((prev) => ({ ...prev, wishes: [w, ...prev.wishes] }));
    pushToast({ kind: "ai", title: "AI 助理", text: "收到你的願望，我會看看怎麼排進來。" });
  }

  /* ---- roadmap actions (reuse gantt + TaskDrawer) ---- */
  function rUpdate(id, fn) { setRoad((prev) => prev.map((t) => t.id === id ? fn(t) : t)); }
  const roadActions = {
    toggleSub(taskId, stageKey, subId) {
      rUpdate(taskId, (task) => ({ ...task, stages: task.stages.map((s) => { if (s.key !== stageKey) return s; const subtasks = s.subtasks.map((sub) => sub.id === subId ? { ...sub, done: !sub.done } : sub); let status = s.status; if (s.status !== "blocked") status = subtasks.every((x) => x.done) ? "done" : subtasks.some((x) => x.done) ? "active" : "upcoming"; return { ...s, subtasks, status }; }) }));
    },
    resolve(taskId, bId) { rUpdate(taskId, (task) => ({ ...task, blockers: task.blockers.map((b) => b.id === bId ? { ...b, resolved: true } : b), stages: task.stages.map((s) => s.status === "blocked" ? { ...s, status: "active" } : s) })); },
    send(taskId, text) { rUpdate(taskId, (t) => ({ ...t, comments: [...t.comments, { author: "me", time: "2026-05-30 " + new Date().toTimeString().slice(0, 5), text }] })); setTimeout(() => rUpdate(taskId, (t) => ({ ...t, comments: [...t.comments, { author: "ai", time: "2026-05-30 " + new Date().toTimeString().slice(0, 5), text: "收到，我納入處理。" }] })), 1300); },
    reorder(taskId, stageKey, from, to) { if (from === to) return; rUpdate(taskId, (task) => ({ ...task, stages: task.stages.map((s) => { if (s.key !== stageKey) return s; const arr = [...s.subtasks]; const [m] = arr.splice(from, 1); arr.splice(to, 0, m); return { ...s, subtasks: arr }; }) })); },
    shift(taskId, stageKey, dd) { rUpdate(taskId, (task) => ({ ...task, stages: task.stages.map((s) => { if (s.key !== stageKey) return s; const ns = new Date(d(s.start).getTime() + dd * DAY).toISOString().slice(0, 10); const ne = new Date(d(s.end).getTime() + dd * DAY).toISOString().slice(0, 10); return { ...s, start: ns, end: ne }; }) })); },
  };
  function roadAdvance() {
    const target = road.find((x) => !isBlocked(x) && taskProgress(x) < 1 && x.stages.some((s) => (s.status === "active" || s.status === "upcoming") && s.subtasks.some((sub) => !sub.done)));
    if (!target) { pushToast({ kind: "ok", title: "沒有可推進的任務", text: "都完成或在等你決策" }); return; }
    const stage = target.stages.find((s) => (s.status === "active" || s.status === "upcoming") && s.subtasks.some((x) => !x.done));
    const sub = stage.subtasks.find((x) => !x.done);
    const lines = ["完成了，已推到測試環境。", "這塊收尾了，沒遇到大問題。", "搞定，順手更新了文件。"];
    const line = lines[Math.floor(Math.random() * lines.length)];
    const now = "2026-05-30 " + new Date().toTimeString().slice(0, 5);
    rUpdate(target.id, (task) => ({ ...task, stages: task.stages.map((s) => { if (s.key !== stage.key) return s; const subtasks = s.subtasks.map((x) => x.id === sub.id ? { ...x, done: true } : x); return { ...s, subtasks, status: subtasks.every((x) => x.done) ? "done" : "active" }; }), updates: [{ time: now, stage: stage.key, text: `「${sub.text}」${line}`, fresh: true }, ...task.updates] }));
    flash(target.id); pushToast({ kind: "ai", title: target.title, text: line, stage: stage.key });
  }

  function resetAll() {
    if (confirm("還原成範例資料？你新增/編輯的內容會清除。")) { setData(JSON.parse(JSON.stringify(window.CM))); setRoad(JSON.parse(JSON.stringify(window.CM.roadmap))); setOpen(null); setOpenRoad(null); }
  }

  const inboxOpen = data.inbox.filter((x) => x.chosen == null).length;
  const bugsOpen = data.bugs.filter((x) => x.status === "open").length;
  const auditNew = data.audits.filter((x) => x.status === "new").length;

  const NAV = [
    { group: "工作台", items: [
      { k: "overview", icon: "target", label: "總覽" },
      { k: "inbox", icon: "inbox", label: "收件匣", badge: inboxOpen, alert: true },
      { k: "todos", icon: "grid", label: "待辦" },
      { k: "dogfood", icon: "beaker", label: "Dogfood 測試" },
      { k: "bugs", icon: "bug", label: "Bugs", badge: bugsOpen },
      { k: "audit", icon: "moon", label: "夜審結果", badge: auditNew },
    ] },
    { group: "客人 LINE", items: [
      { k: "lineInbox", icon: "msg", label: "LINE 收件匣" },
      { k: "lineSetup", icon: "shield", label: "LINE 設定教學" },
    ] },
    { group: "規劃與文件", items: [
      { k: "spec", icon: "file", label: "Spec / 需求" },
      { k: "wishlist", icon: "wand", label: "願望清單" },
      { k: "roadmap", icon: "gantt", label: "進度 Roadmap" },
      { k: "initiatives", icon: "shield", label: "大計劃" },
      { k: "sysmap", icon: "layers", label: "系統地圖" },
    ] },
    { group: "參考", items: [
      { k: "security", icon: "shield", label: "資安審查" },
      { k: "principles", icon: "shield", label: "原則與規則" },
      { k: "archAudit", icon: "bug", label: "架構審查" },
      { k: "healthPlan", icon: "moon", label: "夜審計畫" },
      { k: "completed", icon: "todo", label: "完成里程碑" },
      { k: "decisions", icon: "inbox", label: "決策紀錄" },
      { k: "glossary", icon: "file", label: "名詞表" },
      { k: "tools", icon: "search", label: "工具" },
    ] },
  ];
  // 參考頁 → 用通用 RefView 渲染 data.ref[key]（key 對應 cm_data.ref）
  const REF = { initiatives: "initiatives", sysmap: "sysmap", security: "security", principles: "principles", archAudit: "archAudit", healthPlan: "healthPlan", completed: "completed", decisions: "decisions", glossary: "glossary", tools: "tools" };
  const titles = { overview: ["總覽", "工作台首頁"], inbox: ["收件匣", "需要你決定的事"], todos: ["待辦", "看板"], dogfood: ["Dogfood 測試", "上架前試用"], bugs: ["Bugs", "問題追蹤"], audit: ["夜審結果", "AI 自動健檢"], spec: ["Spec / 需求", "規格文件"], wishlist: ["願望清單", "想到就丟"], roadmap: ["進度 Roadmap", "開發時間軸"],
    lineInbox: ["LINE 收件匣", "客人訊息 · 預設回覆 · 回覆草稿"], lineSetup: ["LINE 設定教學", "把官方帳號接上來"],
    initiatives: ["大計劃", "策略規劃與大專案"], sysmap: ["系統地圖", "170 份架構文件"], security: ["資安審查", "權限/隔離/集團架構"], principles: ["原則與規則", "4 層架構 + 開發鐵則"], archAudit: ["架構審查", "2026-05-23 深掃紀錄"], healthPlan: ["夜審計畫", "每晚自動健檢"], completed: ["完成里程碑", "已完成 / 已出貨"], decisions: ["決策紀錄", "等拍板 / 已拍板"], glossary: ["名詞表", "術語白話"], tools: ["工具", "查詢 / 分析 / 舊報告"] };

  const roadTask = road.find((x) => x.id === openRoad) || null;

  return (
    <div className="app">
      {navOpen && <div className="sidebar-scrim" onClick={() => setNavOpen(false)} />}
      <aside className={"sidebar" + (navOpen ? " open" : "")}>
        <div className="brand">
          <div className="brand-mark"><Icon name="layers" size={19} /></div>
          <div><div className="brand-name">ChefsMate</div><div className="brand-sub">老闆 × AI 主控台</div></div>
        </div>
        <button className="btn btn-primary" style={{ justifyContent: "center", margin: "2px 2px 6px" }} onClick={() => { setAskOpen(true); setNavOpen(false); }}><Icon name="spark" size={15} />問 AI</button>
        {NAV.map((g) => (
          <React.Fragment key={g.group}>
            <div className="nav-section">{g.group}</div>
            {g.items.map((n) => (
              <button key={n.k} className={"nav-item" + (section === n.k ? " active" : "")} onClick={() => { setSection(n.k); setNavOpen(false); }}>
                <Icon name={n.icon} size={18} className="ico" />{n.label}
                {n.badge > 0 && <span className={"nav-badge" + (n.alert ? " alert" : "")}>{n.badge}</span>}
              </button>
            ))}
          </React.Fragment>
        ))}
        <div style={{ marginTop: "auto", display: "flex", flexDirection: "column", gap: 4 }}>
          <SyncBar sync={sync} />
          <button className="nav-item" onClick={resetAll}><Icon name="refresh" size={16} className="ico" />還原範例資料</button>
          <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "10px", borderRadius: 11, background: "var(--surface-2)", marginTop: 2 }}>
            <AIAvatar size={30} />
            <div style={{ minWidth: 0 }}><div style={{ fontSize: 12.5, fontWeight: 700 }}>Claude</div><div style={{ fontSize: 11, color: "var(--ok)", display: "flex", alignItems: "center", gap: 4 }}><span className="dot" style={{ background: "var(--ok)" }} />值班中</div></div>
          </div>
        </div>
      </aside>

      <main className="main">
        <header className="topbar">
          <button className="btn btn-ghost btn-icon hamburger" onClick={() => setNavOpen(true)}><Icon name="menu" size={20} /></button>
          <div style={{ flex: 1, minWidth: 0 }}>
            <h1>{titles[section][0]}</h1>
            <div className="sub">{titles[section][1]}</div>
          </div>
          {section === "roadmap" && <button className="btn" onClick={roadAdvance}><Icon name="spark" size={15} />讓 AI 推進一步</button>}
          <QuickCapture onCapture={quickCapture} />
        </header>

        <div className="scroll-area" style={{ padding: "22px 26px 44px" }}>
          {section === "overview" && <OverviewView data={data} onOpen={(it, t) => setOpen({ item: it, type: t })} onGoto={setSection} />}
          {section === "inbox" && <InboxView items={data.inbox} onOpen={(it, t) => setOpen({ item: it, type: t })} onAnswer={actions.onAnswer} />}
          {section === "todos" && <TodosView items={data.todos} onOpen={(it, t) => setOpen({ item: it, type: t })} justId={justId} />}
          {section === "dogfood" && <DogfoodView items={data.dogfood} onOpen={(it, t) => setOpen({ item: it, type: t })} justId={justId} />}
          {section === "bugs" && <BugsView bugs={data.bugs} onOpen={(it, t) => setOpen({ item: it, type: t })} onToggleStatus={actions.onToggleStatus} />}
          {section === "audit" && <AuditView audits={data.audits} onReview={actions.onReview} onOpen={(it, t) => setOpen({ item: it, type: t })} />}
          {section === "spec" && <SpecView items={data.specs} onOpen={(it, t) => setOpen({ item: it, type: t })} />}
          {section === "wishlist" && <WishlistView wishes={data.wishes} onAdd={addWish} />}
          {section === "roadmap" && <RoadmapView road={road} onOpen={setOpenRoad} justId={justId} actions={roadActions} />}
          {section === "lineInbox" && <LineInboxView />}
          {section === "lineSetup" && <LineSetupView />}
          {REF[section] && <RefView sec={(data.ref || {})[REF[section]]} />}
        </div>
      </main>

      {liveOpen && <ItemDrawer item={liveOpen.item} type={liveOpen.type} onClose={() => setOpen(null)} actions={actions} />}
      {roadTask && <TaskDrawer task={roadTask} onClose={() => setOpenRoad(null)} onToggle={roadActions.toggleSub} onResolve={roadActions.resolve} onSend={roadActions.send} onReorder={roadActions.reorder} />}
      {askOpen && <AskAIModal onClose={() => setAskOpen(false)} onAsk={(q) => { setAskOpen(false); quickCapture("（問 AI）" + q); }} />}

      <div className="toast-stack">
        {toasts.map((t) => (
          <div key={t.id} className="toast">
            {t.kind === "ai" ? <AIAvatar size={32} /> : <div style={{ width: 32, height: 32, borderRadius: 10, background: "var(--ok-soft)", color: "var(--ok)", display: "grid", placeItems: "center", flexShrink: 0 }}><Icon name="check" size={17} /></div>}
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 2 }}>
                <span style={{ fontSize: 12.5, fontWeight: 700 }}>{t.kind === "ai" ? "AI 助理" : "完成"}</span>
                {t.stage && <span className="dot" style={{ background: STAGE_COLOR[t.stage] }} />}
              </div>
              <div style={{ fontSize: 12.5, color: "var(--ink-2)", lineHeight: 1.45 }}>{t.text}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ---- topbar quick capture ---- */
function QuickCapture({ onCapture }) {
  const [text, setText] = useState("");
  function go() { const v = text.trim(); if (!v) return; onCapture(v); setText(""); }
  return (
    <div className="qc-wrap" style={{ maxWidth: 340 }}>
      <input className="input qc-input" style={{ width: 220 }} placeholder="快速捕捉一個待辦…" value={text} onChange={(e) => setText(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); go(); } }} />
      <button className="btn btn-icon btn-primary" onClick={go} title="加入待辦"><Icon name="plus" size={16} /></button>
    </div>
  );
}

/* ---- roadmap (reuse GanttView + StatHeader) ---- */
function RoadmapView({ road, onOpen, justId, actions }) {
  return (
    <div>
      <PageHead icon="gantt" color="var(--accent)" title="進度 Roadmap" desc="開發大項的階段時間軸。色塊可拖曳調整時程、虛線表示相依；點任務看子任務、進度紀錄與卡關。" />
      <div style={{ marginBottom: 16 }}><StatHeader tasks={road} /></div>
      <GanttView tasks={road} onOpen={onOpen} pxDay={16} onShiftStage={actions.shift} justUpdatedId={justId} />
    </div>
  );
}

/* ---- 系統地圖 ---- */
function SysmapView() {
  const totalDone = SYSMAP_LAYERS.reduce((a, l) => a + l.done, 0);
  const totalAll = SYSMAP_LAYERS.reduce((a, l) => a + l.total, 0);
  return (
    <div>
      <PageHead icon="layers" color="var(--c-bluegrey)" title="系統地圖" desc="架構文件分 L0–L7 層，AI 與你都能查某功能怎麼運作（Mermaid 流程圖 + 目錄）。下面是文件覆蓋度。" />
      <div className="card" style={{ padding: "18px 20px", marginBottom: 14, display: "flex", alignItems: "center", gap: 18 }}>
        <ProgressRing value={totalDone / totalAll} size={62} stroke={6} />
        <div><div style={{ fontSize: 12.5, color: "var(--ink-3)", fontWeight: 700 }}>整體文件覆蓋度</div><div style={{ fontSize: 24, fontWeight: 700 }}>{Math.round(totalDone / totalAll * 100)}%</div><div className="mono" style={{ fontSize: 12, color: "var(--ink-3)" }}>{totalDone}/{totalAll} 份文件</div></div>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {SYSMAP_LAYERS.map((l) => (
          <div key={l.id} className="card" style={{ padding: "13px 18px", display: "flex", alignItems: "center", gap: 14 }}>
            <span className="mono" style={{ fontSize: 12, fontWeight: 700, color: "var(--c-bluegrey)", width: 30 }}>{l.id}</span>
            <span style={{ fontSize: 13.5, fontWeight: 600, width: 130 }}>{l.label}</span>
            <div style={{ flex: 1 }}><Bar value={l.done / l.total} color="var(--c-bluegrey)" /></div>
            <span className="mono" style={{ fontSize: 12, color: "var(--ink-3)" }}>{l.done}/{l.total}</span>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 14, padding: "14px 18px", border: "1px dashed var(--line)", borderRadius: 14, fontSize: 12.5, color: "var(--ink-3)", textAlign: "center" }} className="mono">［ Mermaid 架構圖閱讀器 — 接 sysmap/viewer ］</div>
    </div>
  );
}

/* ---- 大計劃 ---- */
function PlansView() {
  return (
    <div>
      <PageHead icon="shield" color="var(--c-purple)" title="大計劃" desc="長線策略規劃。方向性的東西放這，落地後拆成 Roadmap 與待辦。" />
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {PLANS.map((p) => (
          <div key={p.id} className="card" style={{ padding: 0, overflow: "hidden" }}>
            <div className="card-top" style={{ background: p.color }} />
            <div style={{ padding: "15px 18px" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
                <span style={{ fontSize: 15, fontWeight: 700 }}>{p.title}</span>
                <span className="chip" style={{ marginLeft: "auto", background: p.color + "22", color: p.color }}>{p.status}</span>
              </div>
              <div style={{ fontSize: 13, color: "var(--ink-2)", lineHeight: 1.65 }}>{p.body}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ---- 問 AI modal ---- */
function AskAIModal({ onClose, onAsk }) {
  const [q, setQ] = useState("");
  const [reply, setReply] = useState(null);
  function send() { const v = q.trim(); if (!v) return; setReply("我看了你的問題。我可以把它變成一個待辦來追蹤，或你想先討論方向？按下面把它丟進待辦，我就開始。"); }
  return (
    <div className="overlay" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 480 }}>
        <div style={{ padding: "18px 22px 12px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", gap: 11 }}>
          <AIAvatar size={32} /><div style={{ flex: 1 }}><div style={{ fontWeight: 700, fontSize: 16 }}>問 AI 助理</div><div style={{ fontSize: 12, color: "var(--ink-3)" }}>有想法、有問題，先問問看</div></div>
          <button className="btn btn-ghost btn-icon" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div style={{ padding: "18px 22px", display: "flex", flexDirection: "column", gap: 14 }}>
          <textarea className="textarea" autoFocus placeholder="例如：訂位尖峰那個重複扣位，最快能怎麼擋？" value={q} onChange={(e) => { setQ(e.target.value); setReply(null); }} style={{ minHeight: 90 }} />
          {reply && <div style={{ display: "flex", gap: 10, padding: "11px 13px", background: "var(--accent-soft)", borderRadius: 12 }}><AIAvatar size={26} /><div style={{ fontSize: 13, color: "var(--ink)", lineHeight: 1.55 }}>{reply}</div></div>}
        </div>
        <div style={{ padding: "12px 22px", borderTop: "1px solid var(--line)", display: "flex", gap: 9, justifyContent: "flex-end" }}>
          {!reply ? <button className="btn btn-primary" onClick={send}><Icon name="send" size={15} />送出</button>
            : <><button className="btn" onClick={onClose}>關閉</button><button className="btn btn-primary" onClick={() => onAsk(q)}><Icon name="plus" size={15} />建立成待辦</button></>}
        </div>
      </div>
    </div>
  );
}

/* ---- 雲端同步狀態列 ---- */
function SyncBar({ sync }) {
  function login() { if (window.consoleStore) window.consoleStore.loginWithGoogle(); }
  function pasteUrl() {
    const u = prompt("若登入後沒回到這頁，把登入完成後網址列那串貼進來：");
    if (u && window.consoleStore) window.consoleStore.applyAuthFromURL(u).then((r) => {
      if (r && r.ok) location.reload(); else alert("沒成功：" + ((r && r.error) || "未知"));
    });
  }
  if (!sync.loaded) {
    return <div style={{ fontSize: 11.5, color: "var(--ink-3)", padding: "8px 10px" }}>雲端連線中…</div>;
  }
  if (sync.authed) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 7, padding: "8px 10px", borderRadius: 11, background: "var(--ok-soft)", color: "var(--ok)", fontSize: 12, fontWeight: 700 }}>
        <Icon name="check" size={14} />已同步到雲端
      </div>
    );
  }
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
      <button className="btn btn-primary" style={{ justifyContent: "center", fontSize: 12.5 }} onClick={login}>
        <Icon name="spark" size={14} />登入以同步到雲端
      </button>
      <button onClick={pasteUrl} style={{ background: "none", border: "none", color: "var(--ink-3)", fontSize: 10.5, cursor: "pointer", padding: "2px" }}>登入後沒跳回？貼網址救援</button>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<CMApp />);
