// ---------- Main App ----------
const { useState: useS, useEffect: useE, useMemo: useM, useCallback: useC } = React;

const TWEAK_DEFAULS = /*EDITMODE-BEGIN*/{
  "weekStart": "mon",
  "compactCalendar": false,
  "showWeekend": true
}/*EDITMODE-END*/;

function App() {
  const [unlocked, setUnlocked] = useS(false);
  const [user, setUser] = useS(null);
  const [data, setData] = useS({ items: [] });
  const pref = useM(() => loadPref(), []);

  // Firebase auth state
  useE(() => {
    const start = () => {
      if (!window.fb) { window.addEventListener('fb-ready', start, { once: true }); return; }
      return window.fb.onAuthStateChanged(window.fb.auth, (u) => {
        setUser(u || null);
        setUnlocked(!!u);
        if (u) localStorage.setItem('mycal.hasSignedUp', '1');
      });
    };
    const cleanup = start();
    return () => { if (typeof cleanup === 'function') cleanup(); };
  }, []);

  // Firestore subscription: items live-sync per user
  useE(() => {
    if (!user || !window.fb) { setData({ items: [] }); return; }
    const ref = window.fb.collection(window.fb.db, 'users', user.uid, 'items');
    const unsub = window.fb.onSnapshot(ref, (snap) => {
      const items = [];
      snap.forEach((d) => items.push(d.data()));
      setData({ items });
    });
    return unsub;
  }, [user]);
  const [theme, setTheme] = useS(pref.theme || 'light');

  useE(() => {
    document.documentElement.dataset.theme = theme;
    savePref({ ...loadPref(), theme });
  }, [theme]);

  const [tab, setTab] = useS(pref.tab || 'todo');
  const [view, setView] = useS(pref.view || 'calendar');
  const [cursor, setCursor] = useS(() => new Date());
  const [modal, setModal] = useS({ open: false, mode: 'create', item: null });
  const [dayDetail, setDayDetail] = useS(null); // Date object
  const [showFilters, setShowFilters] = useS(false);
  const [showMenu, setShowMenu] = useS(false);
  const [showPicker, setShowPicker] = useS(false);
  const [filters, setFilters] = useS({
    colors: new Set(),
    important: false, urgent: false, status: 'all',
    tags: new Set(), q: '',
  });

  const tweaks = useTweaks(TWEAK_DEFAULS);

  // ---- persistence ----
  useE(() => { savePref({ tab, view, theme }); }, [tab, view, theme]);

  // ---- derived ----
  const tabItems = useM(() => (data.items || []).filter(i => i.tab === tab), [data, tab]);
  const allTags = useM(() => {
    const s = new Set();
    for (const it of (data.items || [])) (it.tags || []).forEach(t => s.add(t));
    return [...s].sort();
  }, [data]);
  const tabTags = useM(() => {
    const s = new Set();
    for (const it of tabItems) (it.tags || []).forEach(t => s.add(t));
    return [...s].sort();
  }, [tabItems]);

  const filtered = useM(() => tabItems.filter(it => itemMatchesFilters(it, filters)), [tabItems, filters]);

  // ---- actions ----
  const openCreate = (preset = {}) => {
    setModal({ open: true, mode: 'create', item: { tab, ...preset } });
  };
  const openEdit = (it) => setModal({ open: true, mode: 'edit', item: it });
  const closeModal = () => setModal((m) => ({ ...m, open: false }));

  const saveItem = (item) => {
    if (user && window.fb) {
      window.fb.setDoc(window.fb.doc(window.fb.db, 'users', user.uid, 'items', item.id), item);
    } else {
      setData((d) => {
        const items = [...(d.items || [])];
        const idx = items.findIndex((x) => x.id === item.id);
        if (idx >= 0) items[idx] = item; else items.push(item);
        return { ...d, items };
      });
    }
    closeModal();
  };
  const deleteItem = (id) => {
    if (!confirm('이 항목을 삭제할까요?')) return;
    if (user && window.fb) {
      window.fb.deleteDoc(window.fb.doc(window.fb.db, 'users', user.uid, 'items', id));
    } else {
      setData((d) => ({ ...d, items: (d.items || []).filter((x) => x.id !== id) }));
    }
    closeModal();
  };

  const reorderTodo = (dragId, overId, pos) => {
    setData((d) => {
      const items = [...(d.items || [])];
      const todos = items.filter((x) => x.tab === 'todo').sort((a, b) => (a.order ?? 9999) - (b.order ?? 9999));
      const di = todos.findIndex((x) => x.id === dragId);
      const oi = todos.findIndex((x) => x.id === overId);
      if (di < 0 || oi < 0) return d;
      const [moved] = todos.splice(di, 1);
      const insertAt = pos === 'before' ? oi - (di < oi ? 1 : 0) : oi + (di < oi ? 0 : 1);
      todos.splice(Math.max(0, insertAt), 0, moved);
      todos.forEach((t, i) => { t.order = i; });
      // re-merge into items
      const byId = new Map(todos.map((t) => [t.id, t]));
      for (let i = 0; i < items.length; i++) {
        if (byId.has(items[i].id)) items[i] = byId.get(items[i].id);
      }
      return { ...d, items };
    });
  };

  // ---- filter toggles ----
  const toggleColor = (id) => setFilters((f) => {
    const colors = new Set(f.colors);
    colors.has(id) ? colors.delete(id) : colors.add(id);
    return { ...f, colors };
  });
  const toggleTag = (t) => setFilters((f) => {
    const tags = new Set(f.tags);
    tags.has(t) ? tags.delete(t) : tags.add(t);
    return { ...f, tags };
  });
  const clearFilters = () => setFilters({ colors: new Set(), important: false, urgent: false, status: 'all', tags: new Set(), q: '' });
  const hasActiveFilter = filters.colors.size || filters.tags.size || filters.important || filters.urgent || (filters.status && filters.status !== 'all') || filters.q;

  // ---- import / export ----
  const onImport = async () => {
    const input = document.createElement('input');
    input.type = 'file'; input.accept = 'application/json';
    input.onchange = async () => {
      const file = input.files?.[0];
      if (!file) return;
      const text = await file.text();
      const mode = confirm('OK = 기존 데이터에 병합\n취소 = 전체 교체') ? 'merge' : 'replace';
      try {
        const next = importJSONText(text, { mode });
        setData(next);
        alert('가져왔어요. ' + (next.items?.length || 0) + '개 항목.');
      } catch (e) {
        alert('가져오기 실패: ' + e.message);
      }
    };
    input.click();
  };

  // ---- lock screen ----
  if (!unlocked) return <PinLock onUnlock={() => setUnlocked(true)} />;

  const tabColors = (() => {
    const s = new Set();
    for (const it of tabItems) if (it.color) s.add(it.color);
    return COLORS.filter((c) => s.has(c.id));
  })();

  const monthLabel = `${cursor.getFullYear()}년 ${cursor.getMonth() + 1}월`;
  const curTab = TAB_BY_ID[tab] || TABS[0];
  const tabAccent = curTab.accent;

  return (
    <div className="app" data-tab={tab} style={{ '--tab-accent': tabAccent }}>
      {/* ===== Top bar ===== */}
      <header className="topbar">
        <div className="brand">
          <div className="brand-mark"><Icon.Calendar size={16} /></div>
          <div className="brand-name">Dada Calendar</div>
        </div>

        <nav className="tabs" role="tablist">
          {TABS.map((t) => (
            <button
              key={t.id}
              role="tab"
              aria-selected={tab === t.id}
              className={'tab' + (tab === t.id ? ' on' : '')}
              style={{ '--ac': t.accent }}
              onClick={() => { setTab(t.id); clearFilters(); }}
            >
              <span className="tab-dot" />
              {t.label}
            </button>
          ))}
        </nav>

        <div className="top-right">
          <button className="ico-btn" onClick={() => setShowMenu(true)} aria-label="설정">
            <Icon.Settings size={16} />
          </button>
        </div>
      </header>

      {/* ===== Toolbar ===== */}
      <div className="toolbar">
        <div className="tool-l">
          {view === 'calendar' ? (
            <div className="month-nav">
              <button className="ico-btn sm" onClick={() => setCursor((d) => new Date(d.getFullYear(), d.getMonth() - 1, 1))} aria-label="이전 달"><Icon.Chevron size={16} dir="left" /></button>
              <button className="month-title month-title-btn" onClick={() => setShowPicker(true)} aria-label="년/월 선택">
                {monthLabel}
                <Icon.Chevron size={12} dir="down" />
              </button>
              <button className="ico-btn sm" onClick={() => setCursor((d) => new Date(d.getFullYear(), d.getMonth() + 1, 1))} aria-label="다음 달"><Icon.Chevron size={16} /></button>
              <button className="today-btn" onClick={() => setCursor(new Date())}>오늘</button>
            </div>
          ) : (
            <h2 className="month-title">{TAB_BY_ID[tab]?.korTitle}</h2>
          )}
        </div>

        <div className="tool-r">
          <div className="seg">
            <button className={'seg-btn' + (view === 'calendar' ? ' on' : '')} onClick={() => setView('calendar')}>
              <Icon.Calendar size={14} /><span className="lbl">캘린더</span>
            </button>
            <button className={'seg-btn' + (view === 'list' ? ' on' : '')} onClick={() => setView('list')}>
              <Icon.List size={14} /><span className="lbl">리스트</span>
            </button>
          </div>
          <button
            className={'ico-btn' + (showFilters || hasActiveFilter ? ' on' : '')}
            onClick={() => setShowFilters((x) => !x)}
            aria-label="필터"
          >
            <Icon.Filter size={15} />
            {hasActiveFilter ? <span className="dot" /> : null}
          </button>
          <button className="add-btn" onClick={() => openCreate()}>
            <Icon.Plus size={16} /> <span className="lbl">추가</span>
          </button>
        </div>
      </div>

      {/* ===== Filters panel ===== */}
      {showFilters && (
        <section className="filt">
          <div className="filt-row">
            <div className="filt-lbl">검색</div>
            <div className="filt-search">
              <Icon.Search size={13} />
              <input
                placeholder="제목/메모/장소/태그…"
                value={filters.q}
                onChange={(e) => setFilters((f) => ({ ...f, q: e.target.value }))}
              />
            </div>
          </div>

          {tabColors.length > 0 && (
            <div className="filt-row">
              <div className="filt-lbl">색상</div>
              <div className="filt-chips">
                {tabColors.map((c) => (
                  <button
                    key={c.id}
                    className={'chip color-chip' + (filters.colors.has(c.id) ? ' on' : '')}
                    onClick={() => toggleColor(c.id)}
                    style={{ '--c': c.hex }}
                  >
                    <span className="chip-dot" />
                    {c.label}
                  </button>
                ))}
              </div>
            </div>
          )}

          {tab === 'todo' && (
            <>
              <div className="filt-row">
                <div className="filt-lbl">속성</div>
                <div className="filt-chips">
                  <button className={'chip' + (filters.important ? ' on' : '')} onClick={() => setFilters((f) => ({ ...f, important: !f.important }))}>
                    <Icon.Star size={12} filled={filters.important} fillColor="#f59e0b" stroke="#f59e0b" /> 중요
                  </button>
                  <button className={'chip' + (filters.urgent ? ' on' : '')} onClick={() => setFilters((f) => ({ ...f, urgent: !f.urgent }))}>
                    <Icon.Flame size={12} filled={filters.urgent} fillColor="#ef4444" stroke="#ef4444" /> 긴급
                  </button>
                </div>
              </div>
              <div className="filt-row">
                <div className="filt-lbl">상태</div>
                <div className="filt-chips">
                  {[{ id: 'all', label: '전체' }, ...STATUSES].map((s) => (
                    <button key={s.id} className={'chip' + (filters.status === s.id ? ' on' : '')} onClick={() => setFilters((f) => ({ ...f, status: s.id }))}>
                      {s.dot && <span className="chip-dot" style={{ background: s.dot }} />}
                      {s.label}
                    </button>
                  ))}
                </div>
              </div>
            </>
          )}

          {tabTags.length > 0 && (
            <div className="filt-row">
              <div className="filt-lbl">태그</div>
              <div className="filt-chips">
                {tabTags.map((t) => (
                  <button key={t} className={'chip' + (filters.tags.has(t) ? ' on' : '')} onClick={() => toggleTag(t)}>
                    #{t}
                  </button>
                ))}
              </div>
            </div>
          )}

          {hasActiveFilter && (
            <div className="filt-row filt-foot">
              <span className="filt-lbl"></span>
              <button className="filt-clear" onClick={clearFilters}>필터 초기화</button>
              <span className="filt-count">{filtered.length}개 표시</span>
            </div>
          )}
        </section>
      )}

      {/* ===== Main ===== */}
      <main className="main">
        {view === 'calendar' ? (
          <MonthCalendar
            cursor={cursor}
            setCursor={setCursor}
            items={filtered}
            tab={tab}
            tabAccent={tabAccent}
            onItemClick={openEdit}
            onDayDetail={(d) => setDayDetail(d)}
            onDayCreate={(d) => {
              const iso = D.toISO(d);
              openCreate(tab === 'todo' ? { startISO: iso, endISO: iso } : { dateISO: iso });
            }}
          />
        ) : (
          <ListView
            tab={tab}
            items={filtered}
            onItemClick={openEdit}
            onReorder={reorderTodo}
          />
        )}
      </main>

      {/* Floating add for mobile */}
      <button className="fab" onClick={() => openCreate()} aria-label="추가">
        <Icon.Plus size={22} />
      </button>

      {/* Year/Month picker */}
      {showPicker && (
        <MonthYearPicker
          cursor={cursor}
          onPick={(y, m) => { setCursor(new Date(y, m, 1)); setShowPicker(false); }}
          onClose={() => setShowPicker(false)}
        />
      )}

      {/* Day detail sheet */}
      {dayDetail && (
        <DayDetail
          date={dayDetail}
          items={filtered.filter((it) => {
            const days = it.tab === 'todo' ? D.spanDays(it.startISO, it.endISO) : [(it.dateISO || '').slice(0, 10)];
            return days.includes(D.toISO(dayDetail));
          })}
          onClose={() => setDayDetail(null)}
          onItemClick={(it) => { setDayDetail(null); openEdit(it); }}
          onAdd={() => {
            const iso = D.toISO(dayDetail);
            setDayDetail(null);
            openCreate(tab === 'todo' ? { startISO: iso, endISO: iso } : { dateISO: iso });
          }}
        />
      )}

      {/* Item modal */}
      <ItemModal
        open={modal.open}
        mode={modal.mode}
        tab={tab}
        initial={modal.item}
        allTags={allTags}
        onSave={saveItem}
        onDelete={deleteItem}
        onClose={closeModal}
      />

      {/* Settings menu */}
      {showMenu && (
        <SettingsMenu
          onClose={() => setShowMenu(false)}
          onExport={() => { exportJSON(); setShowMenu(false); }}
          onImport={() => { onImport(); setShowMenu(false); }}
          onLock={() => {
            if (window.fb) window.fb.signOut(window.fb.auth);
            setUnlocked(false); setShowMenu(false);
          }}
          theme={theme}
          setTheme={setTheme}
          itemCount={data.items?.length || 0}
        />
      )}

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="캘린더">
          <TweakRadio
            label="주 시작"
            value={tweaks.weekStart}
            onChange={(v) => tweaks.setTweak('weekStart', v)}
            options={[{ value: 'mon', label: '월요일' }, { value: 'sun', label: '일요일' }]}
          />
          <TweakToggle
            label="컴팩트 모드"
            value={tweaks.compactCalendar}
            onChange={(v) => tweaks.setTweak('compactCalendar', v)}
          />
        </TweakSection>
        <TweakSection label="데이터">
          <TweakButton label="JSON 백업 내려받기" onClick={exportJSON} />
          <TweakButton label="JSON 가져오기" onClick={onImport} secondary />
        </TweakSection>
      </TweaksPanel>

      <AppStyles tweaks={tweaks} />
    </div>
  );
}

