const { useState, useEffect, useRef, useMemo } = React;

// ---------- tiny hand-drawn icon set (no external icon package needed) ----------

function makeIcon(children) {
  return function IconComp({ size = 18, strokeWidth = 1.8 }) {
    return (
      <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
           strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round">
        {children}
      </svg>
    );
  };
}

const Check = makeIcon(<polyline points="20 6 9 17 4 12" />);
const XIcon = makeIcon(<><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></>);
const ChevronLeft = makeIcon(<polyline points="15 18 9 12 15 6" />);
const ChevronRight = makeIcon(<polyline points="9 18 15 12 9 6" />);
const Plus = makeIcon(<><line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" /></>);
const Trash2 = makeIcon(<><polyline points="3 6 5 6 21 6" /><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" /><path d="M10 11v6" /><path d="M14 11v6" /><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" /></>);
const Camera = makeIcon(<><path d="M4 8h3l1.5-2h7L17 8h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z" /><circle cx="12" cy="13" r="3.5" /></>);
const Moon = makeIcon(<path d="M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z" />);
const ScaleIcon = makeIcon(<><path d="M12 3v18" /><path d="M6 7l-3 6a3 3 0 0 0 6 0z" /><path d="M18 7l-3 6a3 3 0 0 0 6 0z" /><path d="M4 21h16" /><path d="M6 7h12" /></>);
const Flame = makeIcon(<path d="M12 2c1 3-2 4-2 7a3 3 0 1 0 6 0c0-1-.5-2-1-2 2 1 4 3 4 6a7 7 0 1 1-14 0c0-4 3-6 4-8 1-1 2-2 3-3z" />);
const Trophy = makeIcon(<><path d="M8 4h8v5a4 4 0 0 1-8 0V4z" /><path d="M8 5H5a3 3 0 0 0 3 4" /><path d="M16 5h3a3 3 0 0 1-3 4" /><path d="M10 13v3" /><path d="M14 13v3" /><path d="M8 20h8" /><path d="M9 20c0-2 1-3 3-3s3 1 3 3" /></>);
const Target = makeIcon(<><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="5" /><circle cx="12" cy="12" r="1" /></>);
const CalendarIcon = makeIcon(<><rect x="3" y="5" width="18" height="16" rx="2" /><line x1="3" y1="10" x2="21" y2="10" /><line x1="8" y1="3" x2="8" y2="7" /><line x1="16" y1="3" x2="16" y2="7" /></>);
const BookOpen = makeIcon(<><path d="M3 5c3-1 6-1 9 1v13c-3-2-6-2-9-1V5z" /><path d="M21 5c-3-1-6-1-9 1v13c3-2 6-2 9-1V5z" /></>);
const ClipboardList = makeIcon(<><rect x="5" y="4" width="14" height="17" rx="2" /><rect x="9" y="2" width="6" height="4" rx="1" /><line x1="8" y1="11" x2="16" y2="11" /><line x1="8" y1="15" x2="16" y2="15" /></>);
const Dumbbell = makeIcon(<><rect x="2" y="9" width="3" height="6" rx="1" /><rect x="19" y="9" width="3" height="6" rx="1" /><line x1="6" y1="12" x2="18" y2="12" /><rect x="5" y="7" width="2" height="10" rx="1" /><rect x="17" y="7" width="2" height="10" rx="1" /></>);
const TreePine = makeIcon(<><path d="M12 3l5 7h-3l4 6h-4l3 5H7l3-5H6l4-6H7z" /><line x1="12" y1="21" x2="12" y2="24" /></>);
const Code2 = makeIcon(<><polyline points="9 6 4 12 9 18" /><polyline points="15 6 20 12 15 18" /></>);
const Droplets = makeIcon(<path d="M12 3c3 4 6 7.5 6 11a6 6 0 0 1-12 0c0-3.5 3-7 6-11z" />);
const UtensilsCrossed = makeIcon(<><path d="M4 3l7 7" /><path d="M8 3l-5 5 1.5 1.5L10 4z" /><path d="M20 3l-9 9-3 8" /><path d="M14 12l7 7" /></>);

// ---------- constants ----------

const RULES = [
  { key: "indoor", label: "Indoor workout", icon: Dumbbell, type: "boolean" },
  { key: "outdoor", label: "Outdoor workout", icon: TreePine, type: "boolean" },
  { key: "leetcode", label: "LeetCode", icon: Code2, type: "boolean" },
  { key: "water", label: "Water", icon: Droplets, type: "boolean" },
  { key: "diet", label: "Diet", icon: UtensilsCrossed, type: "tristate" },
];

const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const MONTH_NAMES = ["January","February","March","April","May","June","July","August","September","October","November","December"];

const TABS = [
  { key: "dashboard", label: "Log book", icon: BookOpen },
  { key: "today", label: "Today's entry", icon: ClipboardList },
  { key: "calendar", label: "Calendar", icon: CalendarIcon },
  { key: "leetcode", label: "Code log", icon: Code2 },
  { key: "rules", label: "Rules", icon: Target },
];

function emptyLcEntry() {
  return { problem: "", difficulty: "", topic: "", result: "", solved: "", help: "", notes: "" };
}

function emptyDay() {
  return {
    indoor: false, outdoor: false, leetcode: false, water: false,
    diet: "", // '' | 'done' | 'failed'
    sleep: "", weight: "", notes: "",
    lc: [],
  };
}

function normalizeDay(raw) {
  if (!raw) return emptyDay();
  const d = { ...emptyDay(), ...raw };
  if (typeof raw.diet === "boolean") d.diet = raw.diet ? "done" : "";
  if (!Array.isArray(raw.lc)) d.lc = raw.lc && raw.lc.problem ? [raw.lc] : [];
  else d.lc = raw.lc;
  return d;
}

function ruleSatisfied(entry, rule) {
  if (!entry) return false;
  if (rule.type === "tristate") return entry[rule.key] === "done";
  return !!entry[rule.key];
}

// ---------- date helpers ----------

function dkey(d) {
  const y = d.getFullYear(), m = String(d.getMonth() + 1).padStart(2, "0"), day = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${day}`;
}
function parseKey(k) {
  const [y, m, d] = k.split("-").map(Number);
  return new Date(y, m - 1, d);
}
function addDays(d, n) {
  const nd = new Date(d);
  nd.setDate(nd.getDate() + n);
  return nd;
}
function fmtLong(d) {
  return d.toLocaleDateString(undefined, { weekday: "long", month: "long", day: "numeric", year: "numeric" });
}
function fmtShort(d) {
  return d.toLocaleDateString(undefined, { month: "short", day: "numeric" });
}
function todayKey() {
  const t = new Date();
  t.setHours(0, 0, 0, 0);
  return dkey(t);
}

// ---------- Supabase API ----------

async function apiLoad() {
  const r = await fetch("/api/data");
  if (!r.ok) throw new Error("Couldn't reach the local server");
  const parsed = await r.json();
  const days = {};
  Object.keys(parsed.days || {}).forEach((k) => { days[k] = normalizeDay(parsed.days[k]); });
  return { config: parsed.config || { startDate: todayKey(), target: 75 }, days };
}
async function apiSave(state) {
  const r = await fetch("/api/data", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(state) });
  if (!r.ok) throw new Error("Save failed");
}
async function apiUploadPhoto(dateKey, dataUrl) {
  const r = await fetch("/api/photos/" + encodeURIComponent(dateKey), {
    method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ dataUrl }),
  });
  if (!r.ok) throw new Error("Photo save failed");
}
async function apiDeletePhoto(dateKey) {
  await fetch("/api/photos/" + encodeURIComponent(dateKey), { method: "DELETE" });
}

// ---------- stats engine ----------

function computeAll(state) {
  const start = parseKey(state.config.startDate);
  start.setHours(0, 0, 0, 0);
  const tKey = todayKey();
  const statusMap = {};
  let d = new Date(start);
  let running = 0, best = 0, totalSuccess = 0, totalFailed = 0, totalTracked = 0;
  let dayNumMap = {};
  let n = 0;
  while (dkey(d) <= tKey) {
    const key = dkey(d);
    dayNumMap[key] = n;
    const entry = state.days[key];
    const allDone = !!entry && RULES.every((r) => ruleSatisfied(entry, r));
    totalTracked += 1;
    if (key === tKey) {
      if (allDone) { statusMap[key] = "success"; running += 1; totalSuccess += 1; best = Math.max(best, running); }
      else { statusMap[key] = "today"; }
    } else {
      if (allDone) { statusMap[key] = "success"; running += 1; totalSuccess += 1; best = Math.max(best, running); }
      else { statusMap[key] = "fail"; running = 0; totalFailed += 1; }
    }
    d = addDays(d, 1);
    n += 1;
  }
  return {
    statusMap, dayNumMap, currentStreak: running, bestStreak: best,
    totalSuccess, totalFailed, totalTracked, todayKey: tKey,
    target: state.config.target, startKey: dkey(start),
  };
}

function ruleCounts(state, statKeys) {
  const counts = {};
  RULES.forEach((r) => (counts[r.key] = 0));
  let n = 0;
  Object.keys(state.days).forEach((k) => {
    if (k > statKeys.todayKey) return;
    const entry = state.days[k];
    n += 1;
    RULES.forEach((r) => { if (ruleSatisfied(entry, r)) counts[r.key] += 1; });
  });
  return { counts, n: Math.max(n, 1) };
}

function monthSummary(state, stats, y, m) {
  const first = new Date(y, m, 1);
  const last = new Date(y, m + 1, 0);
  let success = 0, fail = 0, ahead = 0, tracked = 0;
  let d = new Date(first);
  while (d <= last) {
    const key = dkey(d);
    if (key >= stats.startKey) {
      const status = stats.statusMap[key] || "future";
      if (status === "success") { success++; tracked++; }
      else if (status === "fail") { fail++; tracked++; }
      else if (status === "future" || status === "today") { ahead++; }
    }
    d = addDays(d, 1);
  }
  const rate = tracked > 0 ? Math.round((success / tracked) * 100) : null;
  return { success, fail, ahead, rate };
}

// ---------- small UI atoms ----------

function Stamp({ status, size = 30, onClick, label }) {
  const cls = status === "success" ? "stamp stamp-success" : status === "fail" ? "stamp stamp-fail" : status === "today" ? "stamp stamp-today" : status === "future" ? "stamp stamp-future" : "stamp stamp-none";
  return (
    <button className={cls} style={{ width: size, height: size }} onClick={onClick} title={label} type="button">
      {status === "success" && <Check size={size * 0.55} strokeWidth={3} />}
      {status === "fail" && <XIcon size={size * 0.55} strokeWidth={3} />}
    </button>
  );
}

function RuleToggle({ rule, checked, onToggle }) {
  const Icon = rule.icon;
  return (
    <button type="button" onClick={onToggle} className={"rule-row" + (checked ? " rule-row-on" : "")}>
      <span className="rule-icon"><Icon size={20} strokeWidth={1.8} /></span>
      <span className="rule-label">{rule.label}</span>
      <span className={"rule-check" + (checked ? " rule-check-on" : "")}>
        {checked ? <Check size={15} strokeWidth={3} /> : null}
      </span>
    </button>
  );
}

function DietControl({ value, onChange }) {
  return (
    <div className={"rule-row diet-row" + (value === "done" ? " rule-row-on" : value === "failed" ? " rule-row-fail" : "")}>
      <span className="rule-icon"><UtensilsCrossed size={20} strokeWidth={1.8} /></span>
      <span className="rule-label">Diet</span>
      <div className="diet-btns">
        <button type="button" className={"diet-icon-btn diet-icon-check" + (value === "done" ? " diet-icon-on" : "")} onClick={() => onChange(value === "done" ? "" : "done")} title="On track">
          <Check size={16} strokeWidth={3} />
        </button>
        <button type="button" className={"diet-icon-btn diet-icon-x" + (value === "failed" ? " diet-icon-on" : "")} onClick={() => onChange(value === "failed" ? "" : "failed")} title="Failed today">
          <XIcon size={16} strokeWidth={3} />
        </button>
      </div>
    </div>
  );
}

function PhotoRuleRow({ dateKey, hasPhoto, onUploaded, onRemoved }) {
  const inputRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");

  async function handleFile(e) {
    const file = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!file) return;
    setErr(""); setBusy(true);
    try {
      const dataUrl = await compressImage(file);
      await apiUploadPhoto(dateKey, dataUrl);
      onUploaded();
    } catch (e2) {
      setErr("Couldn't save that photo. Try a smaller image.");
    } finally {
      setBusy(false);
    }
  }

  async function handleRemove(ev) {
    ev.stopPropagation();
    setBusy(true);
    try { await apiDeletePhoto(dateKey); } catch (e) { /* already gone */ }
    setBusy(false);
    onRemoved();
  }

  return (
    <>
      <div
        className={"rule-row" + (hasPhoto ? " rule-row-on" : "")}
        onClick={() => !busy && inputRef.current && inputRef.current.click()}
        role="button"
        tabIndex={0}
        onKeyDown={(e) => { if ((e.key === "Enter" || e.key === " ") && !busy) inputRef.current && inputRef.current.click(); }}
      >
        <input ref={inputRef} type="file" accept="image/*" capture="environment" style={{ display: "none" }} onChange={handleFile} />
        <span className="rule-icon"><Camera size={20} strokeWidth={1.8} /></span>
        <span className="rule-label">{busy ? "Saving photo\u2026" : "Progress photo"}</span>
        {hasPhoto && !busy && <button type="button" className="photo-remove-link" onClick={handleRemove}>Remove</button>}
        <span className={"rule-check" + (hasPhoto ? " rule-check-on" : "")}>
          {hasPhoto ? <Check size={15} strokeWidth={3} /> : null}
        </span>
      </div>
      {err && <div className="photo-err">{err}</div>}
    </>
  );
}

// ---------- main app ----------

function App() {
  const [state, setState] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [loadError, setLoadError] = useState("");
  const [tab, setTab] = useState("dashboard");
  const [selectedKey, setSelectedKey] = useState(todayKey());
  const [calMonth, setCalMonth] = useState(() => { const t = new Date(); return { y: t.getFullYear(), m: t.getMonth() }; });
  const [saveNote, setSaveNote] = useState("");
  const saveTimer = useRef(null);
  const firstLoad = useRef(true);

  useEffect(() => {
    (async () => {
      try {
        const s = await apiLoad();
        setState(s);
      } catch (e) {
        setLoadError("Couldn't reach the local server. Make sure `node server.js` is running, then reload this page.");
        setState({ config: { startDate: todayKey(), target: 75 }, days: {} });
      }
      setLoaded(true);
    })();
  }, []);

  useEffect(() => {
    if (!loaded || !state) return;
    if (firstLoad.current) { firstLoad.current = false; return; }
    setSaveNote("Saving\u2026");
    if (saveTimer.current) clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(async () => {
      try {
        await apiSave(state);
        setSaveNote("Saved");
        setTimeout(() => setSaveNote(""), 1200);
      } catch (e) {
        setSaveNote("Couldn't save \u2014 is the server still running?");
      }
    }, 500);
    return () => clearTimeout(saveTimer.current);
  }, [state, loaded]);

  const stats = useMemo(() => (state ? computeAll(state) : null), [state]);

  function updateDay(key, patch) {
    setState((s) => {
      const existing = s.days[key] || emptyDay();
      return { ...s, days: { ...s.days, [key]: { ...existing, ...patch } } };
    });
  }
  function addLcEntry(key) {
    setState((s) => {
      const existing = s.days[key] || emptyDay();
      return { ...s, days: { ...s.days, [key]: { ...existing, lc: [...existing.lc, emptyLcEntry()] } } };
    });
  }
  function updateLcEntry(key, idx, patch) {
    setState((s) => {
      const existing = s.days[key] || emptyDay();
      const lc = existing.lc.map((item, i) => (i === idx ? { ...item, ...patch } : item));
      return { ...s, days: { ...s.days, [key]: { ...existing, lc } } };
    });
  }
  function removeLcEntry(key, idx) {
    setState((s) => {
      const existing = s.days[key] || emptyDay();
      const lc = existing.lc.filter((_, i) => i !== idx);
      return { ...s, days: { ...s.days, [key]: { ...existing, lc } } };
    });
  }
  function updateConfig(patch) {
    setState((s) => ({ ...s, config: { ...s.config, ...patch } }));
  }

  if (!loaded || !state || !stats) {
    return (
      <div className="app-shell">
        <div className="loading">Opening the log book\u2026</div>
      </div>
    );
  }

  return (
    <div className="app-shell">
      <header className="masthead">
        <div className="masthead-top">
          <div>
            <div className="masthead-kicker">Discipline log \u00b7 local</div>
            <h1 className="masthead-title">75 hard, custom rules</h1>
          </div>
          <div className="save-note">{saveNote}</div>
        </div>
        <nav className="tabbar">
          {TABS.map((t) => {
            const Icon = t.icon;
            return (
              <button key={t.key} className={"tab-btn" + (tab === t.key ? " tab-btn-on" : "")} onClick={() => setTab(t.key)}>
                <Icon size={16} strokeWidth={1.8} />
                <span>{t.label}</span>
              </button>
            );
          })}
        </nav>
      </header>

      {loadError && (
        <div className="page" style={{ paddingTop: 16 }}>
          <div className="server-warning">{loadError}</div>
        </div>
      )}

      <main className="main">
        {tab === "dashboard" && (
          <Dashboard state={state} stats={stats} onJump={(k) => { setSelectedKey(k); setTab("today"); }} />
        )}
        {tab === "today" && (
          <DailyLog
            state={state}
            stats={stats}
            selectedKey={selectedKey}
            setSelectedKey={setSelectedKey}
            updateDay={updateDay}
            addLcEntry={addLcEntry}
            updateLcEntry={updateLcEntry}
            removeLcEntry={removeLcEntry}
          />
        )}
        {tab === "calendar" && (
          <CalendarTab
            state={state}
            stats={stats}
            calMonth={calMonth}
            setCalMonth={setCalMonth}
            onSelect={(k) => { setSelectedKey(k); setTab("today"); }}
          />
        )}
        {tab === "leetcode" && <LeetcodeTab state={state} stats={stats} onJump={(k) => { setSelectedKey(k); setTab("today"); }} />}
        {tab === "rules" && <RulesTab state={state} updateConfig={updateConfig} />}
      </main>
    </div>
  );
}

// ---------- Dashboard ----------

function Dashboard({ state, stats, onJump }) {
  const { counts, n } = ruleCounts(state, stats);
  const chainDays = 21;
  const chain = [];
  let d = addDays(parseKey(stats.todayKey), -(chainDays - 1));
  for (let i = 0; i < chainDays; i++) {
    const key = dkey(d);
    chain.push({ key, status: key < stats.startKey ? "none" : stats.statusMap[key] || "future" });
    d = addDays(d, 1);
  }
  const todayEntry = state.days[stats.todayKey] || emptyDay();
  const todayDone = RULES.filter((r) => ruleSatisfied(todayEntry, r)).length;

  return (
    <div className="page">
      <section className="hero">
        <div className="hero-main">
          <Flame size={28} strokeWidth={1.6} className="hero-flame" />
          <div className="hero-number">{stats.currentStreak}</div>
          <div className="hero-label">day streak</div>
        </div>
        <div className="hero-stats">
          <div className="hero-stat">
            <Trophy size={16} strokeWidth={1.8} />
            <span className="hero-stat-num">{stats.bestStreak}</span>
            <span className="hero-stat-label">best streak</span>
          </div>
          <div className="hero-stat">
            <Check size={16} strokeWidth={1.8} />
            <span className="hero-stat-num">{stats.totalSuccess}</span>
            <span className="hero-stat-label">successful days</span>
          </div>
          <div className="hero-stat">
            <Target size={16} strokeWidth={1.8} />
            <span className="hero-stat-num">{Math.min(100, Math.round((stats.currentStreak / stats.target) * 100))}%</span>
            <span className="hero-stat-label">toward {stats.target}</span>
          </div>
        </div>
      </section>

      <section className="panel">
        <div className="panel-title">Don't break the chain \u2014 last {chainDays} days</div>
        <div className="chain-row">
          {chain.map((c) => (
            <Stamp key={c.key} status={c.status} size={26} onClick={() => c.status !== "none" && c.status !== "future" && onJump(c.key)} label={fmtShort(parseKey(c.key))} />
          ))}
        </div>
      </section>

      <section className="panel">
        <div className="panel-title-row">
          <div className="panel-title">Today, {fmtShort(new Date())}</div>
          <button className="btn-ghost" onClick={() => onJump(stats.todayKey)}>Open today's entry</button>
        </div>
        <div className="today-progress-track">
          <div className="today-progress-fill" style={{ width: `${(todayDone / RULES.length) * 100}%` }} />
        </div>
        <div className="today-progress-text">{todayDone} of {RULES.length} rules done</div>
        <div className="rule-mini-grid">
          {RULES.map((r) => {
            const Icon = r.icon;
            const on = ruleSatisfied(todayEntry, r);
            const failed = r.type === "tristate" && todayEntry[r.key] === "failed";
            return (
              <div key={r.key} className={"rule-mini" + (on ? " rule-mini-on" : "") + (failed ? " rule-mini-fail" : "")}>
                <Icon size={16} strokeWidth={1.8} />
                <span>{r.label}</span>
              </div>
            );
          })}
        </div>
      </section>

      <section className="panel">
        <div className="panel-title">Rule consistency since day one</div>
        <div className="consistency-list">
          {RULES.map((r) => {
            const pct = Math.round((counts[r.key] / n) * 100);
            return (
              <div key={r.key} className="consistency-row">
                <span className="consistency-label">{r.label}</span>
                <div className="consistency-track"><div className="consistency-fill" style={{ width: `${pct}%` }} /></div>
                <span className="consistency-pct">{pct}%</span>
              </div>
            );
          })}
        </div>
      </section>

      <p className="footnote">A failed day resets the streak above to zero, but every successful day you've logged stays counted forever. The journey doesn't reset \u2014 only the count of consecutive days does.</p>
    </div>
  );
}

// ---------- Daily log ----------

function DailyLog({ state, stats, selectedKey, setSelectedKey, updateDay, addLcEntry, updateLcEntry, removeLcEntry }) {
  const entry = state.days[selectedKey] || emptyDay();
  const d = parseKey(selectedKey);
  const isFuture = selectedKey > stats.todayKey;
  const isToday = selectedKey === stats.todayKey;
  const status = stats.statusMap[selectedKey] || (selectedKey < stats.startKey ? "none" : "future");
  const dayNum = stats.dayNumMap[selectedKey];

  const statusText = status === "success" ? "Successful day" : status === "fail" ? "Failed day \u2014 streak reset" : status === "today" ? "In progress" : status === "future" ? "Not here yet" : "Before your start date";
  const statusClass = status === "success" ? "status-success" : status === "fail" ? "status-fail" : status === "today" ? "status-today" : "status-muted";

  return (
    <div className="page">
      <section className="panel">
        <div className="date-nav">
          <button className="icon-btn" onClick={() => setSelectedKey(dkey(addDays(d, -1)))}><ChevronLeft size={18} /></button>
          <div className="date-nav-mid">
            <div className="date-nav-day">{typeof dayNum === "number" ? `Day ${dayNum}` : "Outside challenge"}</div>
            <div className="date-nav-date">{fmtLong(d)}</div>
          </div>
          <button className="icon-btn" onClick={() => setSelectedKey(dkey(addDays(d, 1)))}><ChevronRight size={18} /></button>
        </div>
        <div className="date-nav-row2">
          <span className={"status-pill " + statusClass}>{statusText}</span>
          {!isToday && <button className="btn-ghost" onClick={() => setSelectedKey(stats.todayKey)}>Jump to today</button>}
        </div>
      </section>

      {isFuture ? (
        <section className="panel">
          <p className="footnote">You can log ahead, but rules only count toward your streak once their day arrives.</p>
        </section>
      ) : null}

      <section className="panel">
        <div className="panel-title">The six rules</div>
        <div className="rule-list">
          {RULES.map((r) => {
            if (r.type === "tristate") {
              return <DietControl key={r.key} value={entry.diet} onChange={(v) => updateDay(selectedKey, { diet: v })} />;
            }
            if (r.key === "photo") {
              return (
                <PhotoRuleRow
                  key={r.key}
                  dateKey={selectedKey}
                  hasPhoto={entry.photoStored}
                  onUploaded={() => updateDay(selectedKey, { photoStored: true, photo: true })}
                  onRemoved={() => updateDay(selectedKey, { photoStored: false, photo: false })}
                />
              );
            }
            return <RuleToggle key={r.key} rule={r} checked={!!entry[r.key]} onToggle={() => updateDay(selectedKey, { [r.key]: !entry[r.key] })} />;
          })}
        </div>
      </section>

      <section className="panel">
        <div className="panel-title">Tracked, never pass or fail</div>
        <div className="metric-grid">
          <label className="metric-field">
            <span className="metric-label"><Moon size={14} strokeWidth={1.8} /> Sleep (hours)</span>
            <input type="number" step="0.1" min="0" max="24" value={entry.sleep} onChange={(e) => updateDay(selectedKey, { sleep: e.target.value })} placeholder="\u2014" />
          </label>
          <label className="metric-field">
            <span className="metric-label"><ScaleIcon size={14} strokeWidth={1.8} /> Weight</span>
            <input type="number" step="0.1" value={entry.weight} onChange={(e) => updateDay(selectedKey, { weight: e.target.value })} placeholder="\u2014" />
          </label>
        </div>
        <label className="notes-field">
          <span className="metric-label">Notes</span>
          <textarea rows={3} value={entry.notes} onChange={(e) => updateDay(selectedKey, { notes: e.target.value })} placeholder="How did today go?" />
        </label>
      </section>

      <section className="panel">
        <div className="panel-title-row">
          <div className="panel-title">LeetCode for this day{entry.lc.length > 1 ? ` (${entry.lc.length})` : ""}</div>
          <button className="btn-ghost" onClick={() => addLcEntry(selectedKey)}><Plus size={13} strokeWidth={2} /> Add problem</button>
        </div>
        {entry.lc.length === 0 ? (
          <p className="footnote">No problems logged for this day yet.</p>
        ) : (
          <div className="lc-entries">
            {entry.lc.map((item, idx) => (
              <div key={idx} className="lc-entry">
                <div className="lc-entry-head">
                  <span className="lc-entry-num">Problem {idx + 1}</span>
                  <button className="icon-btn icon-btn-sm" onClick={() => removeLcEntry(selectedKey, idx)} title="Remove"><Trash2 size={14} strokeWidth={1.8} /></button>
                </div>
                <div className="lc-grid">
                  <label className="metric-field lc-wide">
                    <span className="metric-label">Problem</span>
                    <input type="text" value={item.problem} onChange={(e) => updateLcEntry(selectedKey, idx, { problem: e.target.value })} placeholder="e.g. Two Sum" />
                  </label>
                  <label className="metric-field">
                    <span className="metric-label">Difficulty</span>
                    <select value={item.difficulty} onChange={(e) => updateLcEntry(selectedKey, idx, { difficulty: e.target.value })}>
                      <option value="">\u2014</option>
                      <option>Easy</option><option>Medium</option><option>Hard</option>
                    </select>
                  </label>
                  <label className="metric-field">
                    <span className="metric-label">Topic</span>
                    <input type="text" value={item.topic} onChange={(e) => updateLcEntry(selectedKey, idx, { topic: e.target.value })} placeholder="Arrays" />
                  </label>
                  <label className="metric-field">
                    <span className="metric-label">Result</span>
                    <select value={item.result} onChange={(e) => updateLcEntry(selectedKey, idx, { result: e.target.value })}>
                      <option value="">\u2014</option>
                      <option>Solved</option><option>Attempted</option><option>Skipped</option>
                    </select>
                  </label>
                  <label className="metric-field">
                    <span className="metric-label">Solved independently?</span>
                    <select value={item.solved} onChange={(e) => updateLcEntry(selectedKey, idx, { solved: e.target.value })}>
                      <option value="">\u2014</option>
                      <option>Yes</option><option>No</option>
                    </select>
                  </label>
                  <label className="metric-field">
                    <span className="metric-label">Needed help?</span>
                    <select value={item.help} onChange={(e) => updateLcEntry(selectedKey, idx, { help: e.target.value })}>
                      <option value="">\u2014</option>
                      <option>Yes</option><option>No</option>
                    </select>
                  </label>
                </div>
                <label className="notes-field">
                  <span className="metric-label">Notes</span>
                  <textarea rows={2} value={item.notes} onChange={(e) => updateLcEntry(selectedKey, idx, { notes: e.target.value })} placeholder="Approach, gotchas, time complexity\u2026" />
                </label>
              </div>
            ))}
          </div>
        )}
      </section>
    </div>
  );
}

// ---------- Calendar ----------

function CalendarTab({ state, stats, calMonth, setCalMonth, onSelect }) {
  const { y, m } = calMonth;
  const first = new Date(y, m, 1);
  const startPad = (first.getDay() + 6) % 7;
  const daysInM = new Date(y, m + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < startPad; i++) cells.push(null);
  for (let day = 1; day <= daysInM; day++) cells.push(day);
  while (cells.length % 7 !== 0) cells.push(null);
  const summary = monthSummary(state, stats, y, m);

  return (
    <div className="page">
      <section className="panel">
        <div className="cal-nav">
          <button className="icon-btn" onClick={() => setCalMonth(m === 0 ? { y: y - 1, m: 11 } : { y, m: m - 1 })}><ChevronLeft size={18} /></button>
          <div className="cal-nav-title">{MONTH_NAMES[m]} {y}</div>
          <button className="icon-btn" onClick={() => setCalMonth(m === 11 ? { y: y + 1, m: 0 } : { y, m: m + 1 })}><ChevronRight size={18} /></button>
        </div>

        <div className="month-summary">
          <div className="month-summary-item">
            <span className="month-summary-num">{summary.success}</span>
            <span className="month-summary-label">successful</span>
          </div>
          <div className="month-summary-item">
            <span className="month-summary-num">{summary.fail}</span>
            <span className="month-summary-label">failed</span>
          </div>
          <div className="month-summary-item">
            <span className="month-summary-num">{summary.rate === null ? "\u2014" : summary.rate + "%"}</span>
            <span className="month-summary-label">success rate</span>
          </div>
        </div>

        <div className="cal-grid cal-weekday-row">
          {WEEKDAYS.map((w) => <div key={w} className="cal-weekday">{w}</div>)}
        </div>
        <div className="cal-grid">
          {cells.map((day, i) => {
            if (day === null) return <div key={i} className="cal-cell cal-cell-empty" />;
            const dateObj = new Date(y, m, day);
            const key = dkey(dateObj);
            const status = key < stats.startKey ? "none" : (stats.statusMap[key] || "future");
            return (
              <button key={i} className={"cal-cell cal-cell-" + status} onClick={() => onSelect(key)}>
                <span className="cal-daynum">{day}</span>
                {status === "success" && <Check size={13} strokeWidth={3} />}
                {status === "fail" && <XIcon size={13} strokeWidth={3} />}
              </button>
            );
          })}
        </div>
        <div className="legend">
          <span className="legend-item"><span className="legend-dot legend-success" />Successful</span>
          <span className="legend-item"><span className="legend-dot legend-fail" />Failed</span>
          <span className="legend-item"><span className="legend-dot legend-today" />Today</span>
          <span className="legend-item"><span className="legend-dot legend-future" />Ahead</span>
        </div>
      </section>
    </div>
  );
}

// ---------- LeetCode log ----------

function LeetcodeTab({ state, stats, onJump }) {
  const rows = [];
  Object.keys(state.days)
    .filter((k) => k <= stats.todayKey)
    .sort((a, b) => (a < b ? 1 : -1))
    .forEach((k) => {
      const lc = state.days[k].lc || [];
      lc.forEach((item, idx) => {
        if (item.problem) rows.push({ key: k, idx, item });
      });
    });

  return (
    <div className="page">
      <section className="panel">
        <div className="panel-title">Coding journey{rows.length ? ` \u2014 ${rows.length} problems` : ""}</div>
        {rows.length === 0 ? (
          <p className="footnote">No problems logged yet. Open a day's entry and fill in the LeetCode section \u2014 it'll show up here.</p>
        ) : (
          <div className="lc-table">
            {rows.map((row) => (
              <button key={row.key + "-" + row.idx} className="lc-row" onClick={() => onJump(row.key)}>
                <div className="lc-row-date">{fmtShort(parseKey(row.key))}</div>
                <div className="lc-row-main">
                  <div className="lc-row-problem">{row.item.problem}</div>
                  <div className="lc-row-meta">
                    {row.item.difficulty && <span className={"diff-pill diff-" + row.item.difficulty.toLowerCase()}>{row.item.difficulty}</span>}
                    {row.item.topic && <span className="lc-topic">{row.item.topic}</span>}
                  </div>
                </div>
                <div className="lc-row-result">{row.item.result || "\u2014"}</div>
              </button>
            ))}
          </div>
        )}
      </section>
    </div>
  );
}

// ---------- Rules ----------

function RulesTab({ state, updateConfig }) {
  return (
    <div className="page">
      <section className="panel">
        <div className="panel-title">The six mandatory rules</div>
        <ol className="rules-ol">
          {RULES.map((r) => {
            const Icon = r.icon;
            return (
              <li key={r.key}><Icon size={16} strokeWidth={1.8} /> <span>{r.label}</span></li>
            );
          })}
        </ol>
      </section>

      <section className="panel">
        <div className="panel-title">Tracked only, never pass or fail</div>
        <ol className="rules-ol">
          <li><Moon size={16} strokeWidth={1.8} /> <span>Sleep</span></li>
          <li><ScaleIcon size={16} strokeWidth={1.8} /> <span>Weight</span></li>
          <li><ClipboardList size={16} strokeWidth={1.8} /> <span>Notes</span></li>
        </ol>
      </section>

      <section className="panel">
        <div className="panel-title">The reset rule</div>
        <p className="rule-copy">If any of the six mandatory rules goes uncompleted on a day, that day is marked failed and the current streak resets to zero. Diet has its own cross button \u2014 mark it failed the moment you break it partway through the day, rather than waiting until the day closes out.</p>
        <p className="rule-copy">The overall journey never resets. Every successful day you've ever logged stays counted, even after a failed day.</p>
      </section>

      <section className="panel">
        <div className="panel-title">The target</div>
        <p className="rule-copy">{state.config.target} consecutive successful days. A failure adds more calendar days to the road ahead \u2014 it doesn't end the challenge.</p>
      </section>

      <section className="panel">
        <div className="panel-title">Where your data lives</div>
        <p className="rule-copy">Everything you log is saved to <code>data/challenge-data.json</code> in this project folder, and every progress photo is saved as its own file in <code>data/photos/</code>. Nothing leaves your machine.</p>
      </section>

      <section className="panel">
        <div className="panel-title">Settings</div>
        <div className="metric-grid">
          <label className="metric-field">
            <span className="metric-label">Start date</span>
            <input type="date" value={state.config.startDate} onChange={(e) => updateConfig({ startDate: e.target.value })} />
          </label>
          <label className="metric-field">
            <span className="metric-label">Target days</span>
            <input type="number" min="1" value={state.config.target} onChange={(e) => updateConfig({ target: Number(e.target.value) || 75 })} />
          </label>
        </div>
      </section>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
