// coach.jsx — Personal accountability coach
//
// A focused, private "command center" that lives as a third tab inside
// the existing Personal page (alongside To-do and Notes). Everything is
// stored locally on this device via the Store abstraction below.
//
// Sections (bottom tab nav):
//   Today    — habit checklist, plan + review, mentor banner, streak
//   Routine  — CRUD on daily habits (reset every day)
//   Goals    — CRUD on goals (referenced by the mentor engine)
//   Tasks    — CRUD on a persistent to-do list (does NOT reset)
//   Record   — streak / best / 30-day rate / heatmap / last 5 reviews
//   Settings — coach name, strictness, reminder, export, reset today
//
// Storage is namespaced under fb.coach.* so it doesn't collide with
// anything else in the app. All reads/writes go through the Store
// helper so it can be swapped to a server-backed store later by
// editing one object.

(function () {
  const R = window.React;

  // ── Store ───────────────────────────────────────────────────────
  // Tiny abstraction over localStorage. Every value is JSON-encoded.
  // Replace the body of get/set/list to migrate to a server store
  // later without touching the rest of the file.
  const NS = "fb.coach.";
  const Store = {
    get(key, fallback) {
      try {
        const raw = window.localStorage.getItem(NS + key);
        if (raw == null) return fallback;
        return JSON.parse(raw);
      } catch { return fallback; }
    },
    set(key, value) {
      try { window.localStorage.setItem(NS + key, JSON.stringify(value)); } catch {}
    },
    remove(key) {
      try { window.localStorage.removeItem(NS + key); } catch {}
    },
    list(prefix) {
      const out = [];
      try {
        for (let i = 0; i < window.localStorage.length; i++) {
          const k = window.localStorage.key(i);
          if (k && k.startsWith(NS + prefix)) out.push(k.slice(NS.length));
        }
      } catch {}
      return out;
    },
  };

  // ── Date utilities ──────────────────────────────────────────────
  function ymd(d) {
    const y = d.getFullYear(), m = String(d.getMonth() + 1).padStart(2, "0"), da = String(d.getDate()).padStart(2, "0");
    return y + "-" + m + "-" + da;
  }
  function today() { return ymd(new Date()); }
  function shiftDays(iso, n) {
    const [y, m, d] = iso.split("-").map(Number);
    const dt = new Date(y, m - 1, d); dt.setDate(dt.getDate() + n);
    return ymd(dt);
  }
  function rangeDays(n) {
    const out = []; const t = new Date();
    for (let i = n - 1; i >= 0; i--) {
      const d = new Date(t); d.setDate(t.getDate() - i);
      out.push(ymd(d));
    }
    return out;
  }
  function uid() { return Math.random().toString(36).slice(2, 9); }

  // ── Defaults ────────────────────────────────────────────────────
  const DEFAULT_CONFIG = {
    goals: [],
    habits: [],
    mentorName: "Coach",
    strict: 1,            // 0 supportive, 1 firm, 2 drill sergeant
    reminder: "",         // "HH:MM" or empty
  };

  function loadConfig() {
    const c = Store.get("config", null);
    if (!c) return { ...DEFAULT_CONFIG };
    // Merge so newly added defaults appear after upgrades.
    return { ...DEFAULT_CONFIG, ...c, habits: c.habits || [], goals: c.goals || [] };
  }
  function loadDaily(date) {
    return Store.get("daily:" + date, { date, plan: "", review: "", done: {} });
  }
  function loadTasks() {
    return Store.get("tasks", []);
  }

  // ── Hooks ───────────────────────────────────────────────────────
  function useConfig() {
    const [c, setC] = R.useState(loadConfig);
    const save = R.useCallback((next) => {
      const v = typeof next === "function" ? next(c) : next;
      setC(v); Store.set("config", v);
    }, [c]);
    return [c, save];
  }
  function useDaily(date) {
    const [d, setD] = R.useState(() => loadDaily(date));
    // Re-read whenever the date prop changes (e.g. tab switch or
    // crossing midnight). Cheap because it's just localStorage.
    R.useEffect(() => { setD(loadDaily(date)); }, [date]);
    const save = R.useCallback((next) => {
      const v = typeof next === "function" ? next(d) : next;
      setD(v); Store.set("daily:" + date, v);
    }, [d, date]);
    return [d, save];
  }
  function useTasks() {
    const [t, setT] = R.useState(loadTasks);
    const save = R.useCallback((next) => {
      const v = typeof next === "function" ? next(t) : next;
      setT(v); Store.set("tasks", v);
    }, [t]);
    return [t, save];
  }

  // ── Streak logic ────────────────────────────────────────────────
  // A past day "counts" only if every configured habit was marked done
  // that day. Today being incomplete does NOT break the streak — only
  // *past* days do.
  function dayFullyDone(habits, daily) {
    if (!habits.length) return false;
    return habits.every(h => !!(daily && daily.done && daily.done[h.id]));
  }
  function habitDoneOn(habitId, daily) {
    return !!(daily && daily.done && daily.done[habitId]);
  }
  // Walk back from `from` (default = today). Skip today if not yet
  // fully done. Stop at the first past day that wasn't fully done.
  function currentStreakAll(habits, fromIso) {
    fromIso = fromIso || today();
    let cursor = fromIso;
    let count = 0;
    // If today is fully done, count it; otherwise skip it without breaking.
    const todayDaily = loadDaily(cursor);
    if (dayFullyDone(habits, todayDaily)) count++;
    cursor = shiftDays(cursor, -1);
    while (true) {
      const d = loadDaily(cursor);
      if (dayFullyDone(habits, d)) { count++; cursor = shiftDays(cursor, -1); }
      else break;
      if (count > 5000) break; // safety
    }
    return count;
  }
  function currentStreakHabit(habit, fromIso) {
    fromIso = fromIso || today();
    let cursor = fromIso;
    let count = 0;
    if (habitDoneOn(habit.id, loadDaily(cursor))) count++;
    cursor = shiftDays(cursor, -1);
    while (true) {
      if (habitDoneOn(habit.id, loadDaily(cursor))) { count++; cursor = shiftDays(cursor, -1); }
      else break;
      if (count > 5000) break;
    }
    return count;
  }
  // Best streak — longest run of fully-complete consecutive days anywhere
  // in our recorded history. We scan whatever daily:* keys exist.
  function bestStreakAll(habits) {
    const keys = Store.list("daily:").sort(); // ascending by ISO date
    if (!keys.length || !habits.length) return 0;
    let best = 0, run = 0, prev = null;
    for (const k of keys) {
      const iso = k.slice("daily:".length);
      const d = Store.get(k, null);
      const full = dayFullyDone(habits, d);
      if (!full) { run = 0; prev = iso; continue; }
      if (prev && shiftDays(prev, 1) === iso) run++; else run = 1;
      if (run > best) best = run;
      prev = iso;
    }
    return best;
  }
  function completionRate(habits, days) {
    if (!habits.length) return 0;
    const range = rangeDays(days);
    let total = 0, done = 0;
    for (const iso of range) {
      const d = loadDaily(iso);
      for (const h of habits) {
        total++;
        if (habitDoneOn(h.id, d)) done++;
      }
    }
    return total ? Math.round((done / total) * 100) : 0;
  }
  // % of habits done on a given day — drives heatmap intensity.
  function dayCompletion(habits, daily) {
    if (!habits.length) return 0;
    let done = 0;
    for (const h of habits) if (habitDoneOn(h.id, daily)) done++;
    return done / habits.length;
  }

  // ── Mentor engine ───────────────────────────────────────────────
  // Returns { tag, tone, message }.
  //   tag:  one of the state names — useful for testing.
  //   tone: "neutral" | "success" | "danger"
  //   message: the line shown in the banner.
  //
  // Variants[strict]: 0 = supportive, 1 = firm, 2 = drill sergeant.
  function pick(arr, strict) {
    const i = Math.max(0, Math.min(2, strict | 0));
    return arr[i];
  }
  function randomGoalSuffix(goals) {
    if (!goals || !goals.length) return "";
    const g = goals[Math.floor(Math.random() * goals.length)];
    return " Remember why: " + g + ".";
  }
  function mentor(pct, hour, strict, goals, hasRoutine) {
    if (!hasRoutine) {
      return {
        tag: "no-routine",
        tone: "neutral",
        message: pick([
          "Set up a routine to start — small, daily, repeatable.",
          "No routine yet. Define the non-negotiables. Pick three.",
          "No routine, no progress. Set it up. Now.",
        ], strict),
      };
    }
    if (hour >= 20 && pct < 100) {
      return {
        tag: "evening-incomplete",
        tone: "danger",
        message: pick([
          "Day's almost out — close the loop on what you can.",
          "Evening, still incomplete. Don't go to sleep on this.",
          "Day is ending. You haven't earned tomorrow. Move.",
        ], strict),
      };
    }
    if (pct === 100) {
      return {
        tag: "done-all",
        tone: "success",
        message: pick([
          "All done today. Stack the win, get to bed.",
          "Complete. Now don't ruin the trend tomorrow.",
          "Routine cleared. Don't get comfortable. Tomorrow resets.",
        ], strict),
      };
    }
    if (pct === 0) {
      return {
        tag: "zero-pct",
        tone: "danger",
        message: pick([
          "Haven't started. One small action — pick the easiest, go.",
          "Zero. You don't drift into your goals. Start now.",
          "Nothing done. You're not tired — you're avoiding. Move.",
        ], strict),
      };
    }
    // partial
    const base = pick([
      "Good start — keep stacking.",
      "Halfway is nothing. Finish the list.",
      "Don't celebrate halfway. Close it.",
    ], strict);
    const suffix = Math.random() < 0.5 ? randomGoalSuffix(goals) : "";
    return { tag: "partial", tone: "neutral", message: base + suffix };
  }

  // ── Backlog mentor (Tasks view) ─────────────────────────────────
  // Escalates when the open list grows.
  function backlogLine(openCount, strict) {
    if (openCount === 0) return { tone: "success", msg: "Inbox zero. Don't refill it for the sake of it." };
    if (openCount < 6)   return { tone: "neutral", msg: openCount + " open. Manageable. Close them today." };
    return {
      tone: "danger",
      msg: pick([
        openCount + " open — that's a pileup. Triage now.",
        openCount + " open. Stop adding. Start closing.",
        openCount + " open. This is a discipline problem, not a time problem.",
      ], strict),
    };
  }

  // ── Reminders ───────────────────────────────────────────────────
  // NOTE: web builds can only show a notification while the page is
  // open (Notification API + setTimeout). A true mobile build should
  // use native local notifications / push scheduling — replace this
  // hook with a thin wrapper around the native API on that platform.
  function useReminder(timeStr) {
    R.useEffect(() => {
      if (!timeStr || !/^\d{2}:\d{2}$/.test(timeStr)) return;
      if (!("Notification" in window)) return;
      // Don't badger for permission — the Settings save handler does that.
      const [hh, mm] = timeStr.split(":").map(Number);
      const now = new Date();
      const fire = new Date(now);
      fire.setHours(hh, mm, 0, 0);
      if (fire <= now) fire.setDate(fire.getDate() + 1); // tomorrow
      const ms = fire - now;
      const t = setTimeout(() => {
        try {
          if (Notification.permission === "granted") {
            new Notification("Coach", { body: "Time to check in. Open the routine." });
          }
        } catch {}
      }, ms);
      return () => clearTimeout(t);
    }, [timeStr]);
  }

  // ── Shared atoms ────────────────────────────────────────────────
  function Bar({ pct }) {
    return (
      <div className="coach-bar">
        <div className="coach-bar-fill" style={{ width: Math.min(100, Math.max(0, pct)) + "%" }}/>
      </div>
    );
  }
  function Banner({ data }) {
    if (!data) return null;
    return (
      <div className={"coach-banner coach-banner-" + data.tone}>
        <div className="coach-banner-icon" aria-hidden="true">
          {data.tone === "success" ? "✓" : data.tone === "danger" ? "!" : "·"}
        </div>
        <div className="coach-banner-msg">{data.message}</div>
      </div>
    );
  }
  function H({ children }) { return <h2 className="coach-h">{children}</h2>; }
  function Empty({ children }) { return <div className="coach-empty">{children}</div>; }

  // ── Today view ──────────────────────────────────────────────────
  function TodayView({ config, daily, saveDaily, streak }) {
    const habits = config.habits || [];
    const doneCount = habits.filter(h => habitDoneOn(h.id, daily)).length;
    const pct = habits.length ? Math.round((doneCount / habits.length) * 100) : 0;
    const hour = new Date().getHours();
    const banner = mentor(pct, hour, config.strict, config.goals, habits.length > 0);

    function toggleHabit(id) {
      saveDaily(prev => ({ ...prev, done: { ...(prev.done || {}), [id]: !prev.done?.[id] } }));
    }
    function setPlan(v)   { saveDaily(prev => ({ ...prev, plan: v })); }
    function setReview(v) { saveDaily(prev => ({ ...prev, review: v })); }

    return (
      <div className="coach-view">
        <div className="coach-headstrip">
          <div className="coach-streak">
            <div className="coach-streak-n">{streak}</div>
            <div className="coach-streak-k">DAY STREAK</div>
          </div>
          <div className="coach-progress">
            <div className="coach-progress-top">
              <span className="coach-progress-pct">{pct}%</span>
              <span className="coach-progress-x">{doneCount} / {habits.length || 0} done</span>
            </div>
            <Bar pct={pct}/>
          </div>
        </div>

        <Banner data={banner}/>

        <H>Routine</H>
        {!habits.length ? (
          <Empty>No habits yet. Add them in <b>Routine</b>.</Empty>
        ) : (
          <ul className="coach-checks">
            {habits.map(h => {
              const on = habitDoneOn(h.id, daily);
              return (
                <li key={h.id}
                    className={"coach-check" + (on ? " is-on" : "")}
                    onClick={() => toggleHabit(h.id)}>
                  <span className="coach-check-box">{on ? "✓" : ""}</span>
                  <span className="coach-check-txt">{h.name}</span>
                </li>
              );
            })}
          </ul>
        )}

        <H>Morning Plan</H>
        <textarea className="coach-area" rows={4}
                  placeholder="What's the one thing that matters today?"
                  value={daily.plan || ""}
                  onChange={e => setPlan(e.target.value)}/>

        <H>Evening Review</H>
        <textarea className="coach-area" rows={4}
                  placeholder="What worked. What didn't. What changes tomorrow."
                  value={daily.review || ""}
                  onChange={e => setReview(e.target.value)}/>
      </div>
    );
  }

  // ── Routine view (habits CRUD) ──────────────────────────────────
  function RoutineView({ config, saveConfig }) {
    const [txt, setTxt] = R.useState("");
    function add() {
      const name = txt.trim();
      if (!name) return;
      saveConfig(c => ({ ...c, habits: [...(c.habits || []), { id: uid(), name }] }));
      setTxt("");
    }
    function remove(id) {
      saveConfig(c => ({ ...c, habits: (c.habits || []).filter(h => h.id !== id) }));
    }
    function rename(id, name) {
      saveConfig(c => ({ ...c, habits: (c.habits || []).map(h => h.id === id ? { ...h, name } : h) }));
    }
    return (
      <div className="coach-view">
        <H>Routine</H>
        <div className="coach-subhead">Daily non-negotiables. They reset every day.</div>
        <div className="coach-addrow">
          <input className="coach-input" placeholder="New habit (e.g. read 20 pages)"
                 value={txt} onChange={e => setTxt(e.target.value)}
                 onKeyDown={e => e.key === "Enter" && add()}/>
          <button className="coach-btn coach-btn-primary" onClick={add}>Add</button>
        </div>
        {!(config.habits || []).length
          ? <Empty>No habits. Add the first one above.</Empty>
          : (
            <ul className="coach-list">
              {config.habits.map(h => {
                const s = currentStreakHabit(h);
                return (
                  <li className="coach-row" key={h.id}>
                    <input className="coach-input coach-input-flat"
                           value={h.name}
                           onChange={e => rename(h.id, e.target.value)}/>
                    <span className="coach-row-meta">{s}d</span>
                    <button className="coach-x" onClick={() => remove(h.id)} title="Delete habit">×</button>
                  </li>
                );
              })}
            </ul>
          )}
      </div>
    );
  }

  // ── Goals view ──────────────────────────────────────────────────
  function GoalsView({ config, saveConfig }) {
    const [txt, setTxt] = R.useState("");
    function add() {
      const name = txt.trim();
      if (!name) return;
      saveConfig(c => ({ ...c, goals: [...(c.goals || []), name] }));
      setTxt("");
    }
    function remove(i) {
      saveConfig(c => ({ ...c, goals: (c.goals || []).filter((_, idx) => idx !== i) }));
    }
    function edit(i, v) {
      saveConfig(c => ({ ...c, goals: (c.goals || []).map((g, idx) => idx === i ? v : g) }));
    }
    return (
      <div className="coach-view">
        <H>Goals</H>
        <div className="coach-subhead">The reasons behind the routine. The coach will surface these.</div>
        <div className="coach-addrow">
          <input className="coach-input" placeholder="New goal (e.g. ship v1 by August)"
                 value={txt} onChange={e => setTxt(e.target.value)}
                 onKeyDown={e => e.key === "Enter" && add()}/>
          <button className="coach-btn coach-btn-primary" onClick={add}>Add</button>
        </div>
        {!(config.goals || []).length
          ? <Empty>No goals yet. Define the destination.</Empty>
          : (
            <ul className="coach-list">
              {config.goals.map((g, i) => (
                <li className="coach-row" key={i}>
                  <input className="coach-input coach-input-flat"
                         value={g}
                         onChange={e => edit(i, e.target.value)}/>
                  <button className="coach-x" onClick={() => remove(i)} title="Delete goal">×</button>
                </li>
              ))}
            </ul>
          )}
      </div>
    );
  }

  // ── Tasks view (persistent, NOT reset daily) ────────────────────
  function TasksView({ tasks, saveTasks, strict }) {
    const [txt, setTxt] = R.useState("");
    const open = tasks.filter(t => !t.done);
    const done = tasks.filter(t => t.done);
    const back = backlogLine(open.length, strict);

    function add() {
      const text = txt.trim();
      if (!text) return;
      saveTasks(arr => [{ id: uid(), text, done: false, created: Date.now() }, ...arr]);
      setTxt("");
    }
    function toggle(id) {
      saveTasks(arr => arr.map(t => t.id === id ? { ...t, done: !t.done } : t));
    }
    function remove(id) {
      saveTasks(arr => arr.filter(t => t.id !== id));
    }
    function clearDone() {
      saveTasks(arr => arr.filter(t => !t.done));
    }

    return (
      <div className="coach-view">
        <H>Tasks</H>
        <Banner data={{ tone: back.tone, message: back.msg }}/>
        <div className="coach-addrow">
          <input className="coach-input" placeholder="What needs to get done?"
                 value={txt} onChange={e => setTxt(e.target.value)}
                 onKeyDown={e => e.key === "Enter" && add()}/>
          <button className="coach-btn coach-btn-primary" onClick={add}>Add</button>
        </div>
        {!open.length && !done.length
          ? <Empty>No tasks. Don't pretend everything is fine — name them.</Empty>
          : null}
        {open.length > 0 && (
          <ul className="coach-list">
            {open.map(t => (
              <li className="coach-row coach-task" key={t.id}>
                <span className="coach-check-box" onClick={() => toggle(t.id)}>{t.done ? "✓" : ""}</span>
                <span className="coach-task-txt" onClick={() => toggle(t.id)}>{t.text}</span>
                <button className="coach-x" onClick={() => remove(t.id)} title="Delete task">×</button>
              </li>
            ))}
          </ul>
        )}
        {done.length > 0 && (
          <>
            <div className="coach-section-head">
              <span>Completed · {done.length}</span>
              <button className="coach-link" onClick={clearDone}>Clear</button>
            </div>
            <ul className="coach-list">
              {done.map(t => (
                <li className="coach-row coach-task coach-task-done" key={t.id}>
                  <span className="coach-check-box is-on" onClick={() => toggle(t.id)}>✓</span>
                  <span className="coach-task-txt" onClick={() => toggle(t.id)}>{t.text}</span>
                  <button className="coach-x" onClick={() => remove(t.id)} title="Delete task">×</button>
                </li>
              ))}
            </ul>
          </>
        )}
      </div>
    );
  }

  // ── Record view ─────────────────────────────────────────────────
  function RecordView({ config }) {
    const habits = config.habits || [];
    const cur  = currentStreakAll(habits);
    const best = bestStreakAll(habits);
    const rate = completionRate(habits, 30);
    const range = rangeDays(30);
    // Last 5 reviews — walk back from today over all daily:* keys.
    const reviews = [];
    const all = Store.list("daily:").sort().reverse();
    for (const k of all) {
      const iso = k.slice("daily:".length);
      const d = Store.get(k, null);
      if (d && d.review && d.review.trim()) reviews.push({ iso, review: d.review.trim() });
      if (reviews.length >= 5) break;
    }
    function intensityClass(p) {
      if (p <= 0)   return "lvl-0";
      if (p < .34)  return "lvl-1";
      if (p < .67)  return "lvl-2";
      if (p < 1)    return "lvl-3";
      return "lvl-4";
    }
    return (
      <div className="coach-view">
        <H>Record</H>
        <div className="coach-rec-grid">
          <div className="coach-rec-tile">
            <div className="coach-rec-n">{cur}</div>
            <div className="coach-rec-k">CURRENT STREAK</div>
          </div>
          <div className="coach-rec-tile">
            <div className="coach-rec-n">{best}</div>
            <div className="coach-rec-k">BEST EVER</div>
          </div>
          <div className="coach-rec-tile">
            <div className="coach-rec-n">{rate}%</div>
            <div className="coach-rec-k">30-DAY RATE</div>
          </div>
        </div>

        <div className="coach-subhead">Last 30 days · darker = more done</div>
        <div className="coach-heat">
          {range.map(iso => {
            const d = loadDaily(iso);
            const p = dayCompletion(habits, d);
            return <span key={iso} className={"coach-heat-cell " + intensityClass(p)} title={iso + " · " + Math.round(p * 100) + "%"}/>;
          })}
        </div>

        <H>Recent reviews</H>
        {!reviews.length
          ? <Empty>No reviews written yet.</Empty>
          : (
            <ul className="coach-reviews">
              {reviews.map(r => (
                <li key={r.iso}>
                  <div className="coach-rev-date">{r.iso}</div>
                  <div className="coach-rev-body">{r.review}</div>
                </li>
              ))}
            </ul>
          )}
      </div>
    );
  }

  // ── Settings view ───────────────────────────────────────────────
  function SettingsView({ config, saveConfig, onResetToday }) {
    const [name, setName]   = R.useState(config.mentorName || "");
    const [strict, setS]    = R.useState(config.strict ?? 1);
    const [rem, setRem]     = R.useState(config.reminder || "");
    const [savedAt, setSA]  = R.useState(0);

    function save() {
      saveConfig(c => ({ ...c, mentorName: name.trim() || "Coach", strict, reminder: rem }));
      // If a reminder is set, ask for notification permission now.
      if (rem && "Notification" in window && Notification.permission === "default") {
        try { Notification.requestPermission(); } catch {}
      }
      setSA(Date.now());
    }

    function exportJSON() {
      const data = {
        config: loadConfig(),
        tasks: loadTasks(),
        days: {},
        exportedAt: new Date().toISOString(),
      };
      for (const k of Store.list("daily:")) {
        const iso = k.slice("daily:".length);
        data.days[iso] = Store.get(k, null);
      }
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      const date = today().replace(/-/g, "");
      a.href = url; a.download = "coach-backup-" + date + ".json";
      document.body.appendChild(a); a.click();
      setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 0);
    }

    return (
      <div className="coach-view">
        <H>Settings</H>

        <label className="coach-field">
          <span className="coach-field-k">Coach name</span>
          <input className="coach-input" value={name} onChange={e => setName(e.target.value)}/>
        </label>

        <div className="coach-field">
          <span className="coach-field-k">Strictness</span>
          <div className="coach-strict">
            {["Supportive", "Firm", "Drill Sergeant"].map((lbl, i) => (
              <button key={i}
                      className={"coach-strict-opt" + (strict === i ? " is-on" : "")}
                      onClick={() => setS(i)}>{lbl}</button>
            ))}
          </div>
        </div>

        <label className="coach-field">
          <span className="coach-field-k">Daily reminder</span>
          <input className="coach-input" type="time" value={rem}
                 onChange={e => setRem(e.target.value)}/>
          <span className="coach-field-help">
            Web builds can only notify while this page is open. For real
            push, a native mobile build is required.
          </span>
        </label>

        <div className="coach-btnrow">
          <button className="coach-btn coach-btn-primary" onClick={save}>Save</button>
          {savedAt > 0 && <span className="coach-saved">Saved</span>}
        </div>

        <H>Backup</H>
        <button className="coach-btn" onClick={exportJSON}>Export JSON</button>

        <H>Danger zone</H>
        <button className="coach-btn coach-btn-danger" onClick={() => {
          if (window.confirm("Reset today's progress? Plan + review + habit ticks for today will be cleared.")) {
            onResetToday();
          }
        }}>Reset today</button>
      </div>
    );
  }

  // ── Shell ───────────────────────────────────────────────────────
  function CoachShell() {
    const [tab, setTab] = R.useState(() => {
      try { return window.localStorage.getItem("fb.coach.tab") || "today"; } catch { return "today"; }
    });
    R.useEffect(() => {
      try { window.localStorage.setItem("fb.coach.tab", tab); } catch {}
    }, [tab]);

    const [config, saveConfig] = useConfig();
    const dateIso = today();
    const [daily, saveDaily]   = useDaily(dateIso);
    const [tasks, saveTasks]   = useTasks();

    useReminder(config.reminder);

    const streak = currentStreakAll(config.habits || []);

    function resetToday() {
      const blank = { date: dateIso, plan: "", review: "", done: {} };
      Store.set("daily:" + dateIso, blank);
      saveDaily(blank);
    }

    const TABS = [
      { key: "today",    label: "Today",    glyph: "◎" },
      { key: "routine",  label: "Routine",  glyph: "≡" },
      { key: "goals",    label: "Goals",    glyph: "◇" },
      { key: "tasks",    label: "Tasks",    glyph: "□" },
      { key: "record",   label: "Record",   glyph: "▤" },
      { key: "settings", label: "Settings", glyph: "⚙" },
    ];

    return (
      <div className="coach-root">
        <div className="coach-topbar">
          <div className="coach-brand">{config.mentorName || "Coach"} <span className="coach-brand-dot">•</span> <span className="coach-brand-sub">accountability</span></div>
          <div className="coach-date">{dateIso}</div>
        </div>

        <div className={"coach-body coach-body-" + tab}>
          {tab === "today"    && <TodayView config={config} daily={daily} saveDaily={saveDaily} streak={streak}/>}
          {tab === "routine"  && <RoutineView config={config} saveConfig={saveConfig}/>}
          {tab === "goals"    && <GoalsView config={config} saveConfig={saveConfig}/>}
          {tab === "tasks"    && <TasksView tasks={tasks} saveTasks={saveTasks} strict={config.strict}/>}
          {tab === "record"   && <RecordView config={config}/>}
          {tab === "settings" && <SettingsView config={config} saveConfig={saveConfig} onResetToday={resetToday}/>}
        </div>

        <div className="coach-tabs">
          {TABS.map(t => (
            <button key={t.key}
                    className={"coach-tab" + (tab === t.key ? " is-on" : "")}
                    onClick={() => setTab(t.key)}>
              <span className="coach-tab-glyph">{t.glyph}</span>
              <span className="coach-tab-lbl">{t.label}</span>
            </button>
          ))}
        </div>
      </div>
    );
  }

  // ── CSS — dark "command center" scoped to .coach-root ───────────
  const CSS = `
  .coach-root {
    --coach-bg: #0a0d14;
    --coach-panel: #11151f;
    --coach-panel-2: #161b27;
    --coach-line: #1f2535;
    --coach-ink: #e6ecf5;
    --coach-ink-mute: #8b93a8;
    --coach-ink-faint: #5a6178;
    --coach-accent: #ffb547;
    --coach-success: #22c08a;
    --coach-danger: #ff5d5d;
    background: var(--coach-bg);
    color: var(--coach-ink);
    border-radius: 14px;
    overflow: hidden;
    font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, Roboto, sans-serif;
    display: flex; flex-direction: column;
    min-height: 600px;
    position: relative;
    border: 1px solid var(--coach-line);
  }
  .coach-root * { box-sizing: border-box; }
  .coach-topbar {
    display:flex; justify-content:space-between; align-items:center;
    padding: 14px 18px;
    border-bottom: 1px solid var(--coach-line);
    background: linear-gradient(180deg, #0e131d 0%, #0a0d14 100%);
  }
  .coach-brand {
    font-family: "Inter Tight", "Inter", system-ui, sans-serif;
    font-weight: 800; letter-spacing: -0.01em;
    text-transform: uppercase; font-size: 14px;
  }
  .coach-brand-dot { color: var(--coach-accent); margin: 0 6px; }
  .coach-brand-sub { color: var(--coach-ink-mute); font-weight: 600; letter-spacing: .04em; }
  .coach-date {
    font-variant-numeric: tabular-nums;
    font-size: 12px; color: var(--coach-ink-mute);
    font-family: ui-monospace, Menlo, monospace;
  }

  .coach-body { padding: 18px 18px 90px; overflow:auto; flex: 1; }
  .coach-view { display: flex; flex-direction: column; gap: 14px; animation: coach-in .25s ease both; }
  @keyframes coach-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

  .coach-h {
    font-family: "Inter Tight", "Inter", system-ui, sans-serif;
    font-weight: 800; font-size: 13px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--coach-ink-mute); margin: 4px 0 6px;
  }
  .coach-subhead { color: var(--coach-ink-mute); font-size: 12.5px; margin-top:-6px; }
  .coach-empty { color: var(--coach-ink-faint); font-size: 13px; padding: 16px; border:1px dashed var(--coach-line); border-radius: 10px; }
  .coach-section-head { display:flex; justify-content:space-between; align-items:baseline; color: var(--coach-ink-mute); font-size: 12px; text-transform: uppercase; letter-spacing: .08em; margin-top: 8px; }
  .coach-link { background: none; border: none; color: var(--coach-accent); font-size: 12px; cursor: pointer; padding: 0; font-weight: 700; }

  /* Header strip (Today) — streak + progress */
  .coach-headstrip { display:grid; grid-template-columns: auto 1fr; gap:18px; align-items:center; padding: 14px 16px; background: var(--coach-panel); border:1px solid var(--coach-line); border-radius: 12px; }
  .coach-streak { text-align:left; }
  .coach-streak-n { font-family: "Inter Tight", "Inter", sans-serif; font-weight: 900; font-size: 44px; line-height: 1; color: var(--coach-accent); font-variant-numeric: tabular-nums; }
  .coach-streak-k { font-size: 10px; letter-spacing: .14em; color: var(--coach-ink-mute); margin-top: 4px; }
  .coach-progress-top { display:flex; justify-content:space-between; align-items:baseline; margin-bottom: 8px; font-size:12.5px; color: var(--coach-ink-mute); }
  .coach-progress-pct { font-family: "Inter Tight", "Inter", sans-serif; font-weight: 800; font-size: 22px; color: var(--coach-ink); }
  .coach-progress-x { font-variant-numeric: tabular-nums; }

  /* Progress bar */
  .coach-bar { background: #1a2030; height: 8px; border-radius: 99px; overflow: hidden; }
  .coach-bar-fill { height: 100%; background: linear-gradient(90deg, var(--coach-accent), #ffd58b); transition: width .3s ease; }

  /* Banner */
  .coach-banner { display:flex; gap:12px; align-items:center; padding: 12px 14px; border-radius: 10px; border:1px solid var(--coach-line); background: var(--coach-panel); }
  .coach-banner-icon { width: 26px; height: 26px; border-radius: 50%; display:flex; align-items:center; justify-content:center; font-weight: 800; font-size: 14px; flex-shrink:0; background:#1a2030; color: var(--coach-ink-mute); }
  .coach-banner-msg { font-size: 13.5px; line-height: 1.45; color: var(--coach-ink); }
  .coach-banner-success { border-color: rgba(34,192,138,.4); background: linear-gradient(180deg, rgba(34,192,138,.10), rgba(34,192,138,.04)); }
  .coach-banner-success .coach-banner-icon { background: rgba(34,192,138,.18); color: var(--coach-success); }
  .coach-banner-danger { border-color: rgba(255,93,93,.4); background: linear-gradient(180deg, rgba(255,93,93,.10), rgba(255,93,93,.04)); }
  .coach-banner-danger .coach-banner-icon { background: rgba(255,93,93,.18); color: var(--coach-danger); }

  /* Habit checks */
  .coach-checks { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:8px; }
  .coach-check { display:flex; align-items:center; gap:12px; padding: 12px 14px; border:1px solid var(--coach-line); border-radius: 10px; background: var(--coach-panel); cursor:pointer; transition: transform .08s ease, background .15s ease; user-select:none; }
  .coach-check:active { transform: scale(.99); }
  .coach-check:hover { background: var(--coach-panel-2); }
  .coach-check.is-on { background: linear-gradient(180deg, rgba(255,181,71,.08), rgba(255,181,71,.02)); border-color: rgba(255,181,71,.4); }
  .coach-check-box { width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--coach-ink-faint); display:flex; align-items:center; justify-content:center; font-weight: 800; font-size: 14px; color: var(--coach-bg); background: transparent; flex-shrink:0; }
  .coach-check.is-on .coach-check-box { background: var(--coach-accent); border-color: var(--coach-accent); }
  .coach-check-box.is-on { background: var(--coach-accent); border-color: var(--coach-accent); color: var(--coach-bg); }
  .coach-check-txt { font-size: 14.5px; }

  /* Textareas / inputs */
  .coach-area, .coach-input { width:100%; background: var(--coach-panel); color: var(--coach-ink); border: 1px solid var(--coach-line); border-radius: 10px; padding: 12px 14px; font-size: 14px; resize: vertical; font-family: inherit; }
  .coach-area:focus, .coach-input:focus { outline: none; border-color: var(--coach-accent); }
  .coach-input-flat { border-color: transparent; background: transparent; padding: 8px 10px; }

  /* Add row + lists */
  .coach-addrow { display:flex; gap:8px; }
  .coach-list { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:6px; }
  .coach-row { display:flex; align-items:center; gap:6px; padding: 6px 10px; border:1px solid var(--coach-line); border-radius: 10px; background: var(--coach-panel); }
  .coach-row-meta { color: var(--coach-ink-mute); font-size: 12px; font-variant-numeric: tabular-nums; padding: 0 6px; }
  .coach-x { background: transparent; border: 0; color: var(--coach-ink-faint); cursor: pointer; font-size: 18px; width: 28px; height: 28px; border-radius: 6px; }
  .coach-x:hover { color: var(--coach-danger); background: rgba(255,93,93,.08); }

  /* Buttons */
  .coach-btn { background: var(--coach-panel-2); border:1px solid var(--coach-line); color: var(--coach-ink); padding: 9px 14px; border-radius: 9px; font-size: 13px; font-weight: 600; cursor: pointer; }
  .coach-btn:hover { background: #1c2333; }
  .coach-btn-primary { background: var(--coach-accent); border-color: var(--coach-accent); color: #1a1305; }
  .coach-btn-primary:hover { background: #ffc265; border-color: #ffc265; }
  .coach-btn-danger { background: rgba(255,93,93,.12); border-color: rgba(255,93,93,.4); color: var(--coach-danger); }
  .coach-btn-danger:hover { background: rgba(255,93,93,.22); }
  .coach-btnrow { display:flex; gap:10px; align-items:center; }
  .coach-saved { color: var(--coach-success); font-size: 12px; font-weight: 700; }

  /* Tasks */
  .coach-task { padding: 10px 12px; }
  .coach-task-txt { flex:1; font-size: 14px; cursor: pointer; }
  .coach-task-done .coach-task-txt { text-decoration: line-through; color: var(--coach-ink-faint); }

  /* Record */
  .coach-rec-grid { display:grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
  .coach-rec-tile { padding: 14px; background: var(--coach-panel); border:1px solid var(--coach-line); border-radius: 12px; text-align:center; }
  .coach-rec-n { font-family: "Inter Tight", "Inter", sans-serif; font-weight: 900; font-size: 30px; color: var(--coach-accent); font-variant-numeric: tabular-nums; line-height: 1; }
  .coach-rec-k { font-size: 10px; letter-spacing: .14em; color: var(--coach-ink-mute); margin-top: 6px; }
  .coach-heat { display:grid; grid-template-columns: repeat(15, 1fr); gap: 4px; padding: 12px; background: var(--coach-panel); border:1px solid var(--coach-line); border-radius: 12px; }
  .coach-heat-cell { aspect-ratio: 1 / 1; border-radius: 3px; background: #1a2030; }
  .coach-heat-cell.lvl-0 { background: #1a2030; }
  .coach-heat-cell.lvl-1 { background: rgba(255,181,71,.20); }
  .coach-heat-cell.lvl-2 { background: rgba(255,181,71,.40); }
  .coach-heat-cell.lvl-3 { background: rgba(255,181,71,.65); }
  .coach-heat-cell.lvl-4 { background: var(--coach-accent); }
  .coach-reviews { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:8px; }
  .coach-reviews li { padding: 10px 12px; border:1px solid var(--coach-line); border-radius: 10px; background: var(--coach-panel); }
  .coach-rev-date { font-size: 11px; letter-spacing: .08em; color: var(--coach-ink-mute); text-transform: uppercase; margin-bottom: 4px; }
  .coach-rev-body { font-size: 13.5px; white-space: pre-wrap; }

  /* Settings */
  .coach-field { display:flex; flex-direction:column; gap:6px; }
  .coach-field-k { font-size: 11px; letter-spacing: .12em; color: var(--coach-ink-mute); text-transform: uppercase; font-weight: 700; }
  .coach-field-help { font-size: 11.5px; color: var(--coach-ink-faint); }
  .coach-strict { display:flex; gap:6px; }
  .coach-strict-opt { flex:1; padding: 10px 8px; background: var(--coach-panel); border:1px solid var(--coach-line); color: var(--coach-ink); border-radius: 8px; cursor:pointer; font-weight: 600; font-size: 13px; }
  .coach-strict-opt.is-on { background: var(--coach-accent); border-color: var(--coach-accent); color: #1a1305; }

  /* Bottom tab nav */
  .coach-tabs { position: sticky; bottom: 0; left: 0; right: 0; display:flex; background: rgba(10,13,20,.94); backdrop-filter: blur(8px); border-top: 1px solid var(--coach-line); padding: 6px 6px; gap: 4px; }
  .coach-tab { flex:1; display:flex; flex-direction:column; align-items:center; gap:2px; padding: 8px 4px; background: transparent; border: 0; color: var(--coach-ink-mute); cursor: pointer; border-radius: 8px; }
  .coach-tab:hover { color: var(--coach-ink); }
  .coach-tab.is-on { color: var(--coach-accent); background: rgba(255,181,71,.08); }
  .coach-tab-glyph { font-size: 18px; line-height: 1; }
  .coach-tab-lbl { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }

  @media (max-width: 520px) {
    .coach-rec-grid { grid-template-columns: 1fr 1fr; }
    .coach-heat { grid-template-columns: repeat(10, 1fr); }
    .coach-tab-lbl { display: none; }
  }
  `;
  (function injectCSS() {
    if (document.getElementById("coach-css")) return;
    const s = document.createElement("style"); s.id = "coach-css"; s.textContent = CSS;
    document.head.appendChild(s);
  })();

  // Expose to window so NotesView can mount it.
  Object.assign(window, { CoachView: CoachShell });
})();