// ---------- Year/Month quick picker (popup with scroll-wheel year) ----------
function MonthYearPicker({ cursor, onPick, onClose }) {
  const [year, setYear] = useS(cursor.getFullYear());
  const [month, setMonth] = useS(cursor.getMonth());
  const touchRef = React.useRef({ startY: 0, lastYear: 0 });

  const months = ['1월','2월','3월','4월','5월','6월','7월','8월','9월','10월','11월','12월'];

  const handleWheel = (e) => {
    e.preventDefault();
    const delta = e.deltaY > 0 ? 1 : -1;
    setYear((y) => y + delta);
  };

  const handleTouchStart = (e) => {
    touchRef.current = { startY: e.touches[0].clientY, lastYear: year };
  };
  const handleTouchMove = (e) => {
    e.preventDefault();
    const dy = touchRef.current.startY - e.touches[0].clientY;
    const delta = Math.round(dy / 40);
    const newYear = touchRef.current.lastYear + delta;
    if (newYear !== year) setYear(newYear);
  };

  return (
    <div className="mod-back" onClick={onClose}>
      <div className="myp" onClick={(e) => e.stopPropagation()}>
        <header className="myp-head">
          <div>
            <div className="day-eyebrow">빠른 이동</div>
            <h2 className="day-title">{year}년 {month+1}월</h2>
          </div>
          <button className="mod-x" onClick={onClose}><Icon.X size={18} /></button>
        </header>

        <div className="myp-sec-l">연도 (스크롤 또는 스와이프)</div>
        <div className="myp-year-ctrl" onWheel={handleWheel} onTouchStart={handleTouchStart} onTouchMove={handleTouchMove}>
          <button className="myp-yr-arr" onClick={() => setYear((y) => y - 1)} aria-label="이전 연도"><Icon.Chevron size={16} dir="left" /></button>
          <span className="myp-yr-num">{year}</span>
          <button className="myp-yr-arr" onClick={() => setYear((y) => y + 1)} aria-label="다음 연도"><Icon.Chevron size={16} /></button>
        </div>

        <div className="myp-sec-l">월</div>
        <div className="myp-months">
          {months.map((m, i) => (
            <button key={i} className={'mo' + (i === month ? ' on' : '')} onClick={() => setMonth(i)}>{m}</button>
          ))}
        </div>

        <footer className="myp-foot">
          <button className="mod-btn" onClick={() => { const t = new Date(); setYear(t.getFullYear()); setMonth(t.getMonth()); }}>오늘</button>
          <div style={{ flex: 1 }} />
          <button className="mod-btn" onClick={onClose}>취소</button>
          <button className="mod-btn primary" onClick={() => onPick(year, month)}>이동</button>
        </footer>

        <style>{`
          .myp{width:100%;max-width:400px;background:var(--bg);border-radius:16px;box-shadow:var(--shadow-3);display:flex;flex-direction:column;padding:18px;gap:14px;animation:modslide .25s cubic-bezier(.2,.8,.2,1) both;user-select:none}
          @media (max-width:720px){ .myp{border-radius:16px 16px 0 0;max-width:100%} }
          .myp-head{display:flex;align-items:flex-start;justify-content:space-between}
          .myp-sec-l{font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--ink-3);text-transform:uppercase;margin:2px 0}
          .myp-year-ctrl{display:flex;align-items:center;justify-content:center;gap:20px;padding:10px 0;touch-action:none}
          .myp-yr-arr{appearance:none;border:1px solid var(--line);background:var(--bg);width:36px;height:36px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--ink-2);transition:all .12s;font-family:inherit}
          .myp-yr-arr:hover{background:var(--accent-soft);color:var(--ink);border-color:var(--ink-4)}
          .myp-yr-num{font-size:28px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink);min-width:100px;text-align:center}
          .myp-months{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
          .mo{appearance:none;border:1px solid var(--line);background:var(--bg);padding:11px 0;border-radius:9px;font-size:13px;font-weight:500;color:var(--ink-2);cursor:pointer;font-family:inherit;transition:all .12s}
          .mo:hover{border-color:var(--ink-4);color:var(--ink)}
          .mo.on{background:var(--tab-accent,var(--ink));color:#fff;border-color:var(--tab-accent,var(--ink));font-weight:700}
          .myp-foot{display:flex;align-items:center;gap:8px;margin-top:4px}
        `}</style>
      </div>
    </div>
  );
}

