// ============================================================
// Bank To-Do (Personal Workspace) — standalone, per-user module.
// Daily 1-3-5 planner: one big (size 1), three medium (size 3),
// five small (size 5) goals per day. Tasks grouped by Matrix A→B→C→D
// (Eisenhower importance/urgency). Talks DIRECTLY to Supabase REST
// (bank_todos / bank_todo_subtasks) so it stays decoupled from the
// global DATA / dashboard layer. RLS = each user sees only their rows.
// ============================================================
(function () {
  window.SCREENS = window.SCREENS || {};
  const { useState, useEffect, useRef } = React;
  const IC = window.IC;

  // ── tiny self-contained REST client ──
  const cfg  = window.SUPABASE_CONFIG || {};
  const LIVE = !!(cfg.url && cfg.anonKey);
  const BASE = LIVE ? cfg.url.replace(/\/$/, "") : "";
  const ANON = cfg.anonKey || "";
  async function hdr(extra) {
    let token = ANON;
    try { if (window.Auth && window.Auth.getToken) { const t = await window.Auth.getToken(); if (t) token = t; } } catch (e) {}
    return Object.assign({ apikey: ANON, Authorization: "Bearer " + token, "Content-Type": "application/json", Accept: "application/json" }, extra || {});
  }
  async function apiGet(path)         { if (!LIVE) return []; try { const r = await fetch(BASE + "/rest/v1/" + path, { headers: await hdr() }); return r.ok ? r.json() : []; } catch (e) { return []; } }
  async function apiPost(table, body) { if (!LIVE) return null; try { const r = await fetch(BASE + "/rest/v1/" + table, { method: "POST", headers: await hdr({ Prefer: "return=representation" }), body: JSON.stringify(body) }); if (!r.ok) return null; const d = await r.json(); return Array.isArray(d) ? d[0] : d; } catch (e) { return null; } }
  async function apiPatch(table, id, body) { if (!LIVE) return; try { await fetch(BASE + "/rest/v1/" + table + "?id=eq." + id, { method: "PATCH", headers: await hdr(), body: JSON.stringify(body) }); } catch (e) {} }
  async function apiDel(table, id)    { if (!LIVE) return; try { await fetch(BASE + "/rest/v1/" + table + "?id=eq." + id, { method: "DELETE", headers: await hdr() }); } catch (e) {} }
  function uid() { try { return (window.Auth && window.Auth.current() || {}).sub || ""; } catch (e) { return ""; } }
  function localId() { return "tmp-" + Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }

  // ── date helpers (all LOCAL-time safe — never round-trip through UTC) ──
  function localISO(d) { return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0"); }
  function todayISO() { try { if (window.todayISO) return window.todayISO(); } catch (e) {} return localISO(new Date()); }
  // Add n days to a YYYY-MM-DD string using LOCAL date math (fixes the UTC
  // off-by-one that made "+1 day" return the same day in UTC+7).
  function addDaysISO(iso, n, fallback) {
    try {
      const p = (iso || fallback || todayISO()).split("-").map(Number);
      const d = new Date(p[0], p[1] - 1, p[2]);
      d.setDate(d.getDate() + n);
      return localISO(d);
    } catch (e) { return iso || fallback || todayISO(); }
  }
  function fmtDate(iso) { try { return iso ? (window.isoToBE ? window.isoToBE(iso) : iso) : ""; } catch (e) { return iso || ""; } }
  const THM = ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
  function fmtDateTime(iso) {
    try {
      const d = new Date(iso);
      if (isNaN(d)) return "";
      return d.getDate() + " " + THM[d.getMonth()] + " " + String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
    } catch (e) { return ""; }
  }

  // ── display meta ──
  const GROUPS = ["A", "B", "C", "D"];
  // Highly descriptive Eisenhower-style labels (used in dropdowns + headings).
  const GROUP_LABEL = {
    A: "A: สำคัญ & เร่งด่วน (ทำทันที)",
    B: "B: สำคัญ แต่ไม่เร่งด่วน (วางแผนไว้)",
    C: "C: ไม่สำคัญ แต่เร่งด่วน (มอบหมาย/ทำทีหลัง)",
    D: "D: ไม่สำคัญ & ไม่เร่งด่วน (พิจารณาตัดทิ้ง)",
  };
  const SIZES = [1, 3, 5];
  // Full rule explanation for dropdown options…
  const SIZE_OPTION = {
    1: "เป้าหมายหลัก (ควรมี 1 งาน/วัน)",
    3: "งานสำคัญรอง (ไม่เกิน 3 งาน/วัน)",
    5: "งานย่อยทั่วไป (ไม่เกิน 5 งาน/วัน)",
  };
  // …short label for compact pills/badges.
  const SIZE_SHORT = { 1: "เป้าหมายหลัก", 3: "งานสำคัญรอง", 5: "งานย่อยทั่วไป" };
  // The 1-3-5 daily budget: at most 1×size-1, 3×size-3, 5×size-5.
  const LIMIT = { 1: 1, 3: 3, 5: 5 };
  const STATUS_META = {
    pending:     { th: "ค้างอยู่",    tone: "neutral" },
    completed:   { th: "เสร็จแล้ว",   tone: "ok" },
    rescheduled: { th: "เลื่อนแล้ว",  tone: "warn" },
  };

  // ── shared input styles ──
  const inputStyle = { width: "100%", boxSizing: "border-box", border: "1px solid var(--line)", borderRadius: 9, padding: "9px 12px", fontSize: 14, background: "var(--surface)", color: "var(--ink)" };
  const selFieldStyle = { ...inputStyle, fontSize: 13, padding: "9px 10px" };
  const selStyle = { border: 0, background: "none", outline: "none", font: "inherit", fontSize: 13.5, flex: 1, color: "var(--ink)" };

  // Labeled column for the inline add form (label sits above the control).
  function LabeledField({ label, flex, children }) {
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 4, flex: flex || "0 0 auto", minWidth: 0 }}>
        <span style={{ fontSize: 11.5, fontWeight: 600, color: "var(--faint)", paddingLeft: 2 }}>{label}</span>
        {children}
      </div>
    );
  }
  function Field({ label, children }) {
    return (<label className="login-field"><span>{label}</span><div className="login-input">{children}</div></label>);
  }

  // ── Edit modal (add is inline; this handles editing an existing task) ──
  function EditModal({ edit, onClose, onSave }) {
    const [f, setF] = useState({
      title:        edit.title || "",
      target_date:  edit.target_date || todayISO(),
      matrix_group: edit.matrix_group || "A",
      size_rule:    edit.size_rule || 1,
    });
    const [saving, setSaving] = useState(false);
    const set = (k) => (e) => setF((p) => ({ ...p, [k]: e.target.value }));
    const submit = async (e) => {
      e.preventDefault();
      if (!f.title.trim()) return;
      setSaving(true);
      await onSave({ ...f, size_rule: +f.size_rule, target_date: f.target_date || null });
      setSaving(false);
      onClose();
    };
    return (
      <div className="login-overlay" onClick={window.NOOP}>
        <form className="login-card" style={{ maxWidth: 480, width: "94vw" }} onClick={(e) => e.stopPropagation()} onSubmit={submit}>
          <button type="button" className="login-x icon-btn" onClick={onClose}>{IC.x}</button>
          <div className="login-badge">{IC.check}</div>
          <h2>แก้ไขสิ่งที่ต้องทำ</h2>
          <div style={{ display: "grid", gap: 11, marginTop: 8 }}>
            <Field label="งานที่ต้องทำ / เป้าหมาย *"><input value={f.title} onChange={set("title")} placeholder="เช่น อ่านหนังสือการตลาด 1 บท" autoFocus /></Field>
            <Field label="กำหนดเสร็จ (Target Date)"><input type="date" value={f.target_date || ""} onChange={set("target_date")} style={selStyle} /></Field>
            <Field label="ความสำคัญ (Matrix)"><select value={f.matrix_group} onChange={set("matrix_group")} style={selStyle}>{GROUPS.map((g) => <option key={g} value={g}>{GROUP_LABEL[g]}</option>)}</select></Field>
            <Field label="ขนาดงาน (1-3-5)"><select value={f.size_rule} onChange={set("size_rule")} style={selStyle}>{SIZES.map((s) => <option key={s} value={s}>{SIZE_OPTION[s]}</option>)}</select></Field>
          </div>
          <div className="login-acts" style={{ marginTop: 16 }}>
            <button type="button" className="btn" onClick={onClose}>ยกเลิก</button>
            <button type="submit" className="btn pri" disabled={saving}>{saving ? "กำลังบันทึก…" : "บันทึก"}</button>
          </div>
        </form>
      </div>
    );
  }

  window.SCREENS["bank-todo"] = function BankTodoList() {
    const today = todayISO();
    const [todos, setTodos] = useState([]);
    const [subs, setSubs] = useState({});           // todoId -> [subtask]
    const [loading, setLoading] = useState(true);
    const [editing, setEditing] = useState(null);
    const [reviewOpen, setReviewOpen] = useState(true);
    const subInput = useRef({});

    // inline add form
    const [form, setForm] = useState({ title: "", target_date: today, matrix_group: "A", size_rule: 1 });
    const setF = (k) => (e) => setForm((p) => ({ ...p, [k]: e.target.value }));

    // initial load
    useEffect(() => {
      let alive = true;
      (async () => {
        const t = await apiGet("bank_todos?select=*&order=created_at.desc");
        const s = await apiGet("bank_todo_subtasks?select=*&order=order_index.asc");
        if (!alive) return;
        setTodos(t || []);
        const byTodo = {};
        (s || []).forEach((x) => { (byTodo[x.todo_id] = byTodo[x.todo_id] || []).push(x); });
        setSubs(byTodo);
        setLoading(false);
      })();
      return () => { alive = false; };
    }, []);

    // ── todo ops ──
    const addTodo = async (e) => {
      e && e.preventDefault();
      if (!form.title.trim()) return;               // empty guard only — button is NEVER disabled
      const row = { user_id: uid() || null, title: form.title.trim(), target_date: form.target_date || null, matrix_group: form.matrix_group, size_rule: +form.size_rule, status: "pending" };
      const saved = await apiPost("bank_todos", row);
      const rec = saved || { id: localId(), created_at: new Date().toISOString(), ...row };
      setTodos((p) => [rec, ...p]);
      setForm((p) => ({ ...p, title: "" }));         // keep date/group/size for fast repeat entry
    };
    const editTodo = async (data) => {
      await apiPatch("bank_todos", editing.id, data);
      setTodos((p) => p.map((t) => (t.id === editing.id ? { ...t, ...data } : t)));
    };
    const setStatus = async (t, status) => {
      await apiPatch("bank_todos", t.id, { status });
      setTodos((p) => p.map((x) => (x.id === t.id ? { ...x, status } : x)));
    };
    // Move a task to a specific date (always returns it to the active "pending" plan).
    const setDate = async (t, iso, msg) => {
      await apiPatch("bank_todos", t.id, { target_date: iso, status: "pending" });
      setTodos((p) => p.map((x) => (x.id === t.id ? { ...x, target_date: iso, status: "pending" } : x)));
      if (msg) window.toast && window.toast(msg);
    };
    // "เลื่อน 1 วัน" — actually adds +1 day to target_date (local-safe) + updates UI.
    const rescheduleOneDay = (t) => { const next = addDaysISO(t.target_date, 1, today); return setDate(t, next, "เลื่อนเป็น " + fmtDate(next) + " แล้ว"); };
    const removeTodo = async (t) => {
      if (!confirm("ลบรายการนี้?\n\"" + t.title + "\"")) return;
      await apiDel("bank_todos", t.id);
      setTodos((p) => p.filter((x) => x.id !== t.id));
      setSubs((p) => { const n = { ...p }; delete n[t.id]; return n; });
    };

    // ── subtask ops ──
    const addSub = async (todoId) => {
      const title = (subInput.current[todoId] || "").trim();
      if (!title) return;
      const order = (subs[todoId] || []).length;
      const row = { todo_id: todoId, title, is_completed: false, order_index: order };
      const saved = await apiPost("bank_todo_subtasks", row);
      const rec = saved || { id: localId(), ...row };
      setSubs((p) => ({ ...p, [todoId]: [...(p[todoId] || []), rec] }));
      subInput.current[todoId] = "";
      const el = subInput.current["el-" + todoId]; if (el) el.value = "";
    };
    const toggleSub = async (todoId, st) => {
      const next = !st.is_completed;
      const completed_at = next ? new Date().toISOString() : null;   // stamp on check, clear on uncheck
      await apiPatch("bank_todo_subtasks", st.id, { is_completed: next, completed_at });
      setSubs((p) => ({ ...p, [todoId]: p[todoId].map((x) => (x.id === st.id ? { ...x, is_completed: next, completed_at } : x)) }));
    };
    const removeSub = async (todoId, st) => {
      await apiDel("bank_todo_subtasks", st.id);
      setSubs((p) => ({ ...p, [todoId]: p[todoId].filter((x) => x.id !== st.id) }));
    };

    // ── overdue (Daily Review): pending tasks dated before today ──
    const overdue = todos.filter((t) => t.status === "pending" && t.target_date && t.target_date < today)
      .sort((a, b) => (a.target_date < b.target_date ? -1 : 1));

    // ── today vs. other (overdue excluded from "other" — it lives in the Review section) ──
    const isToday = (t) => t.target_date === today;
    const isOverdue = (t) => t.status === "pending" && t.target_date && t.target_date < today;
    const todayTasks = todos.filter(isToday);
    const otherTasks = todos.filter((t) => !isToday(t) && !isOverdue(t));

    // ── 1-3-5 budget (today only) ──
    const used = { 1: 0, 3: 0, 5: 0 };
    todayTasks.forEach((t) => { if (used[t.size_rule] != null) used[t.size_rule]++; });
    const warnings = [];
    if (used[1] > LIMIT[1]) warnings.push("เป้าหมายหลักเกิน 1 งานแล้ว (ควรมีแค่ 1 งาน/วัน)");
    if (used[3] > LIMIT[3]) warnings.push("งานสำคัญรองเกิน 3 งานแล้ว");
    if (used[5] > LIMIT[5]) warnings.push("งานย่อยทั่วไปเกิน 5 งานแล้ว");

    // ── one task card ──
    const TaskCard = (t) => {
      const sm = STATUS_META[t.status] || STATUS_META.pending;
      const st = subs[t.id] || [];
      const done = st.filter((x) => x.is_completed).length;
      const completed = t.status === "completed";
      return (
        <div className="card" key={t.id} style={{ padding: 14, opacity: completed ? 0.6 : 1, transition: "opacity .25s ease, background .25s ease" }}>
          <div style={{ display: "flex", alignItems: "flex-start", gap: 10, flexWrap: "wrap" }}>
            <div style={{ flex: "1 1 220px", minWidth: 0 }}>
              <div style={{ fontWeight: 600, fontSize: 14.5, wordBreak: "break-word", transition: "color .25s ease, text-decoration .25s ease", textDecoration: completed ? "line-through" : "none", textDecorationColor: completed ? "var(--muted)" : "inherit", color: completed ? "var(--muted)" : "var(--ink)" }}>{t.title}</div>
              <div style={{ display: "flex", gap: 7, flexWrap: "wrap", marginTop: 7, alignItems: "center" }}>
                {t.size_rule && <span className="pill neutral" title={SIZE_OPTION[t.size_rule]}><i />ขนาด {t.size_rule} · {SIZE_SHORT[t.size_rule] || ""}</span>}
                {t.status !== "pending" && <span className={"pill " + sm.tone}><i />{sm.th}</span>}
                {!isToday(t) && t.target_date && <span className="fitem" style={{ fontSize: 12 }}>{IC.cal} {fmtDate(t.target_date)}</span>}
                {st.length > 0 && <span className="fitem" style={{ fontSize: 12 }}>{IC.check} {done}/{st.length}</span>}
              </div>
            </div>
            <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
              {!completed
                ? <button className="icon-btn" title="ทำเสร็จ" onClick={() => setStatus(t, "completed")} style={{ width: 30, height: 30, color: "var(--ok)" }}>{IC.check}</button>
                : <button className="icon-btn" title="ทำต่อ" onClick={() => setStatus(t, "pending")} style={{ width: 30, height: 30 }}>{IC.arrowUp}</button>}
              <button className="icon-btn" title="เลื่อนออกไป 1 วัน (+1)" onClick={() => rescheduleOneDay(t)} style={{ width: 30, height: 30, color: "var(--warn)" }}>{IC.clock}</button>
              <button className="icon-btn" title="แก้ไข" onClick={() => setEditing(t)} style={{ width: 30, height: 30 }}>{IC.settings}</button>
              <button className="icon-btn" title="ลบ" onClick={() => removeTodo(t)} style={{ width: 30, height: 30, color: "var(--danger)" }}>{IC.x}</button>
            </div>
          </div>
          {/* subtasks */}
          <div style={{ marginTop: 10, borderTop: "1px solid var(--line-soft)", paddingTop: 8, display: "grid", gap: 5 }}>
            {st.map((s) => (
              <div key={s.id} style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                <button className="icon-btn" onClick={() => toggleSub(t.id, s)} title={s.is_completed ? "ทำเครื่องหมายว่ายังไม่เสร็จ" : "ทำเครื่องหมายว่าเสร็จ"} style={{ width: 22, height: 22, color: s.is_completed ? "var(--ok)" : "var(--faint)", transition: "color .2s ease", flexShrink: 0 }}>{IC.check}</button>
                <span style={{ flex: "1 1 120px", minWidth: 0, fontSize: 13, transition: "color .25s ease, text-decoration .25s ease", textDecoration: s.is_completed ? "line-through" : "none", textDecorationColor: s.is_completed ? "var(--muted)" : "inherit", color: s.is_completed ? "var(--muted)" : "var(--ink)", wordBreak: "break-word" }}>{s.title}</span>
                {s.is_completed && s.completed_at && <span style={{ fontSize: 11, color: "var(--faint)", whiteSpace: "nowrap", flexShrink: 0 }}>(เสร็จเมื่อ {fmtDateTime(s.completed_at)})</span>}
                <button className="icon-btn" onClick={() => removeSub(t.id, s)} style={{ width: 22, height: 22, color: "var(--faint)", flexShrink: 0 }}>{IC.x}</button>
              </div>
            ))}
            <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
              <input ref={(el) => { subInput.current["el-" + t.id] = el; }} defaultValue="" placeholder="เพิ่มขั้นตอนย่อย…"
                onChange={(e) => { subInput.current[t.id] = e.target.value; }}
                onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addSub(t.id); } }}
                style={{ flex: 1, border: "1px solid var(--line)", borderRadius: 8, padding: "5px 10px", fontSize: 12.5, background: "var(--surface)", color: "var(--ink)" }} />
              <button className="btn" onClick={() => addSub(t.id)} style={{ padding: "5px 11px" }}>{IC.plus}</button>
            </div>
          </div>
        </div>
      );
    };

    // group today's tasks by matrix A→B→C→D (+ any without a group, last)
    const todayGroups = GROUPS.map((g) => ({ g, items: todayTasks.filter((t) => t.matrix_group === g) }));
    const ungrouped = todayTasks.filter((t) => !GROUPS.includes(t.matrix_group));

    return (
      <div className="page wide" style={{ maxWidth: 760 }}>
        <header>
          <div className="eyebrow">Personal Workspace</div>
          <h1 className="page-title">To-Do List (Bank)</h1>
          <p className="page-sub">กฎ 1-3-5 ต่อวัน — เป้าหมายหลัก 1 · งานสำคัญรอง 3 · งานย่อยทั่วไป 5 (เห็นเฉพาะคุณ)</p>
        </header>

        {/* ── Daily Review: clear what's carried over from previous days ── */}
        {overdue.length > 0 &&
          <div className="card" style={{ padding: 14, marginBottom: 14, border: "1px solid var(--warn)", background: "var(--warn-soft, #fff8e8)" }}>
            <button type="button" onClick={() => setReviewOpen((v) => !v)}
              style={{ display: "flex", alignItems: "center", gap: 9, width: "100%", background: "none", border: 0, cursor: "pointer", padding: 0, color: "var(--ink)" }}>
              <span style={{ color: "var(--warn)", flexShrink: 0 }}>{IC.warn}</span>
              <b style={{ flex: 1, textAlign: "left", fontSize: 14.5, lineHeight: 1.4 }}>ทบทวนรายการค้างคา (Daily Review)</b>
              <span className="pill warn" style={{ flexShrink: 0 }}><i />{overdue.length}</span>
              <span style={{ color: "var(--faint)", flexShrink: 0, transform: reviewOpen ? "rotate(90deg)" : "none", transition: "transform .15s ease", display: "inline-flex" }}>{IC.chevR}</span>
            </button>
            {reviewOpen &&
              <div style={{ display: "grid", gap: 8, marginTop: 12 }}>
                {overdue.map((t) => (
                  <div key={t.id} style={{ display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center", padding: "10px 12px", borderRadius: 10, background: "var(--surface)", border: "1px solid var(--line)" }}>
                    <div style={{ flex: "1 1 180px", minWidth: 0 }}>
                      <div style={{ fontSize: 13.5, fontWeight: 600, wordBreak: "break-word" }}>{t.title}</div>
                      <div style={{ fontSize: 12, color: "var(--faint)", marginTop: 3, display: "flex", alignItems: "center", gap: 5 }}>{IC.cal} ค้างจาก {fmtDate(t.target_date)}</div>
                    </div>
                    <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                      <button className="btn" onClick={() => setDate(t, today, "ย้ายมาวันนี้แล้ว")} style={{ padding: "6px 11px", fontSize: 12.5, whiteSpace: "nowrap" }}>{IC.arrowR} ย้ายมาวันนี้</button>
                      <button className="btn" onClick={() => setDate(t, addDaysISO(today, 1), "เลื่อนเป็นพรุ่งนี้แล้ว")} style={{ padding: "6px 11px", fontSize: 12.5, whiteSpace: "nowrap" }}>{IC.clock} เลื่อนเป็นพรุ่งนี้</button>
                      <button className="btn" onClick={() => removeTodo(t)} style={{ padding: "6px 11px", fontSize: 12.5, whiteSpace: "nowrap", color: "var(--danger)" }}>{IC.x} ลบรายการ</button>
                    </div>
                  </div>
                ))}
              </div>}
          </div>}

        {/* ── inline add form (labeled fields) ── */}
        <form className="card" onSubmit={addTodo} style={{ padding: 14, marginBottom: 14, display: "flex", gap: 10, flexWrap: "wrap", alignItems: "flex-end" }}>
          <LabeledField label="งานที่ต้องทำ / เป้าหมาย" flex="3 1 240px">
            <input value={form.title} onChange={setF("title")} placeholder="เช่น อ่านหนังสือการตลาด 1 บท" style={inputStyle} />
          </LabeledField>
          <LabeledField label="กำหนดเสร็จ (Target Date)" flex="1 1 150px">
            <input type="date" value={form.target_date || ""} onChange={setF("target_date")} style={inputStyle} />
          </LabeledField>
          <LabeledField label="ความสำคัญ (Matrix)" flex="1 1 200px">
            <select value={form.matrix_group} onChange={setF("matrix_group")} style={selFieldStyle}>
              {GROUPS.map((g) => <option key={g} value={g}>{GROUP_LABEL[g]}</option>)}
            </select>
          </LabeledField>
          <LabeledField label="ขนาดงาน (1-3-5)" flex="1 1 200px">
            <select value={form.size_rule} onChange={setF("size_rule")} style={selFieldStyle}>
              {SIZES.map((s) => <option key={s} value={s}>{SIZE_OPTION[s]}</option>)}
            </select>
          </LabeledField>
          <button type="submit" className="btn pri" style={{ whiteSpace: "nowrap" }}>{IC.plus} เพิ่ม</button>
        </form>

        {/* ── 1-3-5 budget + warning (today) ── */}
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: warnings.length ? 8 : 14 }}>
          {SIZES.map((s) => {
            const over = used[s] > LIMIT[s];
            return (
              <span key={s} className={"pill " + (over ? "danger" : "neutral")} title={SIZE_OPTION[s]}>
                <i />{SIZE_SHORT[s]} {used[s]}/{LIMIT[s]}
              </span>
            );
          })}
        </div>
        {warnings.length > 0 &&
          <div style={{ display: "flex", alignItems: "flex-start", gap: 9, padding: "11px 13px", marginBottom: 14, borderRadius: 11, background: "var(--danger-soft, #fdecec)", border: "1px solid var(--danger)", color: "var(--danger)" }}>
            <span style={{ flexShrink: 0, marginTop: 1 }}>{IC.warn}</span>
            <div style={{ fontSize: 13, fontWeight: 600, lineHeight: 1.55 }}>
              {warnings.map((w, i) => <div key={i}>คำเตือน: {w}</div>)}
              <div style={{ fontWeight: 400, opacity: 0.85, marginTop: 2 }}>ยังบันทึกได้ตามปกติ — แต่ลองโฟกัสให้น้อยลงเพื่อทำได้จริง</div>
            </div>
          </div>}

        {/* ── today, grouped by matrix ── */}
        {loading ?
          <div className="page-sub" style={{ padding: "24px 4px" }}>กำลังโหลด…</div>
        : (todayTasks.length === 0 && otherTasks.length === 0 && overdue.length === 0) ?
          <window.EmptyState icon="check" title="ยังไม่มีรายการ" sub="เพิ่มสิ่งที่ต้องทำของวันนี้ด้านบน" />
        : <>
          <div style={{ fontSize: 12.5, fontWeight: 700, letterSpacing: ".04em", color: "var(--faint)", textTransform: "uppercase", margin: "4px 0 10px" }}>วันนี้ · {fmtDate(today)}</div>
          {todayTasks.length === 0 && <div className="page-sub" style={{ padding: "4px 2px 14px" }}>ยังไม่มีงานสำหรับวันนี้</div>}
          {todayGroups.map(({ g, items }) => items.length === 0 ? null : (
            <div key={g} style={{ marginBottom: 16 }}>
              <div style={{ marginBottom: 8, display: "flex", alignItems: "baseline", gap: 8, flexWrap: "wrap" }}>
                <span style={{ fontWeight: 700, fontSize: 13.5, color: "var(--ink)" }}>{GROUP_LABEL[g]}</span>
                <span style={{ fontSize: 12, color: "var(--faint)" }}>{items.length} งาน</span>
              </div>
              <div style={{ display: "grid", gap: 10 }}>{items.map(TaskCard)}</div>
            </div>
          ))}
          {ungrouped.length > 0 &&
            <div style={{ marginBottom: 16 }}>
              <div style={{ marginBottom: 8, fontWeight: 700, fontSize: 13.5, color: "var(--ink)" }}>ไม่ระบุกลุ่ม</div>
              <div style={{ display: "grid", gap: 10 }}>{ungrouped.map(TaskCard)}</div>
            </div>}

          {/* ── other days / rescheduled ── */}
          {otherTasks.length > 0 && <>
            <div style={{ fontSize: 12.5, fontWeight: 700, letterSpacing: ".04em", color: "var(--faint)", textTransform: "uppercase", margin: "10px 0 10px" }}>รายการอื่น ๆ</div>
            <div style={{ display: "grid", gap: 10 }}>{otherTasks.map(TaskCard)}</div>
          </>}
        </>}

        {editing && <EditModal edit={editing} onClose={() => setEditing(null)} onSave={editTodo} />}
      </div>
    );
  };
})();