// ---------- Day Detail sheet ----------
function DayDetail({ date, items, onClose, onItemClick, onAdd }) {
  const dow = ['일','월','화','수','목','금','토'][date.getDay()];
  const title = `${date.getFullYear()}. ${D.pad(date.getMonth()+1)}. ${D.pad(date.getDate())} (${dow})`;
  return (
    <div className="mod-back" onClick={onClose}>
      <div className="day-sheet" onClick={(e) => e.stopPropagation()}>
        <header className="day-head">
          <div>
            <div className="day-eyebrow">{D.sameDay(date, new Date()) ? '오늘' : '선택일'}</div>
            <h2 className="day-title">{title}</h2>
          </div>
          <button className="mod-x" onClick={onClose} aria-label="닫기"><Icon.X size={18} /></button>
        </header>
        <div className="day-body">
          {items.length === 0 ? (
            <div className="day-empty">이 날에는 아직 항목이 없어요.</div>
          ) : (
            <ul className="day-list">
              {items.map((it) => {
                const c = COLOR_BY_ID[it.color] || COLOR_BY_ID.blue;
                const done = it.tab === 'todo' && it.status === 'done';
                return (
                  <li key={it.id} className="day-row" onClick={() => onItemClick(it)}>
                    <span className="day-bar" style={{ background: c.hex }} />
                    <div className="day-row-main">
                      <div className={'day-row-title' + (done ? ' done' : '')}>{it.title || '(제목 없음)'}</div>
                      <div className="day-row-meta">
                        {it.tab === 'todo' && it.startISO?.length > 10 && (
                          <span>{D.fmtTime(it.startISO)}{it.endISO?.length > 10 ? '–' + D.fmtTime(it.endISO) : ''}</span>
                        )}
                        {it.location && <span>@ {it.location}</span>}
                        {(it.tags || []).slice(0, 3).map(t => <span key={t} className="day-row-tag">#{t}</span>)}
                      </div>
                    </div>
                    <div className="day-row-icons">
                      {it.tab === 'todo' && it.urgent && <Icon.Flame size={13} filled fillColor="#ef4444" stroke="#ef4444" />}
                      {it.tab === 'todo' && it.important && <Icon.Star size={13} filled fillColor="#f59e0b" stroke="#f59e0b" />}
                    </div>
                  </li>
                );
              })}
            </ul>
          )}
        </div>
        <footer className="day-foot">
          <button className="mod-btn primary" onClick={onAdd}><Icon.Plus size={14} /> 이 날짜에 추가</button>
        </footer>
        <style>{`
          .day-sheet{width:100%;max-width:520px;max-height:80vh;background:var(--bg);border-radius:16px 16px 0 0;display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--shadow-3);animation:modslide .25s cubic-bezier(.2,.8,.2,1) both}
          @media (min-width:720px){ .day-sheet{border-radius:16px} }
          .day-head{display:flex;align-items:flex-start;justify-content:space-between;padding:18px 18px 12px}
          .day-eyebrow{font-size:11px;font-weight:600;letter-spacing:.08em;color:var(--ink-3);text-transform:uppercase;margin-bottom:4px}
          .day-title{margin:0;font-size:20px;font-weight:700;letter-spacing:-.01em}
          .day-body{padding:0 18px;overflow-y:auto;flex:1}
          .day-empty{padding:48px 12px;text-align:center;color:var(--ink-3);font-size:13px}
          .day-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
          .day-row{display:flex;gap:10px;padding:12px;border:1px solid var(--line);border-radius:10px;cursor:pointer;transition:all .12s;align-items:center}
          .day-row:hover{border-color:var(--ink-4);background:var(--bg-soft)}
          .day-bar{width:3px;align-self:stretch;border-radius:3px}
          .day-row-main{flex:1;min-width:0}
          .day-row-title{font-size:14px;font-weight:500;color:var(--ink);margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
          .day-row-title.done{text-decoration:line-through;color:var(--ink-3)}
          .day-row-meta{display:flex;flex-wrap:wrap;gap:6px;font-size:11.5px;color:var(--ink-3)}
          .day-row-tab{font-weight:600;color:var(--ink-2);background:var(--accent-soft);padding:1px 6px;border-radius:4px}
          .day-row-tag{color:var(--ink-2)}
          .day-row-icons{display:flex;align-items:center;gap:4px;flex-shrink:0}
          .day-foot{padding:14px 18px;border-top:1px solid var(--line);background:var(--bg-soft);display:flex;justify-content:flex-end}        `}</style>
      </div>
    </div>
  );
}

// ---------- Settings menu ----------
function SettingsMenu({ onClose, onExport, onImport, onLock, theme, setTheme, itemCount }) {
  return (
    <div className="mod-back" onClick={onClose}>
      <div className="set-sheet" onClick={(e) => e.stopPropagation()}>
        <header className="set-head">
          <h2 className="day-title">설정</h2>
          <button className="mod-x" onClick={onClose}><Icon.X size={18} /></button>
        </header>
        <div className="set-body">
          <div className="set-stat">
            <div className="set-stat-n">{itemCount.toLocaleString()}</div>
            <div className="set-stat-l">전체 항목</div>
          </div>

          <div className="set-theme">
            <div className="set-theme-l">테마</div>
            <div className="set-theme-seg">
              <button className={'set-theme-btn' + (theme === 'light' ? ' on' : '')} onClick={() => setTheme('light')}>화이트</button>
              <button className={'set-theme-btn' + (theme === 'dark' ? ' on' : '')} onClick={() => setTheme('dark')}>블랙</button>
            </div>
          </div>
          <button className="set-row" onClick={onExport}>
            <span className="set-row-ico"><Icon.Download size={16} /></span>
            <div className="set-row-main">
              <div className="set-row-t">JSON으로 백업 내려받기</div>
              <div className="set-row-s">전체 데이터를 한 파일로 저장해요</div>
            </div>
          </button>
          <button className="set-row" onClick={onImport}>
            <span className="set-row-ico"><Icon.Upload size={16} /></span>
            <div className="set-row-main">
              <div className="set-row-t">백업 파일 가져오기</div>
              <div className="set-row-s">기존 데이터와 병합하거나 교체해요</div>
            </div>
          </button>
          <button className="set-row" onClick={onLock}>
            <span className="set-row-ico"><Icon.Lock size={16} /></span>
            <div className="set-row-main">
              <div className="set-row-t">로그아웃</div>
              <div className="set-row-s">자동 로그인을 해제하고 처음 화면으로 돌아가요</div>
            </div>
          </button>
        </div>
        <style>{`
          .set-sheet{width:100%;max-width:480px;max-height:80vh;background:var(--bg);border-radius:16px 16px 0 0;display:flex;flex-direction:column;overflow:hidden;box-shadow:var(--shadow-3);animation:modslide .25s cubic-bezier(.2,.8,.2,1) both}
          @media (min-width:720px){ .set-sheet{border-radius:16px} }
          .set-head{display:flex;align-items:center;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--line)}
          .set-body{padding:14px 18px 18px;overflow-y:auto;display:flex;flex-direction:column;gap:6px}
          .set-stat{display:flex;align-items:baseline;gap:8px;padding:14px 16px;background:var(--bg-soft);border:1px solid var(--line);border-radius:10px;margin-bottom:8px}
          .set-stat-n{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums}
          .set-stat-l{font-size:12px;color:var(--ink-3)}
          .set-theme{display:flex;align-items:center;gap:12px;padding:12px 14px;margin-bottom:4px}
          .set-theme-l{font-size:13px;font-weight:500;color:var(--ink);flex:1}
          .set-theme-seg{display:inline-flex;background:var(--bg-soft);border:1px solid var(--line);border-radius:9px;padding:3px}
          .set-theme-btn{appearance:none;border:none;background:transparent;padding:6px 14px;font-size:12.5px;font-weight:500;color:var(--ink-3);cursor:pointer;border-radius:6px;font-family:inherit;transition:all .12s}
          .set-theme-btn:hover{color:var(--ink)}
          .set-theme-btn.on{background:var(--ink);color:var(--bg);font-weight:600}
          .set-row{display:flex;gap:12px;align-items:center;padding:12px 14px;background:transparent;border:1px solid transparent;border-radius:10px;cursor:pointer;text-align:left;font-family:inherit;width:100%;transition:all .12s}
          .set-row:hover{background:var(--bg-soft);border-color:var(--line)}
          .set-row-ico{width:36px;height:36px;border-radius:8px;background:var(--accent-soft);display:inline-flex;align-items:center;justify-content:center;color:var(--ink-2);flex-shrink:0}
          .set-row-main{flex:1;min-width:0}
          .set-row-t{font-size:14px;font-weight:500;color:var(--ink)}
          .set-row-s{font-size:12px;color:var(--ink-3);margin-top:2px}
        `}</style>
      </div>
    </div>
  );
}

// ---------- App-level styles ----------
function AppStyles({ tweaks }) {
  return (
    <style>{`
      .app{min-height:100dvh;display:flex;flex-direction:column;background:var(--bg-soft);padding-bottom:env(safe-area-inset-bottom)}

      /* TOP BAR */
      .topbar{
        position:sticky;top:0;z-index:50;
        display:flex;align-items:center;gap:14px;padding:10px 18px;
        background:color-mix(in oklab, var(--bg) 85%, transparent);backdrop-filter:saturate(180%) blur(10px);
        border-bottom:1px solid var(--line);
      }
      .brand{display:flex;align-items:center;gap:8px}
      .brand-mark{width:26px;height:26px;border-radius:7px;background:var(--ink);color:var(--bg);display:flex;align-items:center;justify-content:center}
      .brand-name{font-size:14px;font-weight:700;letter-spacing:-.01em}

      .tabs{display:flex;align-items:center;gap:2px;flex:1;justify-content:center;background:var(--bg-soft);border-radius:10px;padding:3px;border:1px solid var(--line);max-width:440px;margin:0 auto}
      .tab{flex:1;appearance:none;border:none;background:transparent;padding:7px 12px;font-size:13px;font-weight:500;color:var(--ink-3);cursor:pointer;border-radius:7px;font-family:inherit;transition:all .12s;display:inline-flex;align-items:center;justify-content:center;gap:6px}
      .tab:hover{color:var(--ink)}
      .tab-dot{width:7px;height:7px;border-radius:50%;background:var(--ac);opacity:0;transform:scale(0);transition:opacity .15s,transform .15s}
      .tab.on .tab-dot{opacity:1;transform:scale(1.15)}
      .tab.on{background:var(--bg);color:var(--ac);box-shadow:0 1px 2px rgba(0,0,0,.06),inset 0 0 0 1px color-mix(in oklab,var(--ac) 22%, transparent);font-weight:700}

      .top-right{display:flex;gap:6px;align-items:center}

      .ico-btn{position:relative;appearance:none;border:1px solid transparent;background:transparent;width:34px;height:34px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;color:var(--ink-2);transition:all .12s;font-family:inherit}
      .ico-btn:hover{background:var(--accent-soft);color:var(--ink)}
      .ico-btn.sm{width:30px;height:30px}
      .ico-btn.on{background:var(--ink);color:#fff;border-color:var(--ink)}
      .ico-btn .dot{position:absolute;top:7px;right:7px;width:6px;height:6px;border-radius:50%;background:#ef4444}

      /* TOOLBAR */
      .toolbar{display:flex;align-items:center;gap:10px;padding:14px 18px 10px;flex-wrap:wrap}
      .tool-l{display:flex;align-items:center;gap:8px;flex:1;min-width:0;overflow:hidden}
      .tool-r{display:flex;align-items:center;gap:6px}
      .month-nav{display:flex;align-items:center;gap:6px;flex-shrink:0;white-space:nowrap}
      .month-title{margin:0;font-size:18px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
      .month-title-btn{appearance:none;background:transparent;border:none;padding:4px 6px;border-radius:8px;cursor:pointer;color:var(--ink);font-family:inherit;display:inline-flex;align-items:center;gap:4px;transition:background .12s;white-space:nowrap}
      .month-title-btn:hover{background:var(--accent-soft)}
      .month-title-btn svg{color:var(--ink-3)}
      .today-btn{appearance:none;border:1px solid var(--line);background:var(--bg);padding:5px 10px;border-radius:8px;font-size:11px;font-weight:500;cursor:pointer;color:var(--ink-2);font-family:inherit;margin-left:2px;transition:all .12s;flex-shrink:0}
      .today-btn:hover{border-color:var(--ink);color:var(--ink)}

      .seg{display:inline-flex;background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:3px}
      .seg-btn{appearance:none;border:none;background:transparent;padding:6px 12px;font-size:12.5px;font-weight:500;color:var(--ink-3);cursor:pointer;border-radius:6px;display:inline-flex;align-items:center;gap:5px;font-family:inherit;transition:all .12s}
      .seg-btn:hover{color:var(--ink)}
      .seg-btn.on{background:var(--ink);color:var(--bg)}

      .add-btn{appearance:none;background:var(--ink);color:var(--bg);border:none;padding:8px 14px;border-radius:8px;font-size:13px;font-weight:600;display:inline-flex;align-items:center;gap:5px;cursor:pointer;font-family:inherit;transition:background .12s}
      .add-btn:hover{opacity:.85}

      @media (max-width:720px){
        .topbar{padding:8px 12px;gap:8px}
        .brand-name{display:none}
        .toolbar{padding:10px 12px}
        .month-title{font-size:15px}
        .seg-btn .lbl, .add-btn .lbl{display:none}
        .seg-btn{padding:6px 8px}
        .add-btn{padding:8px 10px}
      }

      /* FILTER PANEL */
      .filt{margin:0 18px 8px;padding:12px 14px;background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);display:flex;flex-direction:column;gap:8px;animation:fadein .15s ease}
      @keyframes fadein{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
      .filt-row{display:flex;align-items:flex-start;gap:10px;min-height:28px}
      .filt-foot{align-items:center;justify-content:flex-start}
      .filt-lbl{flex:0 0 52px;font-size:11px;font-weight:600;color:var(--ink-3);letter-spacing:.02em;padding-top:5px}
      .filt-chips{display:flex;gap:5px;flex-wrap:wrap;flex:1}
      .filt-search{display:flex;align-items:center;gap:6px;flex:1;padding:6px 10px;background:var(--bg-soft);border:1px solid var(--line);border-radius:8px;color:var(--ink-3)}
      .filt-search input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--ink);font-family:inherit}
      .chip{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;border-radius:999px;border:1px solid var(--line);background:var(--bg);font-size:12px;color:var(--ink-2);cursor:pointer;font-family:inherit;transition:all .12s}
      .chip:hover{border-color:var(--ink-4)}
      .chip.on{background:var(--ink);color:var(--bg);border-color:var(--ink)}
      .chip-dot{width:7px;height:7px;border-radius:50%;background:var(--c, var(--ink-4))}
      .color-chip.on{background:var(--c);border-color:var(--c);color:var(--bg)}
      .color-chip.on .chip-dot{background:var(--bg)}
      .filt-clear{appearance:none;border:none;background:transparent;font-size:12px;color:var(--ink-3);cursor:pointer;font-family:inherit;text-decoration:underline}
      .filt-clear:hover{color:var(--ink)}
      .filt-count{font-size:12px;color:var(--ink-3);margin-left:auto}

      @media (max-width:720px){
        .filt{margin:0 12px 8px;padding:10px}
        .filt-lbl{flex-basis:46px}
      }

      /* MAIN */
      .main{flex:1;padding:0 18px 100px}
      @media (max-width:720px){ .main{padding:0 12px 100px} }

      /* FAB */
      .fab{
        position:fixed;right:22px;bottom:calc(22px + env(safe-area-inset-bottom));
        width:54px;height:54px;border-radius:50%;
        background:var(--ink);color:var(--bg);border:none;cursor:pointer;
        display:none;align-items:center;justify-content:center;
        box-shadow:var(--shadow-2);transition:transform .15s;z-index:30;
      }
      .fab:hover{transform:scale(1.05)}
      .fab:active{transform:scale(.95)}
      @media (max-width:720px){ .fab{display:flex} }
    `}</style>
  );
}

// ---------- Mount ----------
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App />);
